From 79e01b7efc6682a4362b1fe0d9926ce9d80f33ac Mon Sep 17 00:00:00 2001 From: Brian Sam-Bodden Date: Wed, 26 Nov 2025 08:41:09 -0700 Subject: [PATCH] fix(autocomplete): use Tuple.getElement() for payload lookup in autocomplete (#673) When both withScore() and withPayload() options are enabled, the code incorrectly passed the Tuple object directly to the hash lookup instead of extracting the string element first. This caused a ClassCastException: "class redis.clients.jedis.resps.Tuple cannot be cast to class java.lang.String" Closes #673 --- .../ops/search/SearchOperationsImpl.java | 2 +- .../autocompletable/AutoCompleteTest.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/search/SearchOperationsImpl.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/search/SearchOperationsImpl.java index ba9d706a5..19fdc18a3 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/search/SearchOperationsImpl.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/search/SearchOperationsImpl.java @@ -135,7 +135,7 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt String[] keyParts = key.split(":"); String payLoadKey = String.format("sugg:payload:%s:%s", keyParts[keyParts.length - 2], keyParts[keyParts.length - 1]); - Object payload = template.opsForHash().get(payLoadKey, suggestion); + Object payload = template.opsForHash().get(payLoadKey, suggestion.getElement()); String json = payload != null ? payload.toString() : "{}"; Map payloadMap = gson.fromJson(json, new TypeToken>() { }.getType()); diff --git a/tests/src/test/java/com/redis/om/spring/annotations/autocompletable/AutoCompleteTest.java b/tests/src/test/java/com/redis/om/spring/annotations/autocompletable/AutoCompleteTest.java index ea829563f..55e3df639 100644 --- a/tests/src/test/java/com/redis/om/spring/annotations/autocompletable/AutoCompleteTest.java +++ b/tests/src/test/java/com/redis/om/spring/annotations/autocompletable/AutoCompleteTest.java @@ -98,4 +98,33 @@ void testGetAutocompleteSuggestionsWithScores() { 0.27)); } + /** + * Test for Issue #673: ClassCastException when using withScore() and withPayload() together. + * + * When both options are enabled, the code incorrectly passes the Tuple object + * (instead of its string element) to the hash lookup for payload retrieval. + * + * @see Issue #673 + */ + @Test + void testGetAutocompleteSuggestionsWithScoresAndPayload() { + Map columbusPayload = Map.of("code", "CMH", "state", "OH"); + Map columbiaPayload = Map.of("code", "CAE", "state", "SC"); + Map coloradoSpringsPayload = Map.of("code", "COS", "state", "CO"); + + // This combination caused ClassCastException before the fix: + // "class redis.clients.jedis.resps.Tuple cannot be cast to class java.lang.String" + List suggestions = repository.autoCompleteName("col", + AutoCompleteOptions.get().withScore().withPayload()); + + List suggestionsString = suggestions.stream().map(Suggestion::getValue).collect(Collectors.toList()); + List scores = suggestions.stream().map(Suggestion::getScore).collect(Collectors.toList()); + List> payloads = suggestions.stream().map(Suggestion::getPayload).collect(Collectors.toList()); + + assertThat(suggestionsString).containsAll(List.of("Columbia", "Columbus", "Colorado Springs")); + assertThat(scores).usingComparatorForType(new DoubleComparator(0.1), Double.class).containsAll(List.of(0.41, 0.41, + 0.27)); + assertThat(payloads).hasSize(3).contains(columbusPayload, columbiaPayload, coloradoSpringsPayload); + } + }