From 60a273f6dc4c484ce5dd2c25a6b2923ee4823174 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Wed, 8 Oct 2025 11:27:38 +0300 Subject: [PATCH 1/9] feat: add CommandListener interface for monitoring Redis command operations --- .../redis/om/spring/ops/CommandListener.java | 90 +++++++ .../om/spring/ops/RedisModulesOperations.java | 8 +- .../ops/search/SearchOperationsImpl.java | 229 ++++++++++++------ 3 files changed, 251 insertions(+), 76 deletions(-) create mode 100644 redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java new file mode 100644 index 00000000..ecc96fcc --- /dev/null +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java @@ -0,0 +1,90 @@ +package com.redis.om.spring.ops; + +import com.redis.om.spring.autocomplete.Suggestion; +import com.redis.om.spring.repository.query.autocomplete.AutoCompleteOptions; +import redis.clients.jedis.search.FTCreateParams; +import redis.clients.jedis.search.FTSearchParams; +import redis.clients.jedis.search.IndexOptions; +import redis.clients.jedis.search.Query; +import redis.clients.jedis.search.Schema; +import redis.clients.jedis.search.SearchProtocol; +import redis.clients.jedis.search.SearchResult; +import redis.clients.jedis.search.aggr.AggregationBuilder; +import redis.clients.jedis.search.aggr.AggregationResult; +import redis.clients.jedis.search.schemafields.SchemaField; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface CommandListener { + default void searchStarted(String indexName, Query q, FTSearchParams params) {} + default void searchFinished(String indexName, Query q, FTSearchParams params, SearchResult searchResult){} + + default void createIndexStarted(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options){} + default void createIndexFinished(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options, String result){} + + default void aggregateStarted(String indexName, AggregationBuilder q){} + default void aggregateFinished(String indexName, AggregationBuilder q){} + + default void cursorDeleteStarted(String string, long cursorId){} + default void cursorDeleteFinished(String string, long cursorId, String result){} + + default void cursorReadStarted(String string, long cursorId, int count){} + default void cursorReadFinished(String string, long cursorId, int count, AggregationResult aggregationResult){} + + default void explainStarted(String string, Query q){} + default void explainFinished(String string, Query q, String s){} + + default void infoStarted(String string){} + default void infoFinished(String string, Map stringObjectMap){} + + default void dropIndexStarted(String string){} + default void dropIndexFinished(String string, String result){} + + default void dropIndexAndDocumentsStarted(String string){} + default void dropIndexAndDocumentsFinished(String string, String result){} + + default void addSuggestionStarted(String string, String key, String suggestion, double score){} + default void addSuggestionFinished(String string, String key, String suggestion, double score, long result){} + + default void getSuggestionStarted(String string, String key, String prefix, AutoCompleteOptions options){} + default void getSuggestionFinished(String string, String key, String prefix, AutoCompleteOptions options, List list){} + + default void deleteSuggestionStarted(String string, String key, String entry){} + default void deleteSuggestionFinished(String string, String key, String entry, boolean result){} + + default void getSuggestionLengthStarted(String string, String key){} + default void getSuggestionLengthFinished(String string, String key, long result){} + + default void alterIndexStarted(String string, SchemaField[] fields){} + default void alterIndexFinished(String string, SchemaField[] fields, String result){} + + default void setConfigStarted(String string, String option, String value){} + default void setConfigFinished(String string, String option, String value, String result){} + + default void getConfigStarted(String string, String option){} + default void getConfigFinished(String string, String option, Map result){} + + default void getIndexConfigStarted(String string, String option){} + default void getIndexConfigFinished(String string, String option, Map result){} + + default void addAliasStarted(String string, String name){} + default void addAliasFinished(String string, String name, String result){} + + default void updateAliasStarted(String string, String name){} + default void updateAliasFinished(String string, String name, String result){} + + default void deleteAliasStarted(String string, String name){} + default void deleteAliasFinished(String string, String name, String result){} + + default void updateSynonymStarted(String string, String synonymGroupId, String[] terms){} + default void updateSynonymFinished(String string, String synonymGroupId, String[] terms, String result){} + + default void dumpSynonymStarted(String string){} + default void dumpSynonymFinished(String string, Map> result){} + + default void tagValsStarted(String string, String field){} + default void tagValsFinished(String string, String field, Set result){} + + default void commandFailed(SearchProtocol.SearchCommand command, String indexName, Throwable t){} +} diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java index 3539058a..0633c766 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java @@ -9,6 +9,7 @@ import com.redis.om.spring.ops.pds.*; import com.redis.om.spring.ops.search.SearchOperations; import com.redis.om.spring.ops.search.SearchOperationsImpl; +import java.util.Optional; /** * A record that provides centralized access to Redis module operations. @@ -26,7 +27,8 @@ * @param client the Redis modules client for executing commands * @param template the Spring Data Redis template for additional Redis operations * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration - * + * @param commandListener An optional command listener for monitoring Redis commands + * * @author Redis OM Spring Team * @see JSONOperations * @see SearchOperations @@ -37,7 +39,7 @@ * @see TDigestOperations */ public record RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, - GsonBuilder gsonBuilder) { + GsonBuilder gsonBuilder, Optional commandListener) { /** * Creates and returns operations for interacting with RedisJSON module. @@ -65,7 +67,7 @@ public JSONOperations opsForJSON() { * @return a {@link SearchOperations} instance for search and indexing operations */ public SearchOperations opsForSearch(K index) { - return new SearchOperationsImpl<>(index, client, template); + return new SearchOperationsImpl<>(index, client, template, commandListener); } /** 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 ba9d706a..7187101b 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 @@ -2,8 +2,9 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; - +import com.redis.om.spring.ops.CommandListener; import org.springframework.data.redis.core.StringRedisTemplate; import com.google.gson.Gson; @@ -39,6 +40,7 @@ public class SearchOperationsImpl implements SearchOperations { private final RedisModulesClient modulesClient; private final K index; private final StringRedisTemplate template; + private final Optional commandListener; /** * Creates a new search operations implementation. @@ -46,77 +48,117 @@ public class SearchOperationsImpl implements SearchOperations { * @param index the search index identifier * @param modulesClient the Redis modules client for search operations * @param template the string Redis template for additional operations + * @param commandListener An optional command listener for monitoring Redis commands */ - public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template) { + public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template, + final Optional commandListener) { this.index = index; this.modulesClient = modulesClient; this.search = modulesClient.clientForSearch(); this.template = template; + this.commandListener = commandListener; } - @Override - public String createIndex(Schema schema, IndexOptions options) { - return search.ftCreate(index.toString(), options, schema); - } + @Override + public String createIndex(Schema schema, IndexOptions options) { + commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), null, null, schema, options)); + final String s = search.ftCreate(index.toString(), options, schema); + commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), null, null, schema, options, s)); + return s; + } - @Override - public String createIndex(FTCreateParams params, List fields) { - return search.ftCreate(index.toString(), params, fields); - } + @Override + public String createIndex(FTCreateParams params, List fields) { + commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), params, fields, null, null)); + final String s = search.ftCreate(index.toString(), params, fields); + commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), params, fields, null, null, s)); + return s; + } - @Override - public SearchResult search(Query q) { - return search.ftSearch(SafeEncoder.encode(index.toString()), q); - } + @Override + @Deprecated + public SearchResult search(Query q) { + commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + final SearchResult searchResult = search.ftSearch(SafeEncoder.encode(index.toString()), q); + commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + return searchResult; + } - @Override - public SearchResult search(Query q, FTSearchParams params) { - return search.ftSearch(index.toString(), q.toString(), params); - } + @Override + public SearchResult search(Query q, FTSearchParams params) { + commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + final SearchResult searchResult = search.ftSearch(index.toString(), q.toString(), params); + commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + return searchResult; + } - @Override - public AggregationResult aggregate(AggregationBuilder q) { - return search.ftAggregate(index.toString(), q); - } + @Override + public AggregationResult aggregate(AggregationBuilder q) { + commandListener.ifPresent(it -> it.aggregateStarted(index.toString(), q)); + final AggregationResult aggregationResult = search.ftAggregate(index.toString(), q); + commandListener.ifPresent(it -> it.aggregateFinished(index.toString(), q)); + return aggregationResult; + } - @Override - public String cursorDelete(long cursorId) { - return search.ftCursorDel(index.toString(), cursorId); - } + @Override + public String cursorDelete(long cursorId) { + commandListener.ifPresent(it -> it.cursorDeleteStarted(index.toString(), cursorId)); + final String result = search.ftCursorDel(index.toString(), cursorId); + commandListener.ifPresent(it -> it.cursorDeleteFinished(index.toString(), cursorId, result)); + return result; + } @Override public AggregationResult cursorRead(long cursorId, int count) { - return search.ftCursorRead(index.toString(), cursorId, count); + commandListener.ifPresent(it -> it.cursorReadStarted(index.toString(), cursorId, count)); + final AggregationResult aggregationResult = search.ftCursorRead(index.toString(), cursorId, count); + commandListener.ifPresent(it -> it.cursorReadFinished(index.toString(), cursorId, count, aggregationResult)); + return aggregationResult; } @Override public String explain(Query q) { - return search.ftExplain(index.toString(), q); + commandListener.ifPresent(it -> it.explainStarted(index.toString(), q)); + final String s = search.ftExplain(index.toString(), q); + commandListener.ifPresent(it -> it.explainFinished(index.toString(), q, s)); + return s; } @Override public Map getInfo() { - return search.ftInfo(index.toString()); + commandListener.ifPresent(it -> it.infoStarted(index.toString())); + final Map result = search.ftInfo(index.toString()); + commandListener.ifPresent(it -> it.infoFinished(index.toString(), result)); + return result; } @Override public String dropIndex() { - return search.ftDropIndex(index.toString()); + commandListener.ifPresent(it -> it.dropIndexStarted(index.toString())); + final String result = search.ftDropIndex(index.toString()); + commandListener.ifPresent(it -> it.dropIndexFinished(index.toString(), result)); + return result; } @Override public String dropIndexAndDocuments() { - return search.ftDropIndexDD(index.toString()); + commandListener.ifPresent(it -> it.dropIndexAndDocumentsStarted(index.toString())); + final String result = search.ftDropIndexDD(index.toString()); + commandListener.ifPresent(it -> it.dropIndexAndDocumentsFinished(index.toString(), result)); + return result; } @Override public Long addSuggestion(String key, String suggestion) { - return search.ftSugAdd(key, suggestion, 1.0); + return addSuggestion(key, suggestion, 1.0); } @Override public Long addSuggestion(String key, String suggestion, double score) { - return search.ftSugAdd(key, suggestion, score); + commandListener.ifPresent(it -> it.addSuggestionStarted(index.toString(), key, suggestion, score)); + final long result = search.ftSugAdd(key, suggestion, score); + commandListener.ifPresent(it -> it.addSuggestionFinished(index.toString(), key, suggestion, score, result)); + return result; } @Override @@ -126,101 +168,142 @@ public List getSuggestion(String key, String prefix) { @Override public List getSuggestion(String key, String prefix, AutoCompleteOptions options) { + commandListener.ifPresent(it -> it.getSuggestionStarted(index.toString(), key, prefix, options)); Gson gson = modulesClient.gsonBuilder().create(); if (options.isWithScore()) { List suggestions = search.ftSugGetWithScores(key, prefix, options.isFuzzy(), options.getLimit()); - return suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); - } else { - return new Suggestion(suggestion.getElement(), suggestion.getScore()); - } - }).toList(); + List list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); + } else { + return new Suggestion(suggestion.getElement(), suggestion.getScore()); + } + }).toList(); + commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); - return suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion, payloadMap); - } else { - return new Suggestion(suggestion); - } - }).toList(); + List list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion, payloadMap); + } else { + return new Suggestion(suggestion); + } + }).toList(); + commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + return list; } } @Override public Boolean deleteSuggestion(String key, String entry) { - return search.ftSugDel(key, entry); + commandListener.ifPresent(it -> it.deleteSuggestionStarted(index.toString(), key, entry)); + final boolean result = search.ftSugDel(key, entry); + commandListener.ifPresent(it -> it.deleteSuggestionFinished(index.toString(), key, entry, result)); + return result; } @Override public Long getSuggestionLength(String key) { - return search.ftSugLen(key); + commandListener.ifPresent(it -> it.getSuggestionLengthStarted(index.toString(), key)); + final long result = search.ftSugLen(key); + commandListener.ifPresent(it -> it.getSuggestionLengthFinished(index.toString(), key, result)); + return result; } @Override public String alterIndex(SchemaField... fields) { - return search.ftAlter(index.toString(), fields); + commandListener.ifPresent(it -> it.alterIndexStarted(index.toString(), fields)); + final String result = search.ftAlter(index.toString(), fields); + commandListener.ifPresent(it -> it.alterIndexFinished(index.toString(), fields, result)); + return result; } @Override public String setConfig(String option, String value) { - return search.ftConfigSet(option, value); + commandListener.ifPresent(it -> it.setConfigStarted(index.toString(), option, value)); + final String result = search.ftConfigSet(option, value); + commandListener.ifPresent(it -> it.setConfigFinished(index.toString(), option, value, result)); + return result; } @Override public Map getConfig(String option) { - return search.ftConfigGet(option); + commandListener.ifPresent(it -> it.getConfigStarted(index.toString(), option)); + final Map result = search.ftConfigGet(option); + commandListener.ifPresent(it -> it.getConfigFinished(index.toString(), option, result)); + return result; } @Override public Map getIndexConfig(String option) { - return search.ftConfigGet(index.toString(), option); + commandListener.ifPresent(it -> it.getIndexConfigStarted(index.toString(), option)); + final Map result = search.ftConfigGet(index.toString(), option); + commandListener.ifPresent(it -> it.getIndexConfigFinished(index.toString(), option, result)); + return result; } @Override public String addAlias(String name) { - return search.ftAliasAdd(name, index.toString()); + commandListener.ifPresent(it -> it.addAliasStarted(index.toString(), name)); + final String result = search.ftAliasAdd(name, index.toString()); + commandListener.ifPresent(it -> it.addAliasFinished(index.toString(), name, result)); + return result; } @Override public String updateAlias(String name) { - return search.ftAliasUpdate(name, index.toString()); + commandListener.ifPresent(it -> it.updateAliasStarted(index.toString(), name)); + final String result = search.ftAliasUpdate(name, index.toString()); + commandListener.ifPresent(it -> it.updateAliasFinished(index.toString(), name, result)); + return result; } @Override public String deleteAlias(String name) { - return search.ftAliasDel(name); + commandListener.ifPresent(it -> it.deleteAliasStarted(index.toString(), name)); + final String result = search.ftAliasDel(name); + commandListener.ifPresent(it -> it.deleteAliasFinished(index.toString(), name, result)); + return result; } @Override public String updateSynonym(String synonymGroupId, String... terms) { - return search.ftSynUpdate(index.toString(), synonymGroupId, terms); + commandListener.ifPresent(it -> it.updateSynonymStarted(index.toString(), synonymGroupId, terms)); + final String result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); + commandListener.ifPresent(it -> it.updateSynonymFinished(index.toString(), synonymGroupId, terms, result)); + return result; } @Override public Map> dumpSynonym() { - return search.ftSynDump(index.toString()); + commandListener.ifPresent(it -> it.dumpSynonymStarted(index.toString())); + final Map> result = search.ftSynDump(index.toString()); + commandListener.ifPresent(it -> it.dumpSynonymFinished(index.toString(), result)); + return result; } @Override public Set tagVals(String field) { - return search.ftTagVals(index.toString(), field); + commandListener.ifPresent(it -> it.tagValsStarted(index.toString(), field)); + final Set result = search.ftTagVals(index.toString(), field); + commandListener.ifPresent(it -> it.tagValsFinished(index.toString(), field, result)); + return result; } } From 89e1482fee6bd8b21a5b830b4050cf5e582d7ab9 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Fri, 10 Oct 2025 08:38:57 +0300 Subject: [PATCH 2/9] feat: add CommandListener support and NoOpCommandListener implementation for Redis command monitoring --- .../om/spring/RedisModulesConfiguration.java | 25 +++- .../om/spring/ops/NoOpCommandListener.java | 7 ++ .../om/spring/ops/RedisModulesOperations.java | 5 +- .../ops/search/SearchOperationsImpl.java | 109 +++++++++--------- 4 files changed, 86 insertions(+), 60 deletions(-) create mode 100644 redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java index f866b479..edc522e2 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Set; +import com.redis.om.spring.ops.CommandListener; +import com.redis.om.spring.ops.NoOpCommandListener; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -202,6 +204,7 @@ RedisModulesClient redisModulesClient( // * @param rmc the Redis modules client for low-level access * @param template the string Redis template for basic operations * @param gsonBuilder the Gson builder for JSON serialization + * @param commandListener a command listener for monitoring Redis commands * @return the Redis modules operations instance */ @Bean( @@ -215,10 +218,28 @@ RedisModulesOperations redisModulesOperations( // StringRedisTemplate template, // @Qualifier( "omGsonBuilder" - ) GsonBuilder gsonBuilder) { - return new RedisModulesOperations<>(rmc, template, gsonBuilder); + ) GsonBuilder gsonBuilder, final CommandListener commandListener) { + return new RedisModulesOperations<>(rmc, template, gsonBuilder, commandListener); } + /** + * Provides a default implementation of the CommandListener bean. + *

+ * This method creates a no-operation (NoOp) implementation of the CommandListener interface. + * It is used as a fallback when no other CommandListener bean is defined in the application context. + *

+ * The {@code @Fallback} annotation ensures that this bean is only used when no other + * CommandListener bean is available, allowing developers to override it with a custom implementation if needed. + * + * @return a NoOpCommandListener instance, which performs no operations. + */ + @Bean + @Fallback + public CommandListener commandListener() { + return new NoOpCommandListener(); + } + + /** * Creates the JSON operations bean for RedisJSON commands. *

diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java new file mode 100644 index 00000000..170e6125 --- /dev/null +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java @@ -0,0 +1,7 @@ +package com.redis.om.spring.ops; + +import org.springframework.stereotype.Component; + +@Component +public class NoOpCommandListener implements CommandListener { +} diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java index 0633c766..19c564ba 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java @@ -9,7 +9,6 @@ import com.redis.om.spring.ops.pds.*; import com.redis.om.spring.ops.search.SearchOperations; import com.redis.om.spring.ops.search.SearchOperationsImpl; -import java.util.Optional; /** * A record that provides centralized access to Redis module operations. @@ -27,7 +26,7 @@ * @param client the Redis modules client for executing commands * @param template the Spring Data Redis template for additional Redis operations * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration - * @param commandListener An optional command listener for monitoring Redis commands + * @param commandListener A command listener for monitoring Redis commands * * @author Redis OM Spring Team * @see JSONOperations @@ -39,7 +38,7 @@ * @see TDigestOperations */ public record RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, - GsonBuilder gsonBuilder, Optional commandListener) { + GsonBuilder gsonBuilder, CommandListener commandListener) { /** * Creates and returns operations for interacting with RedisJSON module. 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 7187101b..1c77e90f 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 @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import com.redis.om.spring.ops.CommandListener; import org.springframework.data.redis.core.StringRedisTemplate; @@ -40,7 +39,7 @@ public class SearchOperationsImpl implements SearchOperations { private final RedisModulesClient modulesClient; private final K index; private final StringRedisTemplate template; - private final Optional commandListener; + private final CommandListener commandListener; /** * Creates a new search operations implementation. @@ -48,10 +47,10 @@ public class SearchOperationsImpl implements SearchOperations { * @param index the search index identifier * @param modulesClient the Redis modules client for search operations * @param template the string Redis template for additional operations - * @param commandListener An optional command listener for monitoring Redis commands + * @param commandListener A command listener for monitoring Redis commands */ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template, - final Optional commandListener) { + final CommandListener commandListener) { this.index = index; this.modulesClient = modulesClient; this.search = modulesClient.clientForSearch(); @@ -61,90 +60,90 @@ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRed @Override public String createIndex(Schema schema, IndexOptions options) { - commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), null, null, schema, options)); + commandListener.createIndexStarted(index.toString(), null, null, schema, options); final String s = search.ftCreate(index.toString(), options, schema); - commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), null, null, schema, options, s)); + commandListener.createIndexFinished(index.toString(), null, null, schema, options, s); return s; } @Override public String createIndex(FTCreateParams params, List fields) { - commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), params, fields, null, null)); + commandListener.createIndexStarted(index.toString(), params, fields, null, null); final String s = search.ftCreate(index.toString(), params, fields); - commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), params, fields, null, null, s)); + commandListener.createIndexFinished(index.toString(), params, fields, null, null, s); return s; } @Override @Deprecated public SearchResult search(Query q) { - commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + commandListener.searchStarted(index.toString(), q, null); final SearchResult searchResult = search.ftSearch(SafeEncoder.encode(index.toString()), q); - commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + commandListener.searchFinished(index.toString(), q, null, searchResult); return searchResult; } @Override public SearchResult search(Query q, FTSearchParams params) { - commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + commandListener.searchStarted(index.toString(), q, null); final SearchResult searchResult = search.ftSearch(index.toString(), q.toString(), params); - commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + commandListener.searchFinished(index.toString(), q, null, searchResult); return searchResult; } @Override public AggregationResult aggregate(AggregationBuilder q) { - commandListener.ifPresent(it -> it.aggregateStarted(index.toString(), q)); + commandListener.aggregateStarted(index.toString(), q); final AggregationResult aggregationResult = search.ftAggregate(index.toString(), q); - commandListener.ifPresent(it -> it.aggregateFinished(index.toString(), q)); + commandListener.aggregateFinished(index.toString(), q); return aggregationResult; } @Override public String cursorDelete(long cursorId) { - commandListener.ifPresent(it -> it.cursorDeleteStarted(index.toString(), cursorId)); + commandListener.cursorDeleteStarted(index.toString(), cursorId); final String result = search.ftCursorDel(index.toString(), cursorId); - commandListener.ifPresent(it -> it.cursorDeleteFinished(index.toString(), cursorId, result)); + commandListener.cursorDeleteFinished(index.toString(), cursorId, result); return result; } @Override public AggregationResult cursorRead(long cursorId, int count) { - commandListener.ifPresent(it -> it.cursorReadStarted(index.toString(), cursorId, count)); + commandListener.cursorReadStarted(index.toString(), cursorId, count); final AggregationResult aggregationResult = search.ftCursorRead(index.toString(), cursorId, count); - commandListener.ifPresent(it -> it.cursorReadFinished(index.toString(), cursorId, count, aggregationResult)); + commandListener.cursorReadFinished(index.toString(), cursorId, count, aggregationResult); return aggregationResult; } @Override public String explain(Query q) { - commandListener.ifPresent(it -> it.explainStarted(index.toString(), q)); + commandListener.explainStarted(index.toString(), q); final String s = search.ftExplain(index.toString(), q); - commandListener.ifPresent(it -> it.explainFinished(index.toString(), q, s)); + commandListener.explainFinished(index.toString(), q, s); return s; } @Override public Map getInfo() { - commandListener.ifPresent(it -> it.infoStarted(index.toString())); + commandListener.infoStarted(index.toString()); final Map result = search.ftInfo(index.toString()); - commandListener.ifPresent(it -> it.infoFinished(index.toString(), result)); + commandListener.infoFinished(index.toString(), result); return result; } @Override public String dropIndex() { - commandListener.ifPresent(it -> it.dropIndexStarted(index.toString())); + commandListener.dropIndexStarted(index.toString()); final String result = search.ftDropIndex(index.toString()); - commandListener.ifPresent(it -> it.dropIndexFinished(index.toString(), result)); + commandListener.dropIndexFinished(index.toString(), result); return result; } @Override public String dropIndexAndDocuments() { - commandListener.ifPresent(it -> it.dropIndexAndDocumentsStarted(index.toString())); + commandListener.dropIndexAndDocumentsStarted(index.toString()); final String result = search.ftDropIndexDD(index.toString()); - commandListener.ifPresent(it -> it.dropIndexAndDocumentsFinished(index.toString(), result)); + commandListener.dropIndexAndDocumentsFinished(index.toString(), result); return result; } @@ -155,9 +154,9 @@ public Long addSuggestion(String key, String suggestion) { @Override public Long addSuggestion(String key, String suggestion, double score) { - commandListener.ifPresent(it -> it.addSuggestionStarted(index.toString(), key, suggestion, score)); + commandListener.addSuggestionStarted(index.toString(), key, suggestion, score); final long result = search.ftSugAdd(key, suggestion, score); - commandListener.ifPresent(it -> it.addSuggestionFinished(index.toString(), key, suggestion, score, result)); + commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); return result; } @@ -168,7 +167,7 @@ public List getSuggestion(String key, String prefix) { @Override public List getSuggestion(String key, String prefix, AutoCompleteOptions options) { - commandListener.ifPresent(it -> it.getSuggestionStarted(index.toString(), key, prefix, options)); + commandListener.getSuggestionStarted(index.toString(), key, prefix, options); Gson gson = modulesClient.gsonBuilder().create(); if (options.isWithScore()) { @@ -187,7 +186,7 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt return new Suggestion(suggestion.getElement(), suggestion.getScore()); } }).toList(); - commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); @@ -205,104 +204,104 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt return new Suggestion(suggestion); } }).toList(); - commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); return list; } } @Override public Boolean deleteSuggestion(String key, String entry) { - commandListener.ifPresent(it -> it.deleteSuggestionStarted(index.toString(), key, entry)); + commandListener.deleteSuggestionStarted(index.toString(), key, entry); final boolean result = search.ftSugDel(key, entry); - commandListener.ifPresent(it -> it.deleteSuggestionFinished(index.toString(), key, entry, result)); + commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); return result; } @Override public Long getSuggestionLength(String key) { - commandListener.ifPresent(it -> it.getSuggestionLengthStarted(index.toString(), key)); + commandListener.getSuggestionLengthStarted(index.toString(), key); final long result = search.ftSugLen(key); - commandListener.ifPresent(it -> it.getSuggestionLengthFinished(index.toString(), key, result)); + commandListener.getSuggestionLengthFinished(index.toString(), key, result); return result; } @Override public String alterIndex(SchemaField... fields) { - commandListener.ifPresent(it -> it.alterIndexStarted(index.toString(), fields)); + commandListener.alterIndexStarted(index.toString(), fields); final String result = search.ftAlter(index.toString(), fields); - commandListener.ifPresent(it -> it.alterIndexFinished(index.toString(), fields, result)); + commandListener.alterIndexFinished(index.toString(), fields, result); return result; } @Override public String setConfig(String option, String value) { - commandListener.ifPresent(it -> it.setConfigStarted(index.toString(), option, value)); + commandListener.setConfigStarted(index.toString(), option, value); final String result = search.ftConfigSet(option, value); - commandListener.ifPresent(it -> it.setConfigFinished(index.toString(), option, value, result)); + commandListener.setConfigFinished(index.toString(), option, value, result); return result; } @Override public Map getConfig(String option) { - commandListener.ifPresent(it -> it.getConfigStarted(index.toString(), option)); + commandListener.getConfigStarted(index.toString(), option); final Map result = search.ftConfigGet(option); - commandListener.ifPresent(it -> it.getConfigFinished(index.toString(), option, result)); + commandListener.getConfigFinished(index.toString(), option, result); return result; } @Override public Map getIndexConfig(String option) { - commandListener.ifPresent(it -> it.getIndexConfigStarted(index.toString(), option)); + commandListener.getIndexConfigStarted(index.toString(), option); final Map result = search.ftConfigGet(index.toString(), option); - commandListener.ifPresent(it -> it.getIndexConfigFinished(index.toString(), option, result)); + commandListener.getIndexConfigFinished(index.toString(), option, result); return result; } @Override public String addAlias(String name) { - commandListener.ifPresent(it -> it.addAliasStarted(index.toString(), name)); + commandListener.addAliasStarted(index.toString(), name); final String result = search.ftAliasAdd(name, index.toString()); - commandListener.ifPresent(it -> it.addAliasFinished(index.toString(), name, result)); + commandListener.addAliasFinished(index.toString(), name, result); return result; } @Override public String updateAlias(String name) { - commandListener.ifPresent(it -> it.updateAliasStarted(index.toString(), name)); + commandListener.updateAliasStarted(index.toString(), name); final String result = search.ftAliasUpdate(name, index.toString()); - commandListener.ifPresent(it -> it.updateAliasFinished(index.toString(), name, result)); + commandListener.updateAliasFinished(index.toString(), name, result); return result; } @Override public String deleteAlias(String name) { - commandListener.ifPresent(it -> it.deleteAliasStarted(index.toString(), name)); + commandListener.deleteAliasStarted(index.toString(), name); final String result = search.ftAliasDel(name); - commandListener.ifPresent(it -> it.deleteAliasFinished(index.toString(), name, result)); + commandListener.deleteAliasFinished(index.toString(), name, result); return result; } @Override public String updateSynonym(String synonymGroupId, String... terms) { - commandListener.ifPresent(it -> it.updateSynonymStarted(index.toString(), synonymGroupId, terms)); + commandListener.updateSynonymStarted(index.toString(), synonymGroupId, terms); final String result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); - commandListener.ifPresent(it -> it.updateSynonymFinished(index.toString(), synonymGroupId, terms, result)); + commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); return result; } @Override public Map> dumpSynonym() { - commandListener.ifPresent(it -> it.dumpSynonymStarted(index.toString())); + commandListener.dumpSynonymStarted(index.toString()); final Map> result = search.ftSynDump(index.toString()); - commandListener.ifPresent(it -> it.dumpSynonymFinished(index.toString(), result)); + commandListener.dumpSynonymFinished(index.toString(), result); return result; } @Override public Set tagVals(String field) { - commandListener.ifPresent(it -> it.tagValsStarted(index.toString(), field)); + commandListener.tagValsStarted(index.toString(), field); final Set result = search.ftTagVals(index.toString(), field); - commandListener.ifPresent(it -> it.tagValsFinished(index.toString(), field, result)); + commandListener.tagValsFinished(index.toString(), field, result); return result; } From 8474f19bc11a9a2baabd5f650add39fb604516b6 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Wed, 8 Oct 2025 11:27:38 +0300 Subject: [PATCH 3/9] feat: add CommandListener interface for monitoring Redis command operations --- .../redis/om/spring/ops/CommandListener.java | 90 +++++++ .../om/spring/ops/RedisModulesOperations.java | 8 +- .../ops/search/SearchOperationsImpl.java | 227 ++++++++++++------ 3 files changed, 250 insertions(+), 75 deletions(-) create mode 100644 redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java new file mode 100644 index 00000000..ecc96fcc --- /dev/null +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java @@ -0,0 +1,90 @@ +package com.redis.om.spring.ops; + +import com.redis.om.spring.autocomplete.Suggestion; +import com.redis.om.spring.repository.query.autocomplete.AutoCompleteOptions; +import redis.clients.jedis.search.FTCreateParams; +import redis.clients.jedis.search.FTSearchParams; +import redis.clients.jedis.search.IndexOptions; +import redis.clients.jedis.search.Query; +import redis.clients.jedis.search.Schema; +import redis.clients.jedis.search.SearchProtocol; +import redis.clients.jedis.search.SearchResult; +import redis.clients.jedis.search.aggr.AggregationBuilder; +import redis.clients.jedis.search.aggr.AggregationResult; +import redis.clients.jedis.search.schemafields.SchemaField; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface CommandListener { + default void searchStarted(String indexName, Query q, FTSearchParams params) {} + default void searchFinished(String indexName, Query q, FTSearchParams params, SearchResult searchResult){} + + default void createIndexStarted(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options){} + default void createIndexFinished(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options, String result){} + + default void aggregateStarted(String indexName, AggregationBuilder q){} + default void aggregateFinished(String indexName, AggregationBuilder q){} + + default void cursorDeleteStarted(String string, long cursorId){} + default void cursorDeleteFinished(String string, long cursorId, String result){} + + default void cursorReadStarted(String string, long cursorId, int count){} + default void cursorReadFinished(String string, long cursorId, int count, AggregationResult aggregationResult){} + + default void explainStarted(String string, Query q){} + default void explainFinished(String string, Query q, String s){} + + default void infoStarted(String string){} + default void infoFinished(String string, Map stringObjectMap){} + + default void dropIndexStarted(String string){} + default void dropIndexFinished(String string, String result){} + + default void dropIndexAndDocumentsStarted(String string){} + default void dropIndexAndDocumentsFinished(String string, String result){} + + default void addSuggestionStarted(String string, String key, String suggestion, double score){} + default void addSuggestionFinished(String string, String key, String suggestion, double score, long result){} + + default void getSuggestionStarted(String string, String key, String prefix, AutoCompleteOptions options){} + default void getSuggestionFinished(String string, String key, String prefix, AutoCompleteOptions options, List list){} + + default void deleteSuggestionStarted(String string, String key, String entry){} + default void deleteSuggestionFinished(String string, String key, String entry, boolean result){} + + default void getSuggestionLengthStarted(String string, String key){} + default void getSuggestionLengthFinished(String string, String key, long result){} + + default void alterIndexStarted(String string, SchemaField[] fields){} + default void alterIndexFinished(String string, SchemaField[] fields, String result){} + + default void setConfigStarted(String string, String option, String value){} + default void setConfigFinished(String string, String option, String value, String result){} + + default void getConfigStarted(String string, String option){} + default void getConfigFinished(String string, String option, Map result){} + + default void getIndexConfigStarted(String string, String option){} + default void getIndexConfigFinished(String string, String option, Map result){} + + default void addAliasStarted(String string, String name){} + default void addAliasFinished(String string, String name, String result){} + + default void updateAliasStarted(String string, String name){} + default void updateAliasFinished(String string, String name, String result){} + + default void deleteAliasStarted(String string, String name){} + default void deleteAliasFinished(String string, String name, String result){} + + default void updateSynonymStarted(String string, String synonymGroupId, String[] terms){} + default void updateSynonymFinished(String string, String synonymGroupId, String[] terms, String result){} + + default void dumpSynonymStarted(String string){} + default void dumpSynonymFinished(String string, Map> result){} + + default void tagValsStarted(String string, String field){} + default void tagValsFinished(String string, String field, Set result){} + + default void commandFailed(SearchProtocol.SearchCommand command, String indexName, Throwable t){} +} diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java index 3539058a..0633c766 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java @@ -9,6 +9,7 @@ import com.redis.om.spring.ops.pds.*; import com.redis.om.spring.ops.search.SearchOperations; import com.redis.om.spring.ops.search.SearchOperationsImpl; +import java.util.Optional; /** * A record that provides centralized access to Redis module operations. @@ -26,7 +27,8 @@ * @param client the Redis modules client for executing commands * @param template the Spring Data Redis template for additional Redis operations * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration - * + * @param commandListener An optional command listener for monitoring Redis commands + * * @author Redis OM Spring Team * @see JSONOperations * @see SearchOperations @@ -37,7 +39,7 @@ * @see TDigestOperations */ public record RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, - GsonBuilder gsonBuilder) { + GsonBuilder gsonBuilder, Optional commandListener) { /** * Creates and returns operations for interacting with RedisJSON module. @@ -65,7 +67,7 @@ public JSONOperations opsForJSON() { * @return a {@link SearchOperations} instance for search and indexing operations */ public SearchOperations opsForSearch(K index) { - return new SearchOperationsImpl<>(index, client, template); + return new SearchOperationsImpl<>(index, client, template, commandListener); } /** 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 19fdc18a..86879d93 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 @@ -2,8 +2,9 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; - +import com.redis.om.spring.ops.CommandListener; import org.springframework.data.redis.core.StringRedisTemplate; import com.google.gson.Gson; @@ -39,6 +40,7 @@ public class SearchOperationsImpl implements SearchOperations { private final RedisModulesClient modulesClient; private final K index; private final StringRedisTemplate template; + private final Optional commandListener; /** * Creates a new search operations implementation. @@ -46,77 +48,117 @@ public class SearchOperationsImpl implements SearchOperations { * @param index the search index identifier * @param modulesClient the Redis modules client for search operations * @param template the string Redis template for additional operations + * @param commandListener An optional command listener for monitoring Redis commands */ - public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template) { + public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template, + final Optional commandListener) { this.index = index; this.modulesClient = modulesClient; this.search = modulesClient.clientForSearch(); this.template = template; + this.commandListener = commandListener; } - @Override - public String createIndex(Schema schema, IndexOptions options) { - return search.ftCreate(index.toString(), options, schema); - } + @Override + public String createIndex(Schema schema, IndexOptions options) { + commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), null, null, schema, options)); + final String s = search.ftCreate(index.toString(), options, schema); + commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), null, null, schema, options, s)); + return s; + } - @Override - public String createIndex(FTCreateParams params, List fields) { - return search.ftCreate(index.toString(), params, fields); - } + @Override + public String createIndex(FTCreateParams params, List fields) { + commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), params, fields, null, null)); + final String s = search.ftCreate(index.toString(), params, fields); + commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), params, fields, null, null, s)); + return s; + } - @Override - public SearchResult search(Query q) { - return search.ftSearch(SafeEncoder.encode(index.toString()), q); - } + @Override + @Deprecated + public SearchResult search(Query q) { + commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + final SearchResult searchResult = search.ftSearch(SafeEncoder.encode(index.toString()), q); + commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + return searchResult; + } - @Override - public SearchResult search(Query q, FTSearchParams params) { - return search.ftSearch(index.toString(), q.toString(), params); - } + @Override + public SearchResult search(Query q, FTSearchParams params) { + commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + final SearchResult searchResult = search.ftSearch(index.toString(), q.toString(), params); + commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + return searchResult; + } - @Override - public AggregationResult aggregate(AggregationBuilder q) { - return search.ftAggregate(index.toString(), q); - } + @Override + public AggregationResult aggregate(AggregationBuilder q) { + commandListener.ifPresent(it -> it.aggregateStarted(index.toString(), q)); + final AggregationResult aggregationResult = search.ftAggregate(index.toString(), q); + commandListener.ifPresent(it -> it.aggregateFinished(index.toString(), q)); + return aggregationResult; + } - @Override - public String cursorDelete(long cursorId) { - return search.ftCursorDel(index.toString(), cursorId); - } + @Override + public String cursorDelete(long cursorId) { + commandListener.ifPresent(it -> it.cursorDeleteStarted(index.toString(), cursorId)); + final String result = search.ftCursorDel(index.toString(), cursorId); + commandListener.ifPresent(it -> it.cursorDeleteFinished(index.toString(), cursorId, result)); + return result; + } @Override public AggregationResult cursorRead(long cursorId, int count) { - return search.ftCursorRead(index.toString(), cursorId, count); + commandListener.ifPresent(it -> it.cursorReadStarted(index.toString(), cursorId, count)); + final AggregationResult aggregationResult = search.ftCursorRead(index.toString(), cursorId, count); + commandListener.ifPresent(it -> it.cursorReadFinished(index.toString(), cursorId, count, aggregationResult)); + return aggregationResult; } @Override public String explain(Query q) { - return search.ftExplain(index.toString(), q); + commandListener.ifPresent(it -> it.explainStarted(index.toString(), q)); + final String s = search.ftExplain(index.toString(), q); + commandListener.ifPresent(it -> it.explainFinished(index.toString(), q, s)); + return s; } @Override public Map getInfo() { - return search.ftInfo(index.toString()); + commandListener.ifPresent(it -> it.infoStarted(index.toString())); + final Map result = search.ftInfo(index.toString()); + commandListener.ifPresent(it -> it.infoFinished(index.toString(), result)); + return result; } @Override public String dropIndex() { - return search.ftDropIndex(index.toString()); + commandListener.ifPresent(it -> it.dropIndexStarted(index.toString())); + final String result = search.ftDropIndex(index.toString()); + commandListener.ifPresent(it -> it.dropIndexFinished(index.toString(), result)); + return result; } @Override public String dropIndexAndDocuments() { - return search.ftDropIndexDD(index.toString()); + commandListener.ifPresent(it -> it.dropIndexAndDocumentsStarted(index.toString())); + final String result = search.ftDropIndexDD(index.toString()); + commandListener.ifPresent(it -> it.dropIndexAndDocumentsFinished(index.toString(), result)); + return result; } @Override public Long addSuggestion(String key, String suggestion) { - return search.ftSugAdd(key, suggestion, 1.0); + return addSuggestion(key, suggestion, 1.0); } @Override public Long addSuggestion(String key, String suggestion, double score) { - return search.ftSugAdd(key, suggestion, score); + commandListener.ifPresent(it -> it.addSuggestionStarted(index.toString(), key, suggestion, score)); + final long result = search.ftSugAdd(key, suggestion, score); + commandListener.ifPresent(it -> it.addSuggestionFinished(index.toString(), key, suggestion, score, result)); + return result; } @Override @@ -126,101 +168,142 @@ public List getSuggestion(String key, String prefix) { @Override public List getSuggestion(String key, String prefix, AutoCompleteOptions options) { + commandListener.ifPresent(it -> it.getSuggestionStarted(index.toString(), key, prefix, options)); Gson gson = modulesClient.gsonBuilder().create(); if (options.isWithScore()) { List suggestions = search.ftSugGetWithScores(key, prefix, options.isFuzzy(), options.getLimit()); - return suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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.getElement()); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); - } else { - return new Suggestion(suggestion.getElement(), suggestion.getScore()); - } + List list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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.getElement()); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); + } else { + return new Suggestion(suggestion.getElement(), suggestion.getScore()); + } }).toList(); + commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); - return suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion, payloadMap); - } else { - return new Suggestion(suggestion); - } - }).toList(); + List list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion, payloadMap); + } else { + return new Suggestion(suggestion); + } + }).toList(); + commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + return list; } } @Override public Boolean deleteSuggestion(String key, String entry) { - return search.ftSugDel(key, entry); + commandListener.ifPresent(it -> it.deleteSuggestionStarted(index.toString(), key, entry)); + final boolean result = search.ftSugDel(key, entry); + commandListener.ifPresent(it -> it.deleteSuggestionFinished(index.toString(), key, entry, result)); + return result; } @Override public Long getSuggestionLength(String key) { - return search.ftSugLen(key); + commandListener.ifPresent(it -> it.getSuggestionLengthStarted(index.toString(), key)); + final long result = search.ftSugLen(key); + commandListener.ifPresent(it -> it.getSuggestionLengthFinished(index.toString(), key, result)); + return result; } @Override public String alterIndex(SchemaField... fields) { - return search.ftAlter(index.toString(), fields); + commandListener.ifPresent(it -> it.alterIndexStarted(index.toString(), fields)); + final String result = search.ftAlter(index.toString(), fields); + commandListener.ifPresent(it -> it.alterIndexFinished(index.toString(), fields, result)); + return result; } @Override public String setConfig(String option, String value) { - return search.ftConfigSet(option, value); + commandListener.ifPresent(it -> it.setConfigStarted(index.toString(), option, value)); + final String result = search.ftConfigSet(option, value); + commandListener.ifPresent(it -> it.setConfigFinished(index.toString(), option, value, result)); + return result; } @Override public Map getConfig(String option) { - return search.ftConfigGet(option); + commandListener.ifPresent(it -> it.getConfigStarted(index.toString(), option)); + final Map result = search.ftConfigGet(option); + commandListener.ifPresent(it -> it.getConfigFinished(index.toString(), option, result)); + return result; } @Override public Map getIndexConfig(String option) { - return search.ftConfigGet(index.toString(), option); + commandListener.ifPresent(it -> it.getIndexConfigStarted(index.toString(), option)); + final Map result = search.ftConfigGet(index.toString(), option); + commandListener.ifPresent(it -> it.getIndexConfigFinished(index.toString(), option, result)); + return result; } @Override public String addAlias(String name) { - return search.ftAliasAdd(name, index.toString()); + commandListener.ifPresent(it -> it.addAliasStarted(index.toString(), name)); + final String result = search.ftAliasAdd(name, index.toString()); + commandListener.ifPresent(it -> it.addAliasFinished(index.toString(), name, result)); + return result; } @Override public String updateAlias(String name) { - return search.ftAliasUpdate(name, index.toString()); + commandListener.ifPresent(it -> it.updateAliasStarted(index.toString(), name)); + final String result = search.ftAliasUpdate(name, index.toString()); + commandListener.ifPresent(it -> it.updateAliasFinished(index.toString(), name, result)); + return result; } @Override public String deleteAlias(String name) { - return search.ftAliasDel(name); + commandListener.ifPresent(it -> it.deleteAliasStarted(index.toString(), name)); + final String result = search.ftAliasDel(name); + commandListener.ifPresent(it -> it.deleteAliasFinished(index.toString(), name, result)); + return result; } @Override public String updateSynonym(String synonymGroupId, String... terms) { - return search.ftSynUpdate(index.toString(), synonymGroupId, terms); + commandListener.ifPresent(it -> it.updateSynonymStarted(index.toString(), synonymGroupId, terms)); + final String result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); + commandListener.ifPresent(it -> it.updateSynonymFinished(index.toString(), synonymGroupId, terms, result)); + return result; } @Override public Map> dumpSynonym() { - return search.ftSynDump(index.toString()); + commandListener.ifPresent(it -> it.dumpSynonymStarted(index.toString())); + final Map> result = search.ftSynDump(index.toString()); + commandListener.ifPresent(it -> it.dumpSynonymFinished(index.toString(), result)); + return result; } @Override public Set tagVals(String field) { - return search.ftTagVals(index.toString(), field); + commandListener.ifPresent(it -> it.tagValsStarted(index.toString(), field)); + final Set result = search.ftTagVals(index.toString(), field); + commandListener.ifPresent(it -> it.tagValsFinished(index.toString(), field, result)); + return result; } } From 38978f045221a526e905b6bff3c714fcb06eb408 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 27 Nov 2025 13:44:30 +0300 Subject: [PATCH 4/9] feat: add CommandListener support and NoOpCommandListener implementation for Redis command monitoring --- .../om/spring/RedisModulesConfiguration.java | 25 +++- .../om/spring/ops/NoOpCommandListener.java | 4 + .../om/spring/ops/RedisModulesOperations.java | 5 +- .../ops/search/SearchOperationsImpl.java | 109 +++++++++--------- 4 files changed, 83 insertions(+), 60 deletions(-) create mode 100644 redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java index f866b479..edc522e2 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Set; +import com.redis.om.spring.ops.CommandListener; +import com.redis.om.spring.ops.NoOpCommandListener; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -202,6 +204,7 @@ RedisModulesClient redisModulesClient( // * @param rmc the Redis modules client for low-level access * @param template the string Redis template for basic operations * @param gsonBuilder the Gson builder for JSON serialization + * @param commandListener a command listener for monitoring Redis commands * @return the Redis modules operations instance */ @Bean( @@ -215,10 +218,28 @@ RedisModulesOperations redisModulesOperations( // StringRedisTemplate template, // @Qualifier( "omGsonBuilder" - ) GsonBuilder gsonBuilder) { - return new RedisModulesOperations<>(rmc, template, gsonBuilder); + ) GsonBuilder gsonBuilder, final CommandListener commandListener) { + return new RedisModulesOperations<>(rmc, template, gsonBuilder, commandListener); } + /** + * Provides a default implementation of the CommandListener bean. + *

+ * This method creates a no-operation (NoOp) implementation of the CommandListener interface. + * It is used as a fallback when no other CommandListener bean is defined in the application context. + *

+ * The {@code @Fallback} annotation ensures that this bean is only used when no other + * CommandListener bean is available, allowing developers to override it with a custom implementation if needed. + * + * @return a NoOpCommandListener instance, which performs no operations. + */ + @Bean + @Fallback + public CommandListener commandListener() { + return new NoOpCommandListener(); + } + + /** * Creates the JSON operations bean for RedisJSON commands. *

diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java new file mode 100644 index 00000000..3b7fdc81 --- /dev/null +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java @@ -0,0 +1,4 @@ +package com.redis.om.spring.ops; + +public class NoOpCommandListener implements CommandListener { +} diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java index 0633c766..19c564ba 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java @@ -9,7 +9,6 @@ import com.redis.om.spring.ops.pds.*; import com.redis.om.spring.ops.search.SearchOperations; import com.redis.om.spring.ops.search.SearchOperationsImpl; -import java.util.Optional; /** * A record that provides centralized access to Redis module operations. @@ -27,7 +26,7 @@ * @param client the Redis modules client for executing commands * @param template the Spring Data Redis template for additional Redis operations * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration - * @param commandListener An optional command listener for monitoring Redis commands + * @param commandListener A command listener for monitoring Redis commands * * @author Redis OM Spring Team * @see JSONOperations @@ -39,7 +38,7 @@ * @see TDigestOperations */ public record RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, - GsonBuilder gsonBuilder, Optional commandListener) { + GsonBuilder gsonBuilder, CommandListener commandListener) { /** * Creates and returns operations for interacting with RedisJSON module. 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 86879d93..289cbcf4 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 @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import com.redis.om.spring.ops.CommandListener; import org.springframework.data.redis.core.StringRedisTemplate; @@ -40,7 +39,7 @@ public class SearchOperationsImpl implements SearchOperations { private final RedisModulesClient modulesClient; private final K index; private final StringRedisTemplate template; - private final Optional commandListener; + private final CommandListener commandListener; /** * Creates a new search operations implementation. @@ -48,10 +47,10 @@ public class SearchOperationsImpl implements SearchOperations { * @param index the search index identifier * @param modulesClient the Redis modules client for search operations * @param template the string Redis template for additional operations - * @param commandListener An optional command listener for monitoring Redis commands + * @param commandListener A command listener for monitoring Redis commands */ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template, - final Optional commandListener) { + final CommandListener commandListener) { this.index = index; this.modulesClient = modulesClient; this.search = modulesClient.clientForSearch(); @@ -61,90 +60,90 @@ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRed @Override public String createIndex(Schema schema, IndexOptions options) { - commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), null, null, schema, options)); + commandListener.createIndexStarted(index.toString(), null, null, schema, options); final String s = search.ftCreate(index.toString(), options, schema); - commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), null, null, schema, options, s)); + commandListener.createIndexFinished(index.toString(), null, null, schema, options, s); return s; } @Override public String createIndex(FTCreateParams params, List fields) { - commandListener.ifPresent(it -> it.createIndexStarted(index.toString(), params, fields, null, null)); + commandListener.createIndexStarted(index.toString(), params, fields, null, null); final String s = search.ftCreate(index.toString(), params, fields); - commandListener.ifPresent(it -> it.createIndexFinished(index.toString(), params, fields, null, null, s)); + commandListener.createIndexFinished(index.toString(), params, fields, null, null, s); return s; } @Override @Deprecated public SearchResult search(Query q) { - commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + commandListener.searchStarted(index.toString(), q, null); final SearchResult searchResult = search.ftSearch(SafeEncoder.encode(index.toString()), q); - commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + commandListener.searchFinished(index.toString(), q, null, searchResult); return searchResult; } @Override public SearchResult search(Query q, FTSearchParams params) { - commandListener.ifPresent(it -> it.searchStarted(index.toString(), q, null)); + commandListener.searchStarted(index.toString(), q, null); final SearchResult searchResult = search.ftSearch(index.toString(), q.toString(), params); - commandListener.ifPresent(it -> it.searchFinished(index.toString(), q, null, searchResult)); + commandListener.searchFinished(index.toString(), q, null, searchResult); return searchResult; } @Override public AggregationResult aggregate(AggregationBuilder q) { - commandListener.ifPresent(it -> it.aggregateStarted(index.toString(), q)); + commandListener.aggregateStarted(index.toString(), q); final AggregationResult aggregationResult = search.ftAggregate(index.toString(), q); - commandListener.ifPresent(it -> it.aggregateFinished(index.toString(), q)); + commandListener.aggregateFinished(index.toString(), q); return aggregationResult; } @Override public String cursorDelete(long cursorId) { - commandListener.ifPresent(it -> it.cursorDeleteStarted(index.toString(), cursorId)); + commandListener.cursorDeleteStarted(index.toString(), cursorId); final String result = search.ftCursorDel(index.toString(), cursorId); - commandListener.ifPresent(it -> it.cursorDeleteFinished(index.toString(), cursorId, result)); + commandListener.cursorDeleteFinished(index.toString(), cursorId, result); return result; } @Override public AggregationResult cursorRead(long cursorId, int count) { - commandListener.ifPresent(it -> it.cursorReadStarted(index.toString(), cursorId, count)); + commandListener.cursorReadStarted(index.toString(), cursorId, count); final AggregationResult aggregationResult = search.ftCursorRead(index.toString(), cursorId, count); - commandListener.ifPresent(it -> it.cursorReadFinished(index.toString(), cursorId, count, aggregationResult)); + commandListener.cursorReadFinished(index.toString(), cursorId, count, aggregationResult); return aggregationResult; } @Override public String explain(Query q) { - commandListener.ifPresent(it -> it.explainStarted(index.toString(), q)); + commandListener.explainStarted(index.toString(), q); final String s = search.ftExplain(index.toString(), q); - commandListener.ifPresent(it -> it.explainFinished(index.toString(), q, s)); + commandListener.explainFinished(index.toString(), q, s); return s; } @Override public Map getInfo() { - commandListener.ifPresent(it -> it.infoStarted(index.toString())); + commandListener.infoStarted(index.toString()); final Map result = search.ftInfo(index.toString()); - commandListener.ifPresent(it -> it.infoFinished(index.toString(), result)); + commandListener.infoFinished(index.toString(), result); return result; } @Override public String dropIndex() { - commandListener.ifPresent(it -> it.dropIndexStarted(index.toString())); + commandListener.dropIndexStarted(index.toString()); final String result = search.ftDropIndex(index.toString()); - commandListener.ifPresent(it -> it.dropIndexFinished(index.toString(), result)); + commandListener.dropIndexFinished(index.toString(), result); return result; } @Override public String dropIndexAndDocuments() { - commandListener.ifPresent(it -> it.dropIndexAndDocumentsStarted(index.toString())); + commandListener.dropIndexAndDocumentsStarted(index.toString()); final String result = search.ftDropIndexDD(index.toString()); - commandListener.ifPresent(it -> it.dropIndexAndDocumentsFinished(index.toString(), result)); + commandListener.dropIndexAndDocumentsFinished(index.toString(), result); return result; } @@ -155,9 +154,9 @@ public Long addSuggestion(String key, String suggestion) { @Override public Long addSuggestion(String key, String suggestion, double score) { - commandListener.ifPresent(it -> it.addSuggestionStarted(index.toString(), key, suggestion, score)); + commandListener.addSuggestionStarted(index.toString(), key, suggestion, score); final long result = search.ftSugAdd(key, suggestion, score); - commandListener.ifPresent(it -> it.addSuggestionFinished(index.toString(), key, suggestion, score, result)); + commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); return result; } @@ -168,7 +167,7 @@ public List getSuggestion(String key, String prefix) { @Override public List getSuggestion(String key, String prefix, AutoCompleteOptions options) { - commandListener.ifPresent(it -> it.getSuggestionStarted(index.toString(), key, prefix, options)); + commandListener.getSuggestionStarted(index.toString(), key, prefix, options); Gson gson = modulesClient.gsonBuilder().create(); if (options.isWithScore()) { @@ -187,7 +186,7 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt return new Suggestion(suggestion.getElement(), suggestion.getScore()); } }).toList(); - commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); @@ -205,104 +204,104 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt return new Suggestion(suggestion); } }).toList(); - commandListener.ifPresent(it -> it.getSuggestionFinished(index.toString(), key, prefix, options, list)); + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); return list; } } @Override public Boolean deleteSuggestion(String key, String entry) { - commandListener.ifPresent(it -> it.deleteSuggestionStarted(index.toString(), key, entry)); + commandListener.deleteSuggestionStarted(index.toString(), key, entry); final boolean result = search.ftSugDel(key, entry); - commandListener.ifPresent(it -> it.deleteSuggestionFinished(index.toString(), key, entry, result)); + commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); return result; } @Override public Long getSuggestionLength(String key) { - commandListener.ifPresent(it -> it.getSuggestionLengthStarted(index.toString(), key)); + commandListener.getSuggestionLengthStarted(index.toString(), key); final long result = search.ftSugLen(key); - commandListener.ifPresent(it -> it.getSuggestionLengthFinished(index.toString(), key, result)); + commandListener.getSuggestionLengthFinished(index.toString(), key, result); return result; } @Override public String alterIndex(SchemaField... fields) { - commandListener.ifPresent(it -> it.alterIndexStarted(index.toString(), fields)); + commandListener.alterIndexStarted(index.toString(), fields); final String result = search.ftAlter(index.toString(), fields); - commandListener.ifPresent(it -> it.alterIndexFinished(index.toString(), fields, result)); + commandListener.alterIndexFinished(index.toString(), fields, result); return result; } @Override public String setConfig(String option, String value) { - commandListener.ifPresent(it -> it.setConfigStarted(index.toString(), option, value)); + commandListener.setConfigStarted(index.toString(), option, value); final String result = search.ftConfigSet(option, value); - commandListener.ifPresent(it -> it.setConfigFinished(index.toString(), option, value, result)); + commandListener.setConfigFinished(index.toString(), option, value, result); return result; } @Override public Map getConfig(String option) { - commandListener.ifPresent(it -> it.getConfigStarted(index.toString(), option)); + commandListener.getConfigStarted(index.toString(), option); final Map result = search.ftConfigGet(option); - commandListener.ifPresent(it -> it.getConfigFinished(index.toString(), option, result)); + commandListener.getConfigFinished(index.toString(), option, result); return result; } @Override public Map getIndexConfig(String option) { - commandListener.ifPresent(it -> it.getIndexConfigStarted(index.toString(), option)); + commandListener.getIndexConfigStarted(index.toString(), option); final Map result = search.ftConfigGet(index.toString(), option); - commandListener.ifPresent(it -> it.getIndexConfigFinished(index.toString(), option, result)); + commandListener.getIndexConfigFinished(index.toString(), option, result); return result; } @Override public String addAlias(String name) { - commandListener.ifPresent(it -> it.addAliasStarted(index.toString(), name)); + commandListener.addAliasStarted(index.toString(), name); final String result = search.ftAliasAdd(name, index.toString()); - commandListener.ifPresent(it -> it.addAliasFinished(index.toString(), name, result)); + commandListener.addAliasFinished(index.toString(), name, result); return result; } @Override public String updateAlias(String name) { - commandListener.ifPresent(it -> it.updateAliasStarted(index.toString(), name)); + commandListener.updateAliasStarted(index.toString(), name); final String result = search.ftAliasUpdate(name, index.toString()); - commandListener.ifPresent(it -> it.updateAliasFinished(index.toString(), name, result)); + commandListener.updateAliasFinished(index.toString(), name, result); return result; } @Override public String deleteAlias(String name) { - commandListener.ifPresent(it -> it.deleteAliasStarted(index.toString(), name)); + commandListener.deleteAliasStarted(index.toString(), name); final String result = search.ftAliasDel(name); - commandListener.ifPresent(it -> it.deleteAliasFinished(index.toString(), name, result)); + commandListener.deleteAliasFinished(index.toString(), name, result); return result; } @Override public String updateSynonym(String synonymGroupId, String... terms) { - commandListener.ifPresent(it -> it.updateSynonymStarted(index.toString(), synonymGroupId, terms)); + commandListener.updateSynonymStarted(index.toString(), synonymGroupId, terms); final String result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); - commandListener.ifPresent(it -> it.updateSynonymFinished(index.toString(), synonymGroupId, terms, result)); + commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); return result; } @Override public Map> dumpSynonym() { - commandListener.ifPresent(it -> it.dumpSynonymStarted(index.toString())); + commandListener.dumpSynonymStarted(index.toString()); final Map> result = search.ftSynDump(index.toString()); - commandListener.ifPresent(it -> it.dumpSynonymFinished(index.toString(), result)); + commandListener.dumpSynonymFinished(index.toString(), result); return result; } @Override public Set tagVals(String field) { - commandListener.ifPresent(it -> it.tagValsStarted(index.toString(), field)); + commandListener.tagValsStarted(index.toString(), field); final Set result = search.ftTagVals(index.toString(), field); - commandListener.ifPresent(it -> it.tagValsFinished(index.toString(), field, result)); + commandListener.tagValsFinished(index.toString(), field, result); return result; } From c5ce78fb36a5448f245eacda72fb6abc8eda57ee Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 27 Nov 2025 15:42:45 +0300 Subject: [PATCH 5/9] feat: add unit tests for CommandListener and implement TestCommandListener for tracking operations --- .../om/spring/ops/CommandListenerTest.java | 106 ++++++++++++ .../om/spring/ops/TestCommandListener.java | 154 ++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java create mode 100644 tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java diff --git a/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java b/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java new file mode 100644 index 00000000..a414ea77 --- /dev/null +++ b/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java @@ -0,0 +1,106 @@ +package com.redis.om.spring.ops; + +import com.redis.om.spring.AbstractBaseDocumentTest; +import com.redis.om.spring.ops.search.SearchOperations; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import redis.clients.jedis.search.*; +import redis.clients.jedis.search.aggr.AggregationBuilder; +import redis.clients.jedis.search.aggr.AggregationResult; +import redis.clients.jedis.search.schemafields.SchemaField; +import redis.clients.jedis.search.schemafields.TextField; +import java.util.List; +import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Slf4j +class CommandListenerTest extends AbstractBaseDocumentTest { + @Autowired + private RedisModulesOperations modulesOperations; + + @Autowired + private TestCommandListener testListener; + + @BeforeEach + void setUp() { + testListener.reset(); + } + + @ParameterizedTest + @ValueSource(strings = { "test-create-index", "another-create-index", "yet-another-create-index" }) + void givenIndexName_whenCreateAndDropIndex_thenAssertResult(final String indexName) { + // When + final SearchOperations searchOps = createIndex(indexName + "-" + UUID.randomUUID()); + // Then + final String result = searchOps.dropIndex(); + // Then + assertNotNull(result); + assertEquals(1, testListener.dropIndexStartedCount.get()); + assertEquals(1, testListener.dropIndexFinishedCount.get()); + assertEquals(indexName, testListener.lastDropIndexName); + assertEquals(result, testListener.lastDropIndexResult); + } + + @Test + void given_whenSearch_thenAssertResult() { + // Given + final String indexName = "test-search-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName + "-" + UUID.randomUUID()); + // When + final Query query = new Query("*"); + final SearchResult result = searchOps.search(query); + // Then + assertEquals(1, testListener.searchStartedCount.get()); + assertEquals(1, testListener.searchFinishedCount.get()); + assertEquals(indexName, testListener.lastSearchIndexName); + assertEquals(query, testListener.lastSearchQuery); + assertEquals(result, testListener.lastSearchResult); + searchOps.dropIndex(); + } + + @Test + void given_whenAggregate_thenAssertResult() { + // Given + final String indexName = "test-aggregate-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final AggregationBuilder aggBuilder = new AggregationBuilder("*"); + final AggregationResult aggResult = searchOps.aggregate(aggBuilder); + // Then + assertNotNull(aggResult); + assertEquals(1, testListener.aggregateStartedCount.get()); + assertEquals(1, testListener.aggregateFinishedCount.get()); + assertEquals(indexName, testListener.lastAggregateIndexName); + assertEquals(aggBuilder, testListener.lastAggregationBuilder); + } + + private SearchOperations createIndex(final String indexName) { + // Given + final SearchOperations searchOps = modulesOperations.opsForSearch(indexName); + try { + searchOps.dropIndex(); + } catch (Exception e) { + log.warn("Index did not exist prior to test setup: {}", e.getMessage()); + } + final FTCreateParams params = FTCreateParams.createParams() + .on(IndexDataType.HASH) + .prefix(indexName + ":"); + final List fields = List.of( TextField.of("content")); + // When + final String result = searchOps.createIndex(params, fields); + // Then + assertThat(testListener.createIndexStartedCount.get()).isEqualTo(1); + assertThat(testListener.createIndexFinishedCount.get()).isEqualTo(1); + assertThat(testListener.lastCreateIndexName).isEqualTo(indexName); + assertThat(testListener.lastCreateParams).isEqualTo(params); + assertThat(testListener.lastCreateFields).isEqualTo(fields); + assertThat(testListener.lastCreateResult).isEqualTo(result); + return searchOps; + } +} diff --git a/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java new file mode 100644 index 00000000..a76b9ebc --- /dev/null +++ b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java @@ -0,0 +1,154 @@ +package com.redis.om.spring.ops; + +import org.springframework.boot.test.context.TestComponent; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import redis.clients.jedis.search.FTCreateParams; +import redis.clients.jedis.search.FTSearchParams; +import redis.clients.jedis.search.IndexOptions; +import redis.clients.jedis.search.Query; +import redis.clients.jedis.search.Schema; +import redis.clients.jedis.search.SearchResult; +import redis.clients.jedis.search.aggr.AggregationBuilder; +import redis.clients.jedis.search.schemafields.SchemaField; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** +* Test implementation of CommandListener that tracks all invocations +*/ +@Component +@Primary +class TestCommandListener implements CommandListener { + // Search tracking + AtomicInteger searchStartedCount = new AtomicInteger(0); + AtomicInteger searchFinishedCount = new AtomicInteger(0); + String lastSearchIndexName; + Query lastSearchQuery; + FTSearchParams lastSearchParams; + SearchResult lastSearchResult; + + // Create index tracking + AtomicInteger createIndexStartedCount = new AtomicInteger(0); + AtomicInteger createIndexFinishedCount = new AtomicInteger(0); + String lastCreateIndexName; + FTCreateParams lastCreateParams; + List lastCreateFields; + Schema lastCreateSchema; + IndexOptions lastCreateOptions; + String lastCreateResult; + + // Aggregate tracking + AtomicInteger aggregateStartedCount = new AtomicInteger(0); + AtomicInteger aggregateFinishedCount = new AtomicInteger(0); + String lastAggregateIndexName; + AggregationBuilder lastAggregationBuilder; + + // Drop index tracking + AtomicInteger dropIndexStartedCount = new AtomicInteger(0); + AtomicInteger dropIndexFinishedCount = new AtomicInteger(0); + String lastDropIndexName; + String lastDropIndexResult; + + // Info tracking + AtomicInteger infoStartedCount = new AtomicInteger(0); + AtomicInteger infoFinishedCount = new AtomicInteger(0); + + @Override + public void searchStarted(String indexName, Query q, FTSearchParams params) { + searchStartedCount.incrementAndGet(); + lastSearchIndexName = indexName; + lastSearchQuery = q; + lastSearchParams = params; + } + + @Override + public void searchFinished(String indexName, Query q, FTSearchParams params, SearchResult searchResult) { + searchFinishedCount.incrementAndGet(); + lastSearchResult = searchResult; + } + + @Override + public void createIndexStarted(String indexName, FTCreateParams params, List fields, + Schema schema, IndexOptions options) { + createIndexStartedCount.incrementAndGet(); + lastCreateIndexName = indexName; + lastCreateParams = params; + lastCreateFields = fields; + lastCreateSchema = schema; + lastCreateOptions = options; + } + + @Override + public void createIndexFinished(String indexName, FTCreateParams params, List fields, + Schema schema, IndexOptions options, String result) { + createIndexFinishedCount.incrementAndGet(); + lastCreateResult = result; + } + + @Override + public void aggregateStarted(String indexName, AggregationBuilder q) { + aggregateStartedCount.incrementAndGet(); + lastAggregateIndexName = indexName; + lastAggregationBuilder = q; + } + + @Override + public void aggregateFinished(String indexName, AggregationBuilder q) { + aggregateFinishedCount.incrementAndGet(); + } + + @Override + public void dropIndexStarted(String indexName) { + dropIndexStartedCount.incrementAndGet(); + lastDropIndexName = indexName; + } + + @Override + public void dropIndexFinished(String indexName, String result) { + dropIndexFinishedCount.incrementAndGet(); + lastDropIndexResult = result; + } + + @Override + public void infoStarted(String indexName) { + infoStartedCount.incrementAndGet(); + } + + @Override + public void infoFinished(String indexName, Map info) { + infoFinishedCount.incrementAndGet(); + } + + /** + * Reset all counters and tracked values + */ + void reset() { + searchStartedCount.set(0); + searchFinishedCount.set(0); + createIndexStartedCount.set(0); + createIndexFinishedCount.set(0); + aggregateStartedCount.set(0); + aggregateFinishedCount.set(0); + dropIndexStartedCount.set(0); + dropIndexFinishedCount.set(0); + infoStartedCount.set(0); + infoFinishedCount.set(0); + lastSearchIndexName = null; + lastSearchQuery = null; + lastSearchParams = null; + lastSearchResult = null; + lastCreateIndexName = null; + lastCreateParams = null; + lastCreateFields = null; + lastCreateSchema = null; + lastCreateOptions = null; + lastCreateResult = null; + lastAggregateIndexName = null; + lastAggregationBuilder = null; + lastDropIndexName = null; + lastDropIndexResult = null; + } + } \ No newline at end of file From bc594ec0157af656f4f536e03a105ab6e565e031 Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 27 Nov 2025 23:09:04 +0300 Subject: [PATCH 6/9] feat: enhance CommandListener with additional parameters for aggregateFinished and createIndex methods and write more tests to cover the listeners. Use try-catch-finally method for exception handling --- .../redis/om/spring/ops/CommandListener.java | 2 +- .../ops/search/SearchOperationsImpl.java | 322 +++++++++++++----- .../om/spring/ops/CommandListenerTest.java | 194 ++++++++++- .../om/spring/ops/TestCommandListener.java | 193 +++++++++-- 4 files changed, 585 insertions(+), 126 deletions(-) diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java index ecc96fcc..754371ac 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java @@ -24,7 +24,7 @@ default void createIndexStarted(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options, String result){} default void aggregateStarted(String indexName, AggregationBuilder q){} - default void aggregateFinished(String indexName, AggregationBuilder q){} + default void aggregateFinished(String indexName, AggregationBuilder q, AggregationResult result){} default void cursorDeleteStarted(String string, long cursorId){} default void cursorDeleteFinished(String string, long cursorId, String result){} 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 289cbcf4..5f9a47dd 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 @@ -61,89 +61,149 @@ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRed @Override public String createIndex(Schema schema, IndexOptions options) { commandListener.createIndexStarted(index.toString(), null, null, schema, options); - final String s = search.ftCreate(index.toString(), options, schema); - commandListener.createIndexFinished(index.toString(), null, null, schema, options, s); - return s; + String result = null; + try { + result = search.ftCreate(index.toString(), options, schema); + } catch (Exception e) { + throw e; + }finally { + commandListener.createIndexFinished(index.toString(), null, null, schema, options, result); + } + return result; } @Override public String createIndex(FTCreateParams params, List fields) { commandListener.createIndexStarted(index.toString(), params, fields, null, null); - final String s = search.ftCreate(index.toString(), params, fields); - commandListener.createIndexFinished(index.toString(), params, fields, null, null, s); - return s; + String result = null; + try { + result = search.ftCreate(index.toString(), params, fields); + } catch (Exception e) { + throw e; + } finally { + commandListener.createIndexFinished(index.toString(), params, fields, null, null, result); + } + return result; } @Override @Deprecated public SearchResult search(Query q) { commandListener.searchStarted(index.toString(), q, null); - final SearchResult searchResult = search.ftSearch(SafeEncoder.encode(index.toString()), q); - commandListener.searchFinished(index.toString(), q, null, searchResult); - return searchResult; + SearchResult result = null; + try { + result = search.ftSearch(SafeEncoder.encode(index.toString()), q); + } catch (Exception e) { + throw e; + } finally { + commandListener.searchFinished(index.toString(), q, null, result); + } + return result; } @Override public SearchResult search(Query q, FTSearchParams params) { commandListener.searchStarted(index.toString(), q, null); - final SearchResult searchResult = search.ftSearch(index.toString(), q.toString(), params); - commandListener.searchFinished(index.toString(), q, null, searchResult); - return searchResult; + final SearchResult result = search.ftSearch(index.toString(), q.toString(), params); + commandListener.searchFinished(index.toString(), q, null, result); + return result; } @Override public AggregationResult aggregate(AggregationBuilder q) { commandListener.aggregateStarted(index.toString(), q); - final AggregationResult aggregationResult = search.ftAggregate(index.toString(), q); - commandListener.aggregateFinished(index.toString(), q); - return aggregationResult; + AggregationResult result = null; + try { + result = search.ftAggregate(index.toString(), q); + } catch (Exception e) { + throw e; + } finally { + commandListener.aggregateFinished(index.toString(), q, result); + } + return result; } @Override public String cursorDelete(long cursorId) { commandListener.cursorDeleteStarted(index.toString(), cursorId); - final String result = search.ftCursorDel(index.toString(), cursorId); - commandListener.cursorDeleteFinished(index.toString(), cursorId, result); + String result = null; + try { + result = search.ftCursorDel(index.toString(), cursorId); + } catch (Exception e) { + throw e; + } finally { + commandListener.cursorDeleteFinished(index.toString(), cursorId, result); + } return result; } @Override public AggregationResult cursorRead(long cursorId, int count) { commandListener.cursorReadStarted(index.toString(), cursorId, count); - final AggregationResult aggregationResult = search.ftCursorRead(index.toString(), cursorId, count); - commandListener.cursorReadFinished(index.toString(), cursorId, count, aggregationResult); - return aggregationResult; + AggregationResult result = null; + try { + result = search.ftCursorRead(index.toString(), cursorId, count); + } catch (Exception e) { + throw e; + } finally { + commandListener.cursorReadFinished(index.toString(), cursorId, count, result); + } + return result; } @Override public String explain(Query q) { commandListener.explainStarted(index.toString(), q); - final String s = search.ftExplain(index.toString(), q); - commandListener.explainFinished(index.toString(), q, s); - return s; + String result = null; + try { + result = search.ftExplain(index.toString(), q); + } catch (Exception e) { + throw e; + } finally { + commandListener.explainFinished(index.toString(), q, result); + } + return result; } @Override public Map getInfo() { commandListener.infoStarted(index.toString()); - final Map result = search.ftInfo(index.toString()); - commandListener.infoFinished(index.toString(), result); + Map result = Map.of(); + try { + result = search.ftInfo(index.toString()); + } catch (Exception e) { + throw e; + } finally { + commandListener.infoFinished(index.toString(), result); + } return result; } @Override public String dropIndex() { commandListener.dropIndexStarted(index.toString()); - final String result = search.ftDropIndex(index.toString()); - commandListener.dropIndexFinished(index.toString(), result); + String result = null; + try { + result = search.ftDropIndex(index.toString()); + } catch (Exception e) { + throw e; + } finally { + commandListener.dropIndexFinished(index.toString(), result); + } return result; } @Override public String dropIndexAndDocuments() { commandListener.dropIndexAndDocumentsStarted(index.toString()); - final String result = search.ftDropIndexDD(index.toString()); - commandListener.dropIndexAndDocumentsFinished(index.toString(), result); + String result = null; + try { + result = search.ftDropIndexDD(index.toString()); + } catch (Exception e) { + throw e; + } finally { + commandListener.dropIndexAndDocumentsFinished(index.toString(), result); + } return result; } @@ -155,8 +215,14 @@ public Long addSuggestion(String key, String suggestion) { @Override public Long addSuggestion(String key, String suggestion, double score) { commandListener.addSuggestionStarted(index.toString(), key, suggestion, score); - final long result = search.ftSugAdd(key, suggestion, score); - commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); + long result = 0; + try { + result = search.ftSugAdd(key, suggestion, score); + } catch (Exception e) { + throw e; + } finally { + commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); + } return result; } @@ -172,136 +238,220 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt if (options.isWithScore()) { List suggestions = search.ftSugGetWithScores(key, prefix, options.isFuzzy(), options.getLimit()); - List list = suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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.getElement()); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); - } else { - return new Suggestion(suggestion.getElement(), suggestion.getScore()); - } - }).toList(); - commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); + List list = List.of(); + try { + list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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.getElement()); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); + } else { + return new Suggestion(suggestion.getElement(), suggestion.getScore()); + } + }).toList(); + } catch (Exception e) { + throw e; + } finally { + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); + } return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); - List list = suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion, payloadMap); - } else { - return new Suggestion(suggestion); - } - }).toList(); - commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); - return list; + List list = List.of(); + try { + list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion, payloadMap); + } else { + return new Suggestion(suggestion); + } + }).toList(); + } catch (Exception e) { + throw e; + }finally { + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); + } + return list; } } @Override public Boolean deleteSuggestion(String key, String entry) { commandListener.deleteSuggestionStarted(index.toString(), key, entry); - final boolean result = search.ftSugDel(key, entry); - commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); + boolean result = false; + try { + result = search.ftSugDel(key, entry); + } catch (Exception e) { + throw e; + } finally { + commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); + } return result; } @Override public Long getSuggestionLength(String key) { commandListener.getSuggestionLengthStarted(index.toString(), key); - final long result = search.ftSugLen(key); - commandListener.getSuggestionLengthFinished(index.toString(), key, result); + long result = 0; + try { + result = search.ftSugLen(key); + } catch (Exception e) { + throw e; + } finally { + commandListener.getSuggestionLengthFinished(index.toString(), key, result); + } return result; } @Override public String alterIndex(SchemaField... fields) { commandListener.alterIndexStarted(index.toString(), fields); - final String result = search.ftAlter(index.toString(), fields); - commandListener.alterIndexFinished(index.toString(), fields, result); + String result = null; + try { + result = search.ftAlter(index.toString(), fields); + } catch (Exception e) { + throw e; + } finally { + commandListener.alterIndexFinished(index.toString(), fields, result); + } return result; } @Override public String setConfig(String option, String value) { commandListener.setConfigStarted(index.toString(), option, value); - final String result = search.ftConfigSet(option, value); - commandListener.setConfigFinished(index.toString(), option, value, result); + String result = null; + try { + result = search.ftConfigSet(option, value); + } catch (Exception e) { + throw e; + } finally { + commandListener.setConfigFinished(index.toString(), option, value, result); + } return result; } @Override public Map getConfig(String option) { commandListener.getConfigStarted(index.toString(), option); - final Map result = search.ftConfigGet(option); - commandListener.getConfigFinished(index.toString(), option, result); + Map result = Map.of(); + try { + result = search.ftConfigGet(option); + } catch (Exception e) { + throw e; + } finally { + commandListener.getConfigFinished(index.toString(), option, result); + } return result; } @Override public Map getIndexConfig(String option) { commandListener.getIndexConfigStarted(index.toString(), option); - final Map result = search.ftConfigGet(index.toString(), option); - commandListener.getIndexConfigFinished(index.toString(), option, result); + Map result = Map.of(); + try { + result = search.ftConfigGet(index.toString(), option); + } catch (Exception e) { + throw e; + } finally { + commandListener.getIndexConfigFinished(index.toString(), option, result); + } return result; } @Override public String addAlias(String name) { commandListener.addAliasStarted(index.toString(), name); - final String result = search.ftAliasAdd(name, index.toString()); - commandListener.addAliasFinished(index.toString(), name, result); + String result = null; + try { + result = search.ftAliasAdd(name, index.toString()); + } catch (Exception e) { + throw e; + } finally { + commandListener.addAliasFinished(index.toString(), name, result); + } return result; } @Override public String updateAlias(String name) { commandListener.updateAliasStarted(index.toString(), name); - final String result = search.ftAliasUpdate(name, index.toString()); - commandListener.updateAliasFinished(index.toString(), name, result); + String result = null; + try { + result = search.ftAliasUpdate(name, index.toString()); + } catch (Exception e) { + throw e; + }finally { + commandListener.updateAliasFinished(index.toString(), name, result); + } return result; } @Override public String deleteAlias(String name) { commandListener.deleteAliasStarted(index.toString(), name); - final String result = search.ftAliasDel(name); - commandListener.deleteAliasFinished(index.toString(), name, result); + String result = null; + try { + result = search.ftAliasDel(name); + } catch (Exception e) { + throw e; + } finally { + commandListener.deleteAliasFinished(index.toString(), name, result); + } return result; } @Override public String updateSynonym(String synonymGroupId, String... terms) { commandListener.updateSynonymStarted(index.toString(), synonymGroupId, terms); - final String result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); - commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); + String result = null; + try { + result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); + } catch (Exception e) { + throw e; + } finally { + commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); + } return result; } @Override public Map> dumpSynonym() { commandListener.dumpSynonymStarted(index.toString()); - final Map> result = search.ftSynDump(index.toString()); - commandListener.dumpSynonymFinished(index.toString(), result); + Map> result = Map.of(); + try { + result = search.ftSynDump(index.toString()); + } catch (Exception e) { + throw e; + } finally { + commandListener.dumpSynonymFinished(index.toString(), result); + } return result; } @Override public Set tagVals(String field) { commandListener.tagValsStarted(index.toString(), field); - final Set result = search.ftTagVals(index.toString(), field); - commandListener.tagValsFinished(index.toString(), field, result); + Set result = Set.of(); + try { + result = search.ftTagVals(index.toString(), field); + } catch (Exception e) { + throw e; + }finally { + commandListener.tagValsFinished(index.toString(), field, result); + } return result; } diff --git a/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java b/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java index a414ea77..c4001c96 100644 --- a/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java +++ b/tests/src/test/java/com/redis/om/spring/ops/CommandListenerTest.java @@ -8,15 +8,20 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import redis.clients.jedis.search.*; +import redis.clients.jedis.search.FTCreateParams; +import redis.clients.jedis.search.IndexDataType; +import redis.clients.jedis.search.Query; +import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.search.schemafields.TextField; import java.util.List; +import java.util.Map; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @Slf4j @@ -35,15 +40,17 @@ void setUp() { @ParameterizedTest @ValueSource(strings = { "test-create-index", "another-create-index", "yet-another-create-index" }) void givenIndexName_whenCreateAndDropIndex_thenAssertResult(final String indexName) { + // Given + final String idx = indexName + "-" + UUID.randomUUID(); // When - final SearchOperations searchOps = createIndex(indexName + "-" + UUID.randomUUID()); + final SearchOperations searchOps = createIndex(idx); // Then final String result = searchOps.dropIndex(); // Then assertNotNull(result); - assertEquals(1, testListener.dropIndexStartedCount.get()); - assertEquals(1, testListener.dropIndexFinishedCount.get()); - assertEquals(indexName, testListener.lastDropIndexName); + assertEquals(2, testListener.dropIndexStartedCount.get()); + assertEquals(2, testListener.dropIndexFinishedCount.get()); + assertEquals(idx, testListener.lastDropIndexName); assertEquals(result, testListener.lastDropIndexResult); } @@ -51,9 +58,16 @@ void givenIndexName_whenCreateAndDropIndex_thenAssertResult(final String indexNa void given_whenSearch_thenAssertResult() { // Given final String indexName = "test-search-index-" + UUID.randomUUID(); - final SearchOperations searchOps = createIndex(indexName + "-" + UUID.randomUUID()); + final SearchOperations searchOps = createIndex(indexName); + // Insert test data + final String doc1Key = indexName + ":doc1"; + final String doc2Key = indexName + ":doc2"; + final String doc3Key = indexName + ":doc3"; + template.opsForHash().put(doc1Key, "content", "The quick brown fox"); + template.opsForHash().put(doc2Key, "content", "jumps over the lazy dog"); + template.opsForHash().put(doc3Key, "content", "Redis search is powerful"); // When - final Query query = new Query("*"); + final Query query = new Query("fox"); final SearchResult result = searchOps.search(query); // Then assertEquals(1, testListener.searchStartedCount.get()); @@ -61,7 +75,14 @@ void given_whenSearch_thenAssertResult() { assertEquals(indexName, testListener.lastSearchIndexName); assertEquals(query, testListener.lastSearchQuery); assertEquals(result, testListener.lastSearchResult); - searchOps.dropIndex(); + assertEquals(1, result.getTotalResults()); + assertNotNull(result.getDocuments()); + assertEquals(1, result.getDocuments().size()); + assertEquals(doc1Key, result.getDocuments().getFirst().getId()); + assertEquals("The quick brown fox", result.getDocuments().getFirst().getString("content")); + template.delete(doc1Key); + template.delete(doc2Key); + template.delete(doc3Key); } @Test @@ -69,15 +90,172 @@ void given_whenAggregate_thenAssertResult() { // Given final String indexName = "test-aggregate-index-" + UUID.randomUUID(); final SearchOperations searchOps = createIndex(indexName); + // Insert test data for aggregation + template.opsForHash().put(indexName + ":doc1", "content", "Java programming"); + template.opsForHash().put(indexName + ":doc2", "content", "Python programming"); + template.opsForHash().put(indexName + ":doc3", "content", "Redis database"); // When final AggregationBuilder aggBuilder = new AggregationBuilder("*"); final AggregationResult aggResult = searchOps.aggregate(aggBuilder); // Then assertNotNull(aggResult); + assertEquals(1, aggResult.getTotalResults()); assertEquals(1, testListener.aggregateStartedCount.get()); assertEquals(1, testListener.aggregateFinishedCount.get()); assertEquals(indexName, testListener.lastAggregateIndexName); assertEquals(aggBuilder, testListener.lastAggregationBuilder); + template.delete(indexName + ":doc1"); + template.delete(indexName + ":doc2"); + template.delete(indexName + ":doc3"); + } + + @Test + void given_whenGetInfo_thenAssertResult() { + // Given + final String indexName = "test-info-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final Map infoResult = searchOps.getInfo(); + // Then + assertNotNull(infoResult); + assertThat(infoResult).isNotEmpty(); + assertEquals(1, testListener.infoStartedCount.get()); + assertEquals(1, testListener.infoFinishedCount.get()); + } + + @Test + void given_whenExplainQuery_thenAssertResult() { + // Given + final String indexName = "test-explain-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final Query query = new Query("*"); + final String explanation = searchOps.explain(query); + // Then + assertNotNull(explanation); + assertEquals("\n", explanation); + } + + @Test + void given_whenAlterIndex_thenAssertResult() { + // Given + final String indexName = "test-alter-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final SchemaField newField = TextField.of("newfield"); + final String result = searchOps.alterIndex(newField); + // Then + assertEquals("OK", result); + assertEquals(1, testListener.alterIndexStartedCount.get()); + assertEquals(1, testListener.alterIndexFinishedCount.get()); + } + + @Test + void given_whenDropIndexAndDocuments_thenAssertResult() { + // Given + final String indexName = "test-drop-docs-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // Insert test data + final String doc1Key = indexName + ":doc1"; + final String doc2Key = indexName + ":doc2"; + final String doc3Key = indexName + ":doc3"; + template.opsForHash().put(doc1Key, "content", "The quick brown fox"); + template.opsForHash().put(doc2Key, "content", "jumps over the lazy dog"); + template.opsForHash().put(doc3Key, "content", "Redis search is powerful"); + // When + final String result = searchOps.dropIndexAndDocuments(); + // Then + assertEquals("OK", result); + assertEquals(1, testListener.dropIndexAndDocumentsStartedCount.get()); + assertEquals(1, testListener.dropIndexAndDocumentsFinishedCount.get()); + template.delete(doc1Key); + template.delete(doc2Key); + template.delete(doc3Key); + assertEquals(0, template.opsForHash().keys(doc1Key).size()); + assertEquals(0, template.opsForHash().keys(doc2Key).size()); + assertEquals(0, template.opsForHash().keys(doc3Key).size()); + } + + @Test + void given_whenAddUpdateDeleteAlias_thenAssertResult() { + // Given + final String indexName = "test-add-alias-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // Insert test data + final String doc1Key = indexName + ":doc1"; + final String doc2Key = indexName + ":doc2"; + final String doc3Key = indexName + ":doc3"; + template.opsForHash().put(doc1Key, "content", "The quick brown fox"); + template.opsForHash().put(doc2Key, "content", "jumps over the lazy dog"); + template.opsForHash().put(doc3Key, "content", "Redis search is powerful"); + final String newIndexName = UUID.randomUUID().toString(); + // When addAlias + String result = searchOps.addAlias(newIndexName); + // Then addAlias + assertEquals("OK", result); + assertEquals(1, testListener.addAliasStartedCount.get()); + assertEquals(1, testListener.addAliasFinishedCount.get()); + // When updateAlias + result = searchOps.updateAlias(newIndexName); + // Then updateAlias + assertEquals("OK", result); + assertEquals(1, testListener.updateAliasStartedCount.get()); + assertEquals(1, testListener.updateAliasFinishedCount.get()); + // When deleteAlias + result = searchOps.deleteAlias(newIndexName); + // Then deleteAlias + assertEquals("OK", result); + assertEquals(1, testListener.deleteAliasStartedCount.get()); + assertEquals(1, testListener.deleteAliasFinishedCount.get()); + template.delete(doc1Key); + template.delete(doc2Key); + template.delete(doc3Key); + assertEquals(0, template.opsForHash().keys(doc1Key).size()); + assertEquals(0, template.opsForHash().keys(doc2Key).size()); + assertEquals(0, template.opsForHash().keys(doc3Key).size()); + } + + @Test + void given_whenGetConfig_thenAssertResult() { + // Given + final String indexName = "test-get-config-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final Map result = searchOps.getConfig("*"); + // Then + assertNotNull(result); + assertFalse(result.isEmpty()); + assertEquals(1, testListener.getConfigStartedCount.get()); + assertEquals(1, testListener.getConfigFinishedCount.get()); + } + + @Test + void given_whenGetIndexConfig_thenAssertResult() { + // Given + final String indexName = "test-get-config-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final Map result = searchOps.getIndexConfig("*"); + // Then + assertNotNull(result); + assertFalse(result.isEmpty()); + assertEquals(1, testListener.getIndexConfigStartedCount.get()); + assertEquals(1, testListener.getIndexConfigFinishedCount.get()); + } + + @Test + void given_whenSetConfig_thenAssertResult() { + // Given + final String indexName = "test-get-config-index-" + UUID.randomUUID(); + final SearchOperations searchOps = createIndex(indexName); + // When + final String result = searchOps.setConfig("TIMEOUT", "42"); + // Then + assertEquals("OK", result); + assertEquals(1, testListener.setConfigStartedCount.get()); + assertEquals(1, testListener.setConfigFinishedCount.get()); + final Map getTimeout = searchOps.getConfig("TIMEOUT"); + assertEquals("42", getTimeout.get("TIMEOUT").toString()); } private SearchOperations createIndex(final String indexName) { diff --git a/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java index a76b9ebc..a6d862e0 100644 --- a/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java +++ b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java @@ -10,6 +10,7 @@ import redis.clients.jedis.search.Schema; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.aggr.AggregationBuilder; +import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.schemafields.SchemaField; import java.util.List; @@ -49,6 +50,8 @@ class TestCommandListener implements CommandListener { // Drop index tracking AtomicInteger dropIndexStartedCount = new AtomicInteger(0); AtomicInteger dropIndexFinishedCount = new AtomicInteger(0); + AtomicInteger dropIndexAndDocumentsStartedCount = new AtomicInteger(0); + AtomicInteger dropIndexAndDocumentsFinishedCount = new AtomicInteger(0); String lastDropIndexName; String lastDropIndexResult; @@ -56,6 +59,34 @@ class TestCommandListener implements CommandListener { AtomicInteger infoStartedCount = new AtomicInteger(0); AtomicInteger infoFinishedCount = new AtomicInteger(0); + // Alter index tracking + AtomicInteger alterIndexStartedCount = new AtomicInteger(0); + AtomicInteger alterIndexFinishedCount = new AtomicInteger(0); + + // Set config tracking + AtomicInteger setConfigStartedCount = new AtomicInteger(0); + AtomicInteger setConfigFinishedCount = new AtomicInteger(0); + + // Get config tracking + AtomicInteger getConfigStartedCount = new AtomicInteger(0); + AtomicInteger getConfigFinishedCount = new AtomicInteger(0); + + // Get index config tracking + AtomicInteger getIndexConfigStartedCount = new AtomicInteger(0); + AtomicInteger getIndexConfigFinishedCount = new AtomicInteger(0); + + // Add alias tracking + AtomicInteger addAliasStartedCount = new AtomicInteger(0); + AtomicInteger addAliasFinishedCount = new AtomicInteger(0); + + // Update alias tracking + AtomicInteger updateAliasStartedCount = new AtomicInteger(0); + AtomicInteger updateAliasFinishedCount = new AtomicInteger(0); + + // Delete alias tracking + AtomicInteger deleteAliasStartedCount = new AtomicInteger(0); + AtomicInteger deleteAliasFinishedCount = new AtomicInteger(0); + @Override public void searchStarted(String indexName, Query q, FTSearchParams params) { searchStartedCount.incrementAndGet(); @@ -96,10 +127,20 @@ public void aggregateStarted(String indexName, AggregationBuilder q) { } @Override - public void aggregateFinished(String indexName, AggregationBuilder q) { + public void aggregateFinished(final String indexName, final AggregationBuilder q, final AggregationResult aggregationResult) { aggregateFinishedCount.incrementAndGet(); } + @Override + public void infoStarted(String indexName) { + infoStartedCount.incrementAndGet(); + } + + @Override + public void infoFinished(String indexName, Map info) { + infoFinishedCount.incrementAndGet(); + } + @Override public void dropIndexStarted(String indexName) { dropIndexStartedCount.incrementAndGet(); @@ -113,42 +154,132 @@ public void dropIndexFinished(String indexName, String result) { } @Override - public void infoStarted(String indexName) { - infoStartedCount.incrementAndGet(); + public void dropIndexAndDocumentsStarted(String string) { + dropIndexAndDocumentsStartedCount.incrementAndGet(); } @Override - public void infoFinished(String indexName, Map info) { - infoFinishedCount.incrementAndGet(); + public void dropIndexAndDocumentsFinished(String string, String result) { + dropIndexAndDocumentsFinishedCount.incrementAndGet(); + lastDropIndexResult = result; + } + + @Override + public void alterIndexStarted(String string, SchemaField[] fields) { + alterIndexStartedCount.incrementAndGet(); + } + + @Override + public void alterIndexFinished(String string, SchemaField[] fields, String result) { + alterIndexFinishedCount.incrementAndGet(); + } + + @Override + public void setConfigStarted(String string, String option, String value) { + setConfigStartedCount.incrementAndGet(); + } + + @Override + public void setConfigFinished(String string, String option, String value, String result) { + setConfigFinishedCount.incrementAndGet(); + } + + @Override + public void getConfigStarted(String string, String option) { + getConfigStartedCount.incrementAndGet(); + } + + @Override + public void getConfigFinished(String string, String option, Map result) { + getConfigFinishedCount.incrementAndGet(); + } + + @Override + public void getIndexConfigStarted(String string, String option) { + getIndexConfigStartedCount.incrementAndGet(); + } + + @Override + public void getIndexConfigFinished(String string, String option, Map result) { + getIndexConfigFinishedCount.incrementAndGet(); + } + + @Override + public void addAliasStarted(String string, String name) { + addAliasStartedCount.incrementAndGet(); + } + + @Override + public void addAliasFinished(String string, String name, String result) { + addAliasFinishedCount.incrementAndGet(); + lastDropIndexResult = result; + } + + @Override + public void updateAliasStarted(String string, String name) { + updateAliasStartedCount.incrementAndGet(); + } + + @Override + public void updateAliasFinished(String string, String name, String result) { + updateAliasFinishedCount.incrementAndGet(); + lastDropIndexResult = result; + } + + @Override + public void deleteAliasStarted(String string, String name) { + deleteAliasStartedCount.incrementAndGet(); + } + + @Override + public void deleteAliasFinished(String string, String name, String result) { + deleteAliasFinishedCount.incrementAndGet(); + lastDropIndexResult = result; } /** - * Reset all counters and tracked values - */ + * Reset all counters and tracked values + */ void reset() { - searchStartedCount.set(0); - searchFinishedCount.set(0); - createIndexStartedCount.set(0); - createIndexFinishedCount.set(0); - aggregateStartedCount.set(0); - aggregateFinishedCount.set(0); - dropIndexStartedCount.set(0); - dropIndexFinishedCount.set(0); - infoStartedCount.set(0); - infoFinishedCount.set(0); - lastSearchIndexName = null; - lastSearchQuery = null; - lastSearchParams = null; - lastSearchResult = null; - lastCreateIndexName = null; - lastCreateParams = null; - lastCreateFields = null; - lastCreateSchema = null; - lastCreateOptions = null; - lastCreateResult = null; - lastAggregateIndexName = null; - lastAggregationBuilder = null; - lastDropIndexName = null; - lastDropIndexResult = null; + searchStartedCount.set(0); + searchFinishedCount.set(0); + createIndexStartedCount.set(0); + createIndexFinishedCount.set(0); + aggregateStartedCount.set(0); + aggregateFinishedCount.set(0); + dropIndexStartedCount.set(0); + dropIndexFinishedCount.set(0); + infoStartedCount.set(0); + infoFinishedCount.set(0); + alterIndexStartedCount.set(0); + alterIndexFinishedCount.set(0); + setConfigStartedCount.set(0); + setConfigFinishedCount.set(0); + getConfigStartedCount.set(0); + getConfigFinishedCount.set(0); + getIndexConfigStartedCount.set(0); + getIndexConfigFinishedCount.set(0); + alterIndexStartedCount.set(0); + alterIndexStartedCount.set(0); + addAliasStartedCount.set(0); + addAliasFinishedCount.set(0); + updateAliasStartedCount.set(0); + updateAliasFinishedCount.set(0); + deleteAliasStartedCount.set(0); + deleteAliasFinishedCount.set(0); + lastSearchIndexName = null; + lastSearchQuery = null; + lastSearchParams = null; + lastSearchResult = null; + lastCreateIndexName = null; + lastCreateParams = null; + lastCreateFields = null; + lastCreateSchema = null; + lastCreateOptions = null; + lastCreateResult = null; + lastAggregateIndexName = null; + lastAggregationBuilder = null; + lastDropIndexName = null; + lastDropIndexResult = null; } } \ No newline at end of file From 56250b154afbdef95c1bc381b863016deae21d9b Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Thu, 27 Nov 2025 23:17:44 +0300 Subject: [PATCH 7/9] refactor: remove unused TestComponent import from TestCommandListener --- .../test/java/com/redis/om/spring/ops/TestCommandListener.java | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java index a6d862e0..32666efb 100644 --- a/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java +++ b/tests/src/test/java/com/redis/om/spring/ops/TestCommandListener.java @@ -1,6 +1,5 @@ package com.redis.om.spring.ops; -import org.springframework.boot.test.context.TestComponent; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import redis.clients.jedis.search.FTCreateParams; From 2d8caf79eb5595a065a6e2356b80ad3c5abd7fba Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Fri, 28 Nov 2025 07:16:10 +0300 Subject: [PATCH 8/9] refactor: run :spotlessApply to verify the build --- .../om/spring/RedisModulesConfiguration.java | 43 ++- .../redis/om/spring/ops/CommandListener.java | 175 +++++++--- .../om/spring/ops/NoOpCommandListener.java | 2 +- .../om/spring/ops/RedisModulesOperations.java | 8 +- .../ops/search/SearchOperationsImpl.java | 325 +++++++++--------- 5 files changed, 314 insertions(+), 239 deletions(-) diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java index edc522e2..16b63cf3 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/RedisModulesConfiguration.java @@ -8,8 +8,6 @@ import java.util.List; import java.util.Set; -import com.redis.om.spring.ops.CommandListener; -import com.redis.om.spring.ops.NoOpCommandListener; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -44,6 +42,8 @@ import com.redis.om.spring.client.RedisModulesClient; import com.redis.om.spring.indexing.RediSearchIndexer; import com.redis.om.spring.mapping.RedisEnhancedMappingContext; +import com.redis.om.spring.ops.CommandListener; +import com.redis.om.spring.ops.NoOpCommandListener; import com.redis.om.spring.ops.RedisModulesOperations; import com.redis.om.spring.ops.json.JSONOperations; import com.redis.om.spring.ops.pds.BloomOperations; @@ -201,9 +201,9 @@ RedisModulesClient redisModulesClient( // * including JSON, Search, Bloom filters, and other probabilistic data structures. * It serves as the central operations hub for Redis OM Spring functionality. * - * @param rmc the Redis modules client for low-level access - * @param template the string Redis template for basic operations - * @param gsonBuilder the Gson builder for JSON serialization + * @param rmc the Redis modules client for low-level access + * @param template the string Redis template for basic operations + * @param gsonBuilder the Gson builder for JSON serialization * @param commandListener a command listener for monitoring Redis commands * @return the Redis modules operations instance */ @@ -222,23 +222,22 @@ RedisModulesOperations redisModulesOperations( // return new RedisModulesOperations<>(rmc, template, gsonBuilder, commandListener); } - /** - * Provides a default implementation of the CommandListener bean. - *

- * This method creates a no-operation (NoOp) implementation of the CommandListener interface. - * It is used as a fallback when no other CommandListener bean is defined in the application context. - *

- * The {@code @Fallback} annotation ensures that this bean is only used when no other - * CommandListener bean is available, allowing developers to override it with a custom implementation if needed. - * - * @return a NoOpCommandListener instance, which performs no operations. - */ - @Bean - @Fallback - public CommandListener commandListener() { - return new NoOpCommandListener(); - } - + /** + * Provides a default implementation of the CommandListener bean. + *

+ * This method creates a no-operation (NoOp) implementation of the CommandListener interface. + * It is used as a fallback when no other CommandListener bean is defined in the application context. + *

+ * The {@code @Fallback} annotation ensures that this bean is only used when no other + * CommandListener bean is available, allowing developers to override it with a custom implementation if needed. + * + * @return a NoOpCommandListener instance, which performs no operations. + */ + @Bean + @Fallback + public CommandListener commandListener() { + return new NoOpCommandListener(); + } /** * Creates the JSON operations bean for RedisJSON commands. diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java index 754371ac..60d41aed 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/CommandListener.java @@ -1,7 +1,12 @@ package com.redis.om.spring.ops; +import java.util.List; +import java.util.Map; +import java.util.Set; + import com.redis.om.spring.autocomplete.Suggestion; import com.redis.om.spring.repository.query.autocomplete.AutoCompleteOptions; + import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.FTSearchParams; import redis.clients.jedis.search.IndexOptions; @@ -12,79 +17,149 @@ import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.schemafields.SchemaField; -import java.util.List; -import java.util.Map; -import java.util.Set; public interface CommandListener { - default void searchStarted(String indexName, Query q, FTSearchParams params) {} - default void searchFinished(String indexName, Query q, FTSearchParams params, SearchResult searchResult){} + default void searchStarted(String indexName, Query q, FTSearchParams params) { + } + + default void searchFinished(String indexName, Query q, FTSearchParams params, SearchResult searchResult) { + } + + default void createIndexStarted(String indexName, FTCreateParams params, List fields, Schema schema, + IndexOptions options) { + } + + default void createIndexFinished(String indexName, FTCreateParams params, List fields, Schema schema, + IndexOptions options, String result) { + } + + default void aggregateStarted(String indexName, AggregationBuilder q) { + } + + default void aggregateFinished(String indexName, AggregationBuilder q, AggregationResult result) { + } + + default void cursorDeleteStarted(String string, long cursorId) { + } + + default void cursorDeleteFinished(String string, long cursorId, String result) { + } + + default void cursorReadStarted(String string, long cursorId, int count) { + } + + default void cursorReadFinished(String string, long cursorId, int count, AggregationResult aggregationResult) { + } + + default void explainStarted(String string, Query q) { + } + + default void explainFinished(String string, Query q, String s) { + } + + default void infoStarted(String string) { + } + + default void infoFinished(String string, Map stringObjectMap) { + } + + default void dropIndexStarted(String string) { + } + + default void dropIndexFinished(String string, String result) { + } + + default void dropIndexAndDocumentsStarted(String string) { + } + + default void dropIndexAndDocumentsFinished(String string, String result) { + } + + default void addSuggestionStarted(String string, String key, String suggestion, double score) { + } + + default void addSuggestionFinished(String string, String key, String suggestion, double score, long result) { + } + + default void getSuggestionStarted(String string, String key, String prefix, AutoCompleteOptions options) { + } + + default void getSuggestionFinished(String string, String key, String prefix, AutoCompleteOptions options, + List list) { + } + + default void deleteSuggestionStarted(String string, String key, String entry) { + } + + default void deleteSuggestionFinished(String string, String key, String entry, boolean result) { + } - default void createIndexStarted(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options){} - default void createIndexFinished(String indexName, FTCreateParams params, List fields, Schema schema, IndexOptions options, String result){} + default void getSuggestionLengthStarted(String string, String key) { + } - default void aggregateStarted(String indexName, AggregationBuilder q){} - default void aggregateFinished(String indexName, AggregationBuilder q, AggregationResult result){} + default void getSuggestionLengthFinished(String string, String key, long result) { + } - default void cursorDeleteStarted(String string, long cursorId){} - default void cursorDeleteFinished(String string, long cursorId, String result){} + default void alterIndexStarted(String string, SchemaField[] fields) { + } - default void cursorReadStarted(String string, long cursorId, int count){} - default void cursorReadFinished(String string, long cursorId, int count, AggregationResult aggregationResult){} + default void alterIndexFinished(String string, SchemaField[] fields, String result) { + } - default void explainStarted(String string, Query q){} - default void explainFinished(String string, Query q, String s){} + default void setConfigStarted(String string, String option, String value) { + } - default void infoStarted(String string){} - default void infoFinished(String string, Map stringObjectMap){} + default void setConfigFinished(String string, String option, String value, String result) { + } - default void dropIndexStarted(String string){} - default void dropIndexFinished(String string, String result){} + default void getConfigStarted(String string, String option) { + } - default void dropIndexAndDocumentsStarted(String string){} - default void dropIndexAndDocumentsFinished(String string, String result){} + default void getConfigFinished(String string, String option, Map result) { + } - default void addSuggestionStarted(String string, String key, String suggestion, double score){} - default void addSuggestionFinished(String string, String key, String suggestion, double score, long result){} + default void getIndexConfigStarted(String string, String option) { + } - default void getSuggestionStarted(String string, String key, String prefix, AutoCompleteOptions options){} - default void getSuggestionFinished(String string, String key, String prefix, AutoCompleteOptions options, List list){} + default void getIndexConfigFinished(String string, String option, Map result) { + } - default void deleteSuggestionStarted(String string, String key, String entry){} - default void deleteSuggestionFinished(String string, String key, String entry, boolean result){} + default void addAliasStarted(String string, String name) { + } - default void getSuggestionLengthStarted(String string, String key){} - default void getSuggestionLengthFinished(String string, String key, long result){} + default void addAliasFinished(String string, String name, String result) { + } - default void alterIndexStarted(String string, SchemaField[] fields){} - default void alterIndexFinished(String string, SchemaField[] fields, String result){} + default void updateAliasStarted(String string, String name) { + } - default void setConfigStarted(String string, String option, String value){} - default void setConfigFinished(String string, String option, String value, String result){} + default void updateAliasFinished(String string, String name, String result) { + } - default void getConfigStarted(String string, String option){} - default void getConfigFinished(String string, String option, Map result){} + default void deleteAliasStarted(String string, String name) { + } - default void getIndexConfigStarted(String string, String option){} - default void getIndexConfigFinished(String string, String option, Map result){} + default void deleteAliasFinished(String string, String name, String result) { + } - default void addAliasStarted(String string, String name){} - default void addAliasFinished(String string, String name, String result){} + default void updateSynonymStarted(String string, String synonymGroupId, String[] terms) { + } - default void updateAliasStarted(String string, String name){} - default void updateAliasFinished(String string, String name, String result){} + default void updateSynonymFinished(String string, String synonymGroupId, String[] terms, String result) { + } - default void deleteAliasStarted(String string, String name){} - default void deleteAliasFinished(String string, String name, String result){} + default void dumpSynonymStarted(String string) { + } - default void updateSynonymStarted(String string, String synonymGroupId, String[] terms){} - default void updateSynonymFinished(String string, String synonymGroupId, String[] terms, String result){} + default void dumpSynonymFinished(String string, Map> result) { + } - default void dumpSynonymStarted(String string){} - default void dumpSynonymFinished(String string, Map> result){} + default void tagValsStarted(String string, String field) { + } - default void tagValsStarted(String string, String field){} - default void tagValsFinished(String string, String field, Set result){} + default void tagValsFinished(String string, String field, Set result) { + } - default void commandFailed(SearchProtocol.SearchCommand command, String indexName, Throwable t){} + default void commandFailed(SearchProtocol.SearchCommand command, String indexName, Throwable t) { + } } diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java index 3b7fdc81..bfead3ea 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/NoOpCommandListener.java @@ -1,4 +1,4 @@ package com.redis.om.spring.ops; -public class NoOpCommandListener implements CommandListener { +public class NoOpCommandListener implements CommandListener { } diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java index 19c564ba..2b1b019d 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/ops/RedisModulesOperations.java @@ -22,10 +22,10 @@ * and provides typed access to module-specific operations through the {@code opsFor*} methods. *

* - * @param the type of keys used in Redis operations - * @param client the Redis modules client for executing commands - * @param template the Spring Data Redis template for additional Redis operations - * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration + * @param the type of keys used in Redis operations + * @param client the Redis modules client for executing commands + * @param template the Spring Data Redis template for additional Redis operations + * @param gsonBuilder the Gson builder for JSON serialization/deserialization configuration * @param commandListener A command listener for monitoring Redis commands * * @author Redis OM Spring Team 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 5f9a47dd..afd766bf 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 @@ -3,13 +3,14 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.redis.om.spring.ops.CommandListener; + import org.springframework.data.redis.core.StringRedisTemplate; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.redis.om.spring.autocomplete.Suggestion; import com.redis.om.spring.client.RedisModulesClient; +import com.redis.om.spring.ops.CommandListener; import com.redis.om.spring.repository.query.autocomplete.AutoCompleteOptions; import redis.clients.jedis.resps.Tuple; @@ -44,13 +45,13 @@ public class SearchOperationsImpl implements SearchOperations { /** * Creates a new search operations implementation. * - * @param index the search index identifier - * @param modulesClient the Redis modules client for search operations - * @param template the string Redis template for additional operations + * @param index the search index identifier + * @param modulesClient the Redis modules client for search operations + * @param template the string Redis template for additional operations * @param commandListener A command listener for monitoring Redis commands */ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRedisTemplate template, - final CommandListener commandListener) { + final CommandListener commandListener) { this.index = index; this.modulesClient = modulesClient; this.search = modulesClient.clientForSearch(); @@ -58,95 +59,95 @@ public SearchOperationsImpl(K index, RedisModulesClient modulesClient, StringRed this.commandListener = commandListener; } - @Override - public String createIndex(Schema schema, IndexOptions options) { - commandListener.createIndexStarted(index.toString(), null, null, schema, options); - String result = null; - try { - result = search.ftCreate(index.toString(), options, schema); - } catch (Exception e) { - throw e; - }finally { - commandListener.createIndexFinished(index.toString(), null, null, schema, options, result); - } - return result; - } - - @Override - public String createIndex(FTCreateParams params, List fields) { - commandListener.createIndexStarted(index.toString(), params, fields, null, null); - String result = null; - try { - result = search.ftCreate(index.toString(), params, fields); - } catch (Exception e) { - throw e; - } finally { - commandListener.createIndexFinished(index.toString(), params, fields, null, null, result); - } - return result; + @Override + public String createIndex(Schema schema, IndexOptions options) { + commandListener.createIndexStarted(index.toString(), null, null, schema, options); + String result = null; + try { + result = search.ftCreate(index.toString(), options, schema); + } catch (Exception e) { + throw e; + } finally { + commandListener.createIndexFinished(index.toString(), null, null, schema, options, result); } + return result; + } - @Override - @Deprecated - public SearchResult search(Query q) { - commandListener.searchStarted(index.toString(), q, null); - SearchResult result = null; - try { - result = search.ftSearch(SafeEncoder.encode(index.toString()), q); - } catch (Exception e) { - throw e; - } finally { - commandListener.searchFinished(index.toString(), q, null, result); - } - return result; + @Override + public String createIndex(FTCreateParams params, List fields) { + commandListener.createIndexStarted(index.toString(), params, fields, null, null); + String result = null; + try { + result = search.ftCreate(index.toString(), params, fields); + } catch (Exception e) { + throw e; + } finally { + commandListener.createIndexFinished(index.toString(), params, fields, null, null, result); } + return result; + } - @Override - public SearchResult search(Query q, FTSearchParams params) { - commandListener.searchStarted(index.toString(), q, null); - final SearchResult result = search.ftSearch(index.toString(), q.toString(), params); + @Override + @Deprecated + public SearchResult search(Query q) { + commandListener.searchStarted(index.toString(), q, null); + SearchResult result = null; + try { + result = search.ftSearch(SafeEncoder.encode(index.toString()), q); + } catch (Exception e) { + throw e; + } finally { commandListener.searchFinished(index.toString(), q, null, result); - return result; } + return result; + } - @Override - public AggregationResult aggregate(AggregationBuilder q) { - commandListener.aggregateStarted(index.toString(), q); - AggregationResult result = null; - try { - result = search.ftAggregate(index.toString(), q); - } catch (Exception e) { - throw e; - } finally { - commandListener.aggregateFinished(index.toString(), q, result); - } - return result; + @Override + public SearchResult search(Query q, FTSearchParams params) { + commandListener.searchStarted(index.toString(), q, null); + final SearchResult result = search.ftSearch(index.toString(), q.toString(), params); + commandListener.searchFinished(index.toString(), q, null, result); + return result; + } + + @Override + public AggregationResult aggregate(AggregationBuilder q) { + commandListener.aggregateStarted(index.toString(), q); + AggregationResult result = null; + try { + result = search.ftAggregate(index.toString(), q); + } catch (Exception e) { + throw e; + } finally { + commandListener.aggregateFinished(index.toString(), q, result); } + return result; + } - @Override - public String cursorDelete(long cursorId) { - commandListener.cursorDeleteStarted(index.toString(), cursorId); - String result = null; - try { - result = search.ftCursorDel(index.toString(), cursorId); - } catch (Exception e) { - throw e; - } finally { - commandListener.cursorDeleteFinished(index.toString(), cursorId, result); - } - return result; + @Override + public String cursorDelete(long cursorId) { + commandListener.cursorDeleteStarted(index.toString(), cursorId); + String result = null; + try { + result = search.ftCursorDel(index.toString(), cursorId); + } catch (Exception e) { + throw e; + } finally { + commandListener.cursorDeleteFinished(index.toString(), cursorId, result); } + return result; + } @Override public AggregationResult cursorRead(long cursorId, int count) { commandListener.cursorReadStarted(index.toString(), cursorId, count); AggregationResult result = null; try { - result = search.ftCursorRead(index.toString(), cursorId, count); + result = search.ftCursorRead(index.toString(), cursorId, count); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.cursorReadFinished(index.toString(), cursorId, count, result); + commandListener.cursorReadFinished(index.toString(), cursorId, count, result); } return result; } @@ -156,11 +157,11 @@ public String explain(Query q) { commandListener.explainStarted(index.toString(), q); String result = null; try { - result = search.ftExplain(index.toString(), q); + result = search.ftExplain(index.toString(), q); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.explainFinished(index.toString(), q, result); + commandListener.explainFinished(index.toString(), q, result); } return result; } @@ -170,11 +171,11 @@ public Map getInfo() { commandListener.infoStarted(index.toString()); Map result = Map.of(); try { - result = search.ftInfo(index.toString()); + result = search.ftInfo(index.toString()); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.infoFinished(index.toString(), result); + commandListener.infoFinished(index.toString(), result); } return result; } @@ -184,11 +185,11 @@ public String dropIndex() { commandListener.dropIndexStarted(index.toString()); String result = null; try { - result = search.ftDropIndex(index.toString()); + result = search.ftDropIndex(index.toString()); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.dropIndexFinished(index.toString(), result); + commandListener.dropIndexFinished(index.toString(), result); } return result; } @@ -198,11 +199,11 @@ public String dropIndexAndDocuments() { commandListener.dropIndexAndDocumentsStarted(index.toString()); String result = null; try { - result = search.ftDropIndexDD(index.toString()); + result = search.ftDropIndexDD(index.toString()); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.dropIndexAndDocumentsFinished(index.toString(), result); + commandListener.dropIndexAndDocumentsFinished(index.toString(), result); } return result; } @@ -217,11 +218,11 @@ public Long addSuggestion(String key, String suggestion, double score) { commandListener.addSuggestionStarted(index.toString(), key, suggestion, score); long result = 0; try { - result = search.ftSugAdd(key, suggestion, score); + result = search.ftSugAdd(key, suggestion, score); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); + commandListener.addSuggestionFinished(index.toString(), key, suggestion, score, result); } return result; } @@ -240,48 +241,48 @@ public List getSuggestion(String key, String prefix, AutoCompleteOpt List suggestions = search.ftSugGetWithScores(key, prefix, options.isFuzzy(), options.getLimit()); List list = List.of(); try { - list = suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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.getElement()); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); - } else { - return new Suggestion(suggestion.getElement(), suggestion.getScore()); - } - }).toList(); + list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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.getElement()); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion.getElement(), suggestion.getScore(), payloadMap); + } else { + return new Suggestion(suggestion.getElement(), suggestion.getScore()); + } + }).toList(); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); } return list; } else { List suggestions = search.ftSugGet(key, prefix, options.isFuzzy(), options.getLimit()); List list = List.of(); try { - list = suggestions.stream().map(suggestion -> { - if (options.isWithPayload()) { - 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); - String json = payload != null ? payload.toString() : "{}"; - Map payloadMap = gson.fromJson(json, new TypeToken>() { - }.getType()); - return new Suggestion(suggestion, payloadMap); - } else { - return new Suggestion(suggestion); - } - }).toList(); + list = suggestions.stream().map(suggestion -> { + if (options.isWithPayload()) { + 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); + String json = payload != null ? payload.toString() : "{}"; + Map payloadMap = gson.fromJson(json, new TypeToken>() { + }.getType()); + return new Suggestion(suggestion, payloadMap); + } else { + return new Suggestion(suggestion); + } + }).toList(); } catch (Exception e) { - throw e; - }finally { - commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); + throw e; + } finally { + commandListener.getSuggestionFinished(index.toString(), key, prefix, options, list); } return list; } @@ -292,11 +293,11 @@ public Boolean deleteSuggestion(String key, String entry) { commandListener.deleteSuggestionStarted(index.toString(), key, entry); boolean result = false; try { - result = search.ftSugDel(key, entry); + result = search.ftSugDel(key, entry); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); + commandListener.deleteSuggestionFinished(index.toString(), key, entry, result); } return result; } @@ -306,11 +307,11 @@ public Long getSuggestionLength(String key) { commandListener.getSuggestionLengthStarted(index.toString(), key); long result = 0; try { - result = search.ftSugLen(key); + result = search.ftSugLen(key); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.getSuggestionLengthFinished(index.toString(), key, result); + commandListener.getSuggestionLengthFinished(index.toString(), key, result); } return result; } @@ -320,11 +321,11 @@ public String alterIndex(SchemaField... fields) { commandListener.alterIndexStarted(index.toString(), fields); String result = null; try { - result = search.ftAlter(index.toString(), fields); + result = search.ftAlter(index.toString(), fields); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.alterIndexFinished(index.toString(), fields, result); + commandListener.alterIndexFinished(index.toString(), fields, result); } return result; } @@ -334,11 +335,11 @@ public String setConfig(String option, String value) { commandListener.setConfigStarted(index.toString(), option, value); String result = null; try { - result = search.ftConfigSet(option, value); + result = search.ftConfigSet(option, value); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.setConfigFinished(index.toString(), option, value, result); + commandListener.setConfigFinished(index.toString(), option, value, result); } return result; } @@ -348,11 +349,11 @@ public Map getConfig(String option) { commandListener.getConfigStarted(index.toString(), option); Map result = Map.of(); try { - result = search.ftConfigGet(option); + result = search.ftConfigGet(option); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.getConfigFinished(index.toString(), option, result); + commandListener.getConfigFinished(index.toString(), option, result); } return result; } @@ -362,11 +363,11 @@ public Map getIndexConfig(String option) { commandListener.getIndexConfigStarted(index.toString(), option); Map result = Map.of(); try { - result = search.ftConfigGet(index.toString(), option); + result = search.ftConfigGet(index.toString(), option); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.getIndexConfigFinished(index.toString(), option, result); + commandListener.getIndexConfigFinished(index.toString(), option, result); } return result; } @@ -376,11 +377,11 @@ public String addAlias(String name) { commandListener.addAliasStarted(index.toString(), name); String result = null; try { - result = search.ftAliasAdd(name, index.toString()); + result = search.ftAliasAdd(name, index.toString()); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.addAliasFinished(index.toString(), name, result); + commandListener.addAliasFinished(index.toString(), name, result); } return result; } @@ -390,11 +391,11 @@ public String updateAlias(String name) { commandListener.updateAliasStarted(index.toString(), name); String result = null; try { - result = search.ftAliasUpdate(name, index.toString()); + result = search.ftAliasUpdate(name, index.toString()); } catch (Exception e) { - throw e; - }finally { - commandListener.updateAliasFinished(index.toString(), name, result); + throw e; + } finally { + commandListener.updateAliasFinished(index.toString(), name, result); } return result; } @@ -404,11 +405,11 @@ public String deleteAlias(String name) { commandListener.deleteAliasStarted(index.toString(), name); String result = null; try { - result = search.ftAliasDel(name); + result = search.ftAliasDel(name); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.deleteAliasFinished(index.toString(), name, result); + commandListener.deleteAliasFinished(index.toString(), name, result); } return result; } @@ -418,11 +419,11 @@ public String updateSynonym(String synonymGroupId, String... terms) { commandListener.updateSynonymStarted(index.toString(), synonymGroupId, terms); String result = null; try { - result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); + result = search.ftSynUpdate(index.toString(), synonymGroupId, terms); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); + commandListener.updateSynonymFinished(index.toString(), synonymGroupId, terms, result); } return result; } @@ -432,11 +433,11 @@ public Map> dumpSynonym() { commandListener.dumpSynonymStarted(index.toString()); Map> result = Map.of(); try { - result = search.ftSynDump(index.toString()); + result = search.ftSynDump(index.toString()); } catch (Exception e) { - throw e; + throw e; } finally { - commandListener.dumpSynonymFinished(index.toString(), result); + commandListener.dumpSynonymFinished(index.toString(), result); } return result; } @@ -446,11 +447,11 @@ public Set tagVals(String field) { commandListener.tagValsStarted(index.toString(), field); Set result = Set.of(); try { - result = search.ftTagVals(index.toString(), field); + result = search.ftTagVals(index.toString(), field); } catch (Exception e) { - throw e; - }finally { - commandListener.tagValsFinished(index.toString(), field, result); + throw e; + } finally { + commandListener.tagValsFinished(index.toString(), field, result); } return result; } From a0cd05ce18ecf3312b676afc6da02b6f88b52fed Mon Sep 17 00:00:00 2001 From: "TURKCELL\\TCASENOCAK" Date: Fri, 28 Nov 2025 07:27:42 +0300 Subject: [PATCH 9/9] feat: add CommandListener parameter to RedisModulesOperations bean configuration for romsmultiaclaccount --- .../config/RedisConnectionFactoryConfig.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java index d904b0a3..8e416c34 100644 --- a/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java +++ b/demos/roms-multi-acl-account/src/main/java/com/redis/romsmultiaclaccount/config/RedisConnectionFactoryConfig.java @@ -27,6 +27,7 @@ import com.redis.om.spring.RedisOMProperties; import com.redis.om.spring.client.RedisModulesClient; import com.redis.om.spring.indexing.RediSearchIndexer; +import com.redis.om.spring.ops.CommandListener; import com.redis.om.spring.ops.RedisModulesOperations; import com.redis.om.spring.vectorize.Embedder; @@ -120,8 +121,8 @@ public RedisModulesOperations writeRedisModulesOperations(@Qualifier( "writeJedisConnectionFactory" ) JedisConnectionFactory factory, @Qualifier( "omGsonBuilder" - ) GsonBuilder builder) { - return new RedisModulesOperations<>(client, new StringRedisTemplate(factory), builder); + ) GsonBuilder builder, final CommandListener commandListener) { + return new RedisModulesOperations<>(client, new StringRedisTemplate(factory), builder, commandListener); } @Bean( @@ -133,8 +134,8 @@ public RedisModulesOperations readRedisModulesOperations(@Qualifier( "readJedisConnectionFactory" ) JedisConnectionFactory factory, @Qualifier( "omGsonBuilder" - ) GsonBuilder builder) { - return new RedisModulesOperations<>(client, new StringRedisTemplate(factory), builder); + ) GsonBuilder builder, final CommandListener commandListener) { + return new RedisModulesOperations<>(client, new StringRedisTemplate(factory), builder, commandListener); } @Bean(