diff --git a/docs/src/main/asciidoc/redis-reference.adoc b/docs/src/main/asciidoc/redis-reference.adoc index faaf897d23b60..32bbd9c012a67 100644 --- a/docs/src/main/asciidoc/redis-reference.adoc +++ b/docs/src/main/asciidoc/redis-reference.adoc @@ -285,6 +285,7 @@ As mentioned above, the API is divided into groups: - graph - `.graph()` (requires the https://redis.com/modules/redis-graph/[RedisGraph] module on the server side). These commands are marked as experimental, as we would need feedback before making them stable. - search - `.search()` (requires the https://redis.com/modules/redis-search/[RedisSearch] module on the server side). +- auto-suggest - `.autosuggest()` (requires the https://redis.com/modules/redis-search/[RedisSearch] module on the server side).O These commands are marked as experimental, as we would need feedback before making them stable. Each of these methods returns an object that lets you execute the commands related to the group. diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/ReactiveRedisDataSource.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/ReactiveRedisDataSource.java index 44e0b7fbca10a..f69ddf8ad77cc 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/ReactiveRedisDataSource.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/ReactiveRedisDataSource.java @@ -3,6 +3,7 @@ import java.util.function.BiFunction; import java.util.function.Function; +import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.ReactiveBitMapCommands; import io.quarkus.redis.datasource.bloom.ReactiveBloomCommands; import io.quarkus.redis.datasource.countmin.ReactiveCountMinCommands; @@ -539,6 +540,27 @@ default ReactiveSearchCommands search() { return search(String.class); } + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @param the type of keys + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + ReactiveAutoSuggestCommands autosuggest(Class redisKeyType); + + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + default ReactiveAutoSuggestCommands autosuggest() { + return autosuggest(String.class); + } + /** * Executes a command. * This method is used to execute commands not offered by the API. diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/RedisDataSource.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/RedisDataSource.java index 62189ac67fe40..a814207fac43e 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/RedisDataSource.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/RedisDataSource.java @@ -4,6 +4,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import io.quarkus.redis.datasource.autosuggest.AutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.BitMapCommands; import io.quarkus.redis.datasource.bloom.BloomCommands; import io.quarkus.redis.datasource.countmin.CountMinCommands; @@ -528,6 +529,27 @@ default SearchCommands search() { return search(String.class); } + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @param the type of keys + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + AutoSuggestCommands autosuggest(Class redisKeyType); + + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + default AutoSuggestCommands autosuggest() { + return autosuggest(String.class); + } + /** * Gets the objects to publish and receive messages. * diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/AutoSuggestCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/AutoSuggestCommands.java new file mode 100644 index 0000000000000..4ef1224154758 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/AutoSuggestCommands.java @@ -0,0 +1,88 @@ +package io.quarkus.redis.datasource.autosuggest; + +import java.util.List; + +import io.quarkus.redis.datasource.RedisCommands; + +/** + * Allows executing commands from the {@code auto-suggest} group (requires the Redis Search module from Redis stack). + * See the auto-suggest command list for further information about + * these + * commands. + * + * @param the type of the key + */ +public interface AutoSuggestCommands extends RedisCommands { + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @return A uni emitting the current size of the suggestion dictionary. + */ + default long ftSugAdd(K key, String string, double score) { + return ftSugAdd(key, string, score, false); + } + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @param increment increments the existing entry of the suggestion by the given score, instead of replacing the score. + * This is useful for updating the dictionary based on user queries in real time. + * @return A uni emitting the current size of the suggestion dictionary. + */ + long ftSugAdd(K key, String string, double score, boolean increment); + + /** + * Execute the command FT.SUGDEL. + * Summary: Delete a string from a suggestion index + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @return A uni emitting {@code true} if the value was found, {@code false} otherwise + */ + boolean ftSugDel(K key, String string); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @return A uni emitting a list of the top suggestions matching the prefix, optionally with score after each entry. + */ + List ftSugGet(K key, String prefix); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @param args the extra argument, must not be {@code null} + * @return A uni emitting {@code true} if the value was found, {@code false} otherwise + */ + List ftSugGet(K key, String prefix, GetArgs args); + + /** + * Execute the command FT.SUGLEN. + * Summary: Get the size of an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @return A uni emitting the current size of the suggestion dictionary. + */ + long ftSugLen(K key); +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/GetArgs.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/GetArgs.java new file mode 100644 index 0000000000000..bbfeab15e1406 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/GetArgs.java @@ -0,0 +1,68 @@ +package io.quarkus.redis.datasource.autosuggest; + +import static io.quarkus.redis.runtime.datasource.Validation.positive; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.redis.datasource.RedisCommandExtraArguments; + +public class GetArgs implements RedisCommandExtraArguments { + + private boolean fuzzy; + private int max; + private boolean withScores; + + /** + * Performs a fuzzy prefix search, including prefixes at Levenshtein distance of 1 from the prefix sent. + * + * @return the current {@code GetArgs}. + */ + public GetArgs fuzzy() { + this.fuzzy = true; + return this; + } + + /** + * Limits the results to a maximum of num (default: 5). + * + * @param max the max number of results, must be strictly positive + * @return the current {@code GetArgs}. + */ + public GetArgs max(int max) { + positive(max, "max"); + this.max = max; + return this; + } + + /** + * Also to attach the score of each suggestion. + * This can be used to merge results from multiple instances. + * + * @return the current {@code GetArgs}. + */ + public GetArgs withScores() { + this.withScores = true; + return this; + } + + @Override + public List toArgs() { + List list = new ArrayList<>(); + if (fuzzy) { + list.add("FUZZY"); + } + if (max > 0) { + list.add("MAX"); + list.add(Integer.toString(max)); + } + if (withScores) { + list.add("WITHSCORES"); + } + return list; + } + + public boolean hasScores() { + return withScores; + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveAutoSuggestCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveAutoSuggestCommands.java new file mode 100644 index 0000000000000..17e029f3ff0f2 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveAutoSuggestCommands.java @@ -0,0 +1,91 @@ +package io.quarkus.redis.datasource.autosuggest; + +import java.util.List; + +import io.quarkus.redis.datasource.ReactiveRedisCommands; +import io.smallrye.common.annotation.Experimental; +import io.smallrye.mutiny.Uni; + +/** + * Allows executing commands from the {@code auto-suggest} group (requires the Redis Search module from Redis stack). + * See the auto-suggest command list for further information about + * these commands. + * + * @param the type of the key + */ +@Experimental("The auto-suggest group is experimental") +public interface ReactiveAutoSuggestCommands extends ReactiveRedisCommands { + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @return A uni emitting the current size of the suggestion dictionary. + **/ + default Uni ftSugAdd(K key, String string, double score) { + return ftSugAdd(key, string, score, false); + } + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @param increment increments the existing entry of the suggestion by the given score, instead of replacing the score. + * This is useful for updating the dictionary based on user queries in real time. + * @return A uni emitting the current size of the suggestion dictionary. + **/ + Uni ftSugAdd(K key, String string, double score, boolean increment); + + /** + * Execute the command FT.SUGDEL. + * Summary: Delete a string from a suggestion index + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @return A uni emitting {@code true} if the value was found, {@code false} otherwise + **/ + Uni ftSugDel(K key, String string); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @return A uni emitting a list of the top suggestions matching the prefix, optionally with score after each entry. + **/ + Uni> ftSugGet(K key, String prefix); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @param args the extra argument, must not be {@code null} + * @return A uni emitting {@code true} if the value was found, {@code false} otherwise + **/ + Uni> ftSugGet(K key, String prefix, GetArgs args); + + /** + * Execute the command FT.SUGLEN. + * Summary: Get the size of an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @return A uni emitting the current size of the suggestion dictionary. + **/ + Uni ftSugLen(K key); + +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveTransactionalAutoSuggestCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveTransactionalAutoSuggestCommands.java new file mode 100644 index 0000000000000..fd276cf7da208 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/ReactiveTransactionalAutoSuggestCommands.java @@ -0,0 +1,94 @@ +package io.quarkus.redis.datasource.autosuggest; + +import io.quarkus.redis.datasource.ReactiveTransactionalRedisCommands; +import io.smallrye.mutiny.Uni; + +/** + * Allows executing commands from the {@code auto-suggest} group (requires the Redis Search module from Redis stack). + * See the auto-suggest command list for further information about + * these + * commands. + * This API is intended to be used in a Redis transaction ({@code MULTI}), thus, all command methods return {@code Uni}. + * + * @param the type of the key + */ +public interface ReactiveTransactionalAutoSuggestCommands extends ReactiveTransactionalRedisCommands { + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + default Uni ftSugAdd(K key, String string, double score) { + return ftSugAdd(key, string, score, false); + } + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @param increment increments the existing entry of the suggestion by the given score, instead of replacing the score. + * This is useful for updating the dictionary based on user queries in real time. + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + Uni ftSugAdd(K key, String string, double score, boolean increment); + + /** + * Execute the command FT.SUGDEL. + * Summary: Delete a string from a suggestion index + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + Uni ftSugDel(K key, String string); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + Uni ftSugget(K key, String prefix); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @param args the extra argument, must not be {@code null} + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + Uni ftSugget(K key, String prefix, GetArgs args); + + /** + * Execute the command FT.SUGLEN. + * Summary: Get the size of an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @return A {@code Uni} emitting {@code null} when the command has been enqueued successfully in the transaction, a failure + * otherwise. In the case of failure, the transaction is discarded. + */ + Uni ftSugLen(K key); +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/Suggestion.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/Suggestion.java new file mode 100644 index 0000000000000..9d9a72d35c8e7 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/Suggestion.java @@ -0,0 +1,36 @@ +package io.quarkus.redis.datasource.autosuggest; + +/** + * Represent a suggestion. + *

+ * If the {@code SUGGET} command is executed with the {@code WITHSCORES} parameter, the suggestion also contains the + * score. {@code 0.0} otherwise. + */ +public class Suggestion { + + private final String suggestion; + private final double score; + + public Suggestion(String suggestion, double score) { + this.suggestion = suggestion; + this.score = score; + } + + public Suggestion(String suggestion) { + this(suggestion, 0.0); + } + + /** + * @return the suggestion + */ + public String suggestion() { + return suggestion; + } + + /** + * @return the score, 0.0 is not available. + */ + public double score() { + return score; + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/TransactionalAutoSuggestCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/TransactionalAutoSuggestCommands.java new file mode 100644 index 0000000000000..f0cde9b1a757c --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/autosuggest/TransactionalAutoSuggestCommands.java @@ -0,0 +1,81 @@ +package io.quarkus.redis.datasource.autosuggest; + +import io.quarkus.redis.datasource.TransactionalRedisCommands; + +/** + * Allows executing commands from the {@code auto-suggest} group (requires the Redis Search module from Redis stack). + * See the auto-suggest command list for further information about + * these + * commands. + * This API is intended to be used in a Redis transaction ({@code MULTI}), thus, all command methods return {@code void}. + * + * @param the type of the key + */ +public interface TransactionalAutoSuggestCommands extends TransactionalRedisCommands { + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + */ + default void ftSugAdd(K key, String string, double score) { + ftSugAdd(key, string, score, false); + } + + /** + * Execute the command FT.SUGADD. + * Summary: Add a suggestion string to an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + * @param score the floating point number of the suggestion string's weight + * @param increment increments the existing entry of the suggestion by the given score, instead of replacing the score. + * This is useful for updating the dictionary based on user queries in real time. + */ + void ftSugAdd(K key, String string, double score, boolean increment); + + /** + * Execute the command FT.SUGDEL. + * Summary: Delete a string from a suggestion index + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param string the suggestion string to index + */ + void ftSugDel(K key, String string); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + */ + void ftSugget(K key, String prefix); + + /** + * Execute the command FT.SUGGET. + * Summary: Get completion suggestions for a prefix + * Group: auto-suggest + * + * @param key the suggestion dictionary key + * @param prefix is prefix to complete on. + * @param args the extra argument, must not be {@code null} + */ + void ftSugget(K key, String prefix, GetArgs args); + + /** + * Execute the command FT.SUGLEN. + * Summary: Get the size of an auto-complete suggestion dictionary + * Group: auto-suggest + * + * @param key the suggestion dictionary key + */ + void ftSugLen(K key); +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/search/CreateArgs.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/search/CreateArgs.java index f972ac7126531..619944032d1e5 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/search/CreateArgs.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/search/CreateArgs.java @@ -1,7 +1,7 @@ package io.quarkus.redis.datasource.search; -import static io.quarkus.redis.runtime.datasource.Validation.*; import static io.quarkus.redis.runtime.datasource.Validation.notNullOrBlank; +import static io.quarkus.redis.runtime.datasource.Validation.notNullOrEmpty; import static io.quarkus.redis.runtime.datasource.Validation.positive; import static io.smallrye.mutiny.helpers.ParameterValidation.doesNotContainNull; import static io.smallrye.mutiny.helpers.ParameterValidation.validate; diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/ReactiveTransactionalRedisDataSource.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/ReactiveTransactionalRedisDataSource.java index 3f89f8a3d6443..95d402fc45c2e 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/ReactiveTransactionalRedisDataSource.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/ReactiveTransactionalRedisDataSource.java @@ -1,5 +1,6 @@ package io.quarkus.redis.datasource.transactions; +import io.quarkus.redis.datasource.autosuggest.ReactiveTransactionalAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.ReactiveTransactionalBitMapCommands; import io.quarkus.redis.datasource.bloom.ReactiveTransactionalBloomCommands; import io.quarkus.redis.datasource.countmin.ReactiveTransactionalCountMinCommands; @@ -431,6 +432,27 @@ default ReactiveTransactionalSearchCommands search() { return search(String.class); } + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @param the type of keys + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + ReactiveTransactionalAutoSuggestCommands autosuggest(Class redisKeyType); + + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + default ReactiveTransactionalAutoSuggestCommands autosuggest() { + return autosuggest(String.class); + } + /** * Executes a command. * This method is used to execute commands not offered by the API. diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/TransactionalRedisDataSource.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/TransactionalRedisDataSource.java index 31db54902624d..15cab21b71916 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/TransactionalRedisDataSource.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/datasource/transactions/TransactionalRedisDataSource.java @@ -1,5 +1,6 @@ package io.quarkus.redis.datasource.transactions; +import io.quarkus.redis.datasource.autosuggest.TransactionalAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.TransactionalBitMapCommands; import io.quarkus.redis.datasource.bloom.TransactionalBloomCommands; import io.quarkus.redis.datasource.countmin.TransactionalCountMinCommands; @@ -429,6 +430,27 @@ default TransactionalSearchCommands search() { return search(String.class); } + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @param the type of keys + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + TransactionalAutoSuggestCommands autosuggest(Class redisKeyType); + + /** + * Gets the object to emit commands from the {@code auto-suggest} group. + * This group requires the RedisSearch module. + * + * @return the object to get suggestions + */ + @Experimental("The Redis auto-suggest support is experimental") + default TransactionalAutoSuggestCommands autosuggest() { + return autosuggest(String.class); + } + /** * Executes a command. * This method is used to execute commands not offered by the API. diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractAutoSuggestCommands.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractAutoSuggestCommands.java new file mode 100644 index 0000000000000..e0288d5ea3fe0 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/AbstractAutoSuggestCommands.java @@ -0,0 +1,65 @@ +package io.quarkus.redis.runtime.datasource; + +import static io.quarkus.redis.runtime.datasource.Validation.notNullOrBlank; +import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull; + +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.redis.client.Command; +import io.vertx.mutiny.redis.client.Response; + +public class AbstractAutoSuggestCommands extends AbstractRedisCommands { + + AbstractAutoSuggestCommands(RedisCommandExecutor redis, Class k) { + super(redis, new Marshaller(k)); + } + + Uni _ftSugAdd(K key, String string, double score, boolean increment) { + nonNull(key, "key"); + notNullOrBlank(string, "string"); + RedisCommand cmd = RedisCommand.of(Command.FT_SUGADD) + .put(marshaller.encode(key)) + .put(string) + .put(score); + if (increment) { + cmd.put("INCR"); + } + return execute(cmd); + } + + Uni _ftSugDel(K key, String string) { + nonNull(key, "key"); + notNullOrBlank(string, "string"); + RedisCommand cmd = RedisCommand.of(Command.FT_SUGDEL) + .put(marshaller.encode(key)) + .put(string); + return execute(cmd); + } + + Uni _ftSugget(K key, String prefix) { + nonNull(key, "key"); + notNullOrBlank(prefix, "prefix"); + RedisCommand cmd = RedisCommand.of(Command.FT_SUGGET) + .put(marshaller.encode(key)) + .put(prefix); + return execute(cmd); + } + + Uni _ftSugget(K key, String prefix, GetArgs args) { + nonNull(key, "key"); + notNullOrBlank(prefix, "prefix"); + nonNull(args, "args"); + RedisCommand cmd = RedisCommand.of(Command.FT_SUGGET) + .put(marshaller.encode(key)) + .put(prefix) + .putArgs(args); + return execute(cmd); + } + + Uni _ftSugLen(K key) { + nonNull(key, "key"); + RedisCommand cmd = RedisCommand.of(Command.FT_SUGLEN) + .put(marshaller.encode(key)); + return execute(cmd); + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingAutoSuggestCommandsImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingAutoSuggestCommandsImpl.java new file mode 100644 index 0000000000000..c871abcff0c44 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingAutoSuggestCommandsImpl.java @@ -0,0 +1,45 @@ +package io.quarkus.redis.runtime.datasource; + +import java.time.Duration; +import java.util.List; + +import io.quarkus.redis.datasource.RedisDataSource; +import io.quarkus.redis.datasource.autosuggest.AutoSuggestCommands; +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands; +import io.quarkus.redis.datasource.autosuggest.Suggestion; + +public class BlockingAutoSuggestCommandsImpl extends AbstractRedisCommandGroup implements AutoSuggestCommands { + + private final ReactiveAutoSuggestCommands reactive; + + public BlockingAutoSuggestCommandsImpl(RedisDataSource ds, ReactiveAutoSuggestCommands reactive, Duration timeout) { + super(ds, timeout); + this.reactive = reactive; + } + + @Override + public long ftSugAdd(K key, String string, double score, boolean increment) { + return reactive.ftSugAdd(key, string, score, increment).await().atMost(timeout); + } + + @Override + public boolean ftSugDel(K key, String string) { + return reactive.ftSugDel(key, string).await().atMost(timeout); + } + + @Override + public List ftSugGet(K key, String prefix) { + return reactive.ftSugGet(key, prefix).await().atMost(timeout); + } + + @Override + public List ftSugGet(K key, String prefix, GetArgs args) { + return reactive.ftSugGet(key, prefix, args).await().atMost(timeout); + } + + @Override + public long ftSugLen(K key) { + return reactive.ftSugLen(key).await().atMost(timeout); + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingRedisDataSourceImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingRedisDataSourceImpl.java index 45705189f8b61..0e813fedcfa56 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingRedisDataSourceImpl.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingRedisDataSourceImpl.java @@ -9,6 +9,7 @@ import io.quarkus.redis.datasource.ReactiveRedisDataSource; import io.quarkus.redis.datasource.RedisDataSource; +import io.quarkus.redis.datasource.autosuggest.AutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.BitMapCommands; import io.quarkus.redis.datasource.bloom.BloomCommands; import io.quarkus.redis.datasource.countmin.CountMinCommands; @@ -263,6 +264,11 @@ public SearchCommands search(Class redisKeyType) { return new BlockingSearchCommandsImpl<>(this, reactive.search(redisKeyType), timeout); } + @Override + public AutoSuggestCommands autosuggest(Class redisKeyType) { + return new BlockingAutoSuggestCommandsImpl<>(this, reactive.autosuggest(redisKeyType), timeout); + } + @Override public PubSubCommands pubsub(Class messageType) { return new BlockingPubSubCommandsImpl<>(this, reactive.pubsub(messageType), timeout); diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalAutoSuggestCommandsImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalAutoSuggestCommandsImpl.java new file mode 100644 index 0000000000000..ba0405083055f --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalAutoSuggestCommandsImpl.java @@ -0,0 +1,45 @@ +package io.quarkus.redis.runtime.datasource; + +import java.time.Duration; + +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.datasource.autosuggest.ReactiveTransactionalAutoSuggestCommands; +import io.quarkus.redis.datasource.autosuggest.TransactionalAutoSuggestCommands; +import io.quarkus.redis.datasource.transactions.TransactionalRedisDataSource; + +public class BlockingTransactionalAutoSuggestCommandsImpl extends AbstractTransactionalRedisCommandGroup + implements TransactionalAutoSuggestCommands { + + private final ReactiveTransactionalAutoSuggestCommands reactive; + + public BlockingTransactionalAutoSuggestCommandsImpl(TransactionalRedisDataSource ds, + ReactiveTransactionalAutoSuggestCommands reactive, Duration timeout) { + super(ds, timeout); + this.reactive = reactive; + } + + @Override + public void ftSugAdd(K key, String string, double score, boolean increment) { + reactive.ftSugAdd(key, string, score, increment).await().atMost(timeout); + } + + @Override + public void ftSugDel(K key, String string) { + reactive.ftSugDel(key, string).await().atMost(timeout); + } + + @Override + public void ftSugget(K key, String prefix) { + reactive.ftSugget(key, prefix).await().atMost(timeout); + } + + @Override + public void ftSugget(K key, String prefix, GetArgs args) { + reactive.ftSugget(key, prefix, args).await().atMost(timeout); + } + + @Override + public void ftSugLen(K key) { + reactive.ftSugLen(key).await().atMost(timeout); + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalRedisDataSourceImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalRedisDataSourceImpl.java index 3f6f3fcbcdc32..e35e6b9567e8f 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalRedisDataSourceImpl.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/BlockingTransactionalRedisDataSourceImpl.java @@ -2,6 +2,7 @@ import java.time.Duration; +import io.quarkus.redis.datasource.autosuggest.TransactionalAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.TransactionalBitMapCommands; import io.quarkus.redis.datasource.bloom.TransactionalBloomCommands; import io.quarkus.redis.datasource.countmin.TransactionalCountMinCommands; @@ -131,6 +132,11 @@ public TransactionalSearchCommands search(Class redisKeyType) { return new BlockingTransactionalSearchCommandsImpl(this, reactive.search(redisKeyType), timeout); } + @Override + public TransactionalAutoSuggestCommands autosuggest(Class redisKeyType) { + return new BlockingTransactionalAutoSuggestCommandsImpl<>(this, reactive.autosuggest(redisKeyType), timeout); + } + @Override public void execute(String command, String... args) { reactive.execute(command, args).await().atMost(timeout); diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveAutoSuggestCommandsImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveAutoSuggestCommandsImpl.java new file mode 100644 index 0000000000000..c4c1107d30867 --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveAutoSuggestCommandsImpl.java @@ -0,0 +1,78 @@ +package io.quarkus.redis.runtime.datasource; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.redis.datasource.ReactiveRedisCommands; +import io.quarkus.redis.datasource.ReactiveRedisDataSource; +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands; +import io.quarkus.redis.datasource.autosuggest.Suggestion; +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.redis.client.Response; + +public class ReactiveAutoSuggestCommandsImpl extends AbstractAutoSuggestCommands + implements ReactiveAutoSuggestCommands, ReactiveRedisCommands { + + private final ReactiveRedisDataSource reactive; + + public ReactiveAutoSuggestCommandsImpl(ReactiveRedisDataSourceImpl redis, Class k) { + super(redis, k); + this.reactive = redis; + } + + @Override + public ReactiveRedisDataSource getDataSource() { + return reactive; + } + + @Override + public Uni ftSugAdd(K key, String string, double score, boolean increment) { + return super._ftSugAdd(key, string, score, increment) + .map(Response::toLong); + } + + @Override + public Uni ftSugDel(K key, String string) { + return super._ftSugDel(key, string) + .map(Response::toBoolean); + } + + @Override + public Uni> ftSugGet(K key, String prefix) { + return super._ftSugget(key, prefix) + .map(r -> decodeAsListOfSuggestion(r, false)); + } + + @Override + public Uni> ftSugGet(K key, String prefix, GetArgs args) { + return super._ftSugget(key, prefix, args) + .map(r -> decodeAsListOfSuggestion(r, args.hasScores())); + } + + List decodeAsListOfSuggestion(Response response, boolean hasScores) { + List list = new ArrayList<>(); + if (hasScores) { + String current = null; + for (Response nested : response) { + if (current == null) { + current = nested.toString(); + } else { + list.add(new Suggestion(current, nested.toDouble())); + current = null; + } + } + } else { + for (Response nested : response) { + list.add(new Suggestion(nested.toString())); + } + } + return list; + } + + @Override + public Uni ftSugLen(K key) { + return super._ftSugLen(key) + .map(Response::toLong); + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveRedisDataSourceImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveRedisDataSourceImpl.java index cb3430ad1372c..c848575767c21 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveRedisDataSourceImpl.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveRedisDataSourceImpl.java @@ -10,6 +10,7 @@ import java.util.function.Function; import io.quarkus.redis.datasource.ReactiveRedisDataSource; +import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.ReactiveBitMapCommands; import io.quarkus.redis.datasource.bloom.ReactiveBloomCommands; import io.quarkus.redis.datasource.countmin.ReactiveCountMinCommands; @@ -324,6 +325,11 @@ public ReactiveSearchCommands search(Class redisKeyType) { return new ReactiveSearchCommandsImpl<>(this, redisKeyType); } + @Override + public ReactiveAutoSuggestCommands autosuggest(Class redisKeyType) { + return new ReactiveAutoSuggestCommandsImpl<>(this, redisKeyType); + } + @Override public Redis getRedis() { return redis; diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalAutoSuggestCommandsImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalAutoSuggestCommandsImpl.java new file mode 100644 index 0000000000000..0175d3d2f931c --- /dev/null +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalAutoSuggestCommandsImpl.java @@ -0,0 +1,49 @@ +package io.quarkus.redis.runtime.datasource; + +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.datasource.autosuggest.ReactiveTransactionalAutoSuggestCommands; +import io.quarkus.redis.datasource.transactions.ReactiveTransactionalRedisDataSource; +import io.smallrye.mutiny.Uni; +import io.vertx.mutiny.redis.client.Response; + +public class ReactiveTransactionalAutoSuggestCommandsImpl extends AbstractTransactionalCommands + implements ReactiveTransactionalAutoSuggestCommands { + + private final ReactiveAutoSuggestCommandsImpl reactive; + + public ReactiveTransactionalAutoSuggestCommandsImpl(ReactiveTransactionalRedisDataSource ds, + ReactiveAutoSuggestCommandsImpl reactive, TransactionHolder tx) { + super(ds, tx); + this.reactive = reactive; + } + + @Override + public Uni ftSugAdd(K key, String string, double score, boolean increment) { + this.tx.enqueue(Response::toLong); // Uni + return this.reactive._ftSugAdd(key, string, score, increment).invoke(this::queuedOrDiscard).replaceWithVoid(); + } + + @Override + public Uni ftSugDel(K key, String string) { + this.tx.enqueue(Response::toBoolean); // Uni + return this.reactive._ftSugDel(key, string).invoke(this::queuedOrDiscard).replaceWithVoid(); + } + + @Override + public Uni ftSugget(K key, String prefix) { + this.tx.enqueue(r -> reactive.decodeAsListOfSuggestion(r, false)); + return this.reactive._ftSugget(key, prefix).invoke(this::queuedOrDiscard).replaceWithVoid(); + } + + @Override + public Uni ftSugget(K key, String prefix, GetArgs args) { + this.tx.enqueue(r -> reactive.decodeAsListOfSuggestion(r, args.hasScores())); + return this.reactive._ftSugget(key, prefix, args).invoke(this::queuedOrDiscard).replaceWithVoid(); + } + + @Override + public Uni ftSugLen(K key) { + this.tx.enqueue(Response::toLong); + return this.reactive._ftSugLen(key).invoke(this::queuedOrDiscard).replaceWithVoid(); + } +} diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalRedisDataSourceImpl.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalRedisDataSourceImpl.java index 45b09ddade006..c26893cbfd734 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalRedisDataSourceImpl.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/runtime/datasource/ReactiveTransactionalRedisDataSourceImpl.java @@ -5,6 +5,7 @@ import java.util.Arrays; import io.quarkus.redis.datasource.ReactiveRedisDataSource; +import io.quarkus.redis.datasource.autosuggest.ReactiveTransactionalAutoSuggestCommands; import io.quarkus.redis.datasource.bitmap.ReactiveTransactionalBitMapCommands; import io.quarkus.redis.datasource.bloom.ReactiveTransactionalBloomCommands; import io.quarkus.redis.datasource.countmin.ReactiveTransactionalCountMinCommands; @@ -152,6 +153,12 @@ public ReactiveTransactionalSearchCommands search(Class redisKeyType) { (ReactiveSearchCommandsImpl) this.reactive.search(redisKeyType), tx); } + @Override + public ReactiveTransactionalAutoSuggestCommands autosuggest(Class redisKeyType) { + return new ReactiveTransactionalAutoSuggestCommandsImpl<>(this, + (ReactiveAutoSuggestCommandsImpl) this.reactive.autosuggest(redisKeyType), tx); + } + @Override public Uni execute(String command, String... args) { nonNull(command, "command"); diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/AutoSuggestCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/AutoSuggestCommandsTest.java new file mode 100644 index 0000000000000..03eb4506d85e0 --- /dev/null +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/AutoSuggestCommandsTest.java @@ -0,0 +1,90 @@ +package io.quarkus.redis.datasource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import java.time.Duration; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.quarkus.redis.datasource.autosuggest.AutoSuggestCommands; +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.runtime.datasource.BlockingRedisDataSourceImpl; + +/** + * Lots of tests are using await().untilAsserted as the indexing process runs in the background. + */ +@RequiresCommand("ft.create") +public class AutoSuggestCommandsTest extends DatasourceTestBase { + + private RedisDataSource ds; + + private AutoSuggestCommands auto; + + @BeforeEach + void initialize() { + ds = new BlockingRedisDataSourceImpl(vertx, redis, api, Duration.ofSeconds(1)); + auto = ds.autosuggest(); + } + + @AfterEach + void clear() { + ds.flushall(); + } + + @Test + void getDataSource() { + assertThat(ds).isEqualTo(auto.getDataSource()); + } + + @Test + void testSuggestions() { + assertThat(auto.ftSugAdd(key, "hello world", 1)).isEqualTo(1L); + assertThat(auto.ftSugAdd(key, "hello world", 3, true)).isEqualTo(1L); + + assertThat(auto.ftSugAdd(key, "bonjour", 3)).isEqualTo(2L); + assertThat(auto.ftSugAdd(key, "bonjourno", 1)).isEqualTo(3L); + + assertThat(auto.ftSugLen(key)).isEqualTo(3L); + assertThat(auto.ftSugDel(key, "bonjourno")).isTrue(); + assertThat(auto.ftSugDel(key, "missing")).isFalse(); + + assertThat(auto.ftSugLen(key)).isEqualTo(2L); + + assertThat(auto.ftSugAdd(key, "hell", 3)).isEqualTo(3L); + + assertThat(auto.ftSugGet(key, "hell")).hasSize(2) + .anySatisfy(s -> assertThat(s.suggestion()).isEqualTo("hell")) + .anySatisfy(s -> assertThat(s.suggestion()).isEqualTo("hello world")); + + assertThat(auto.ftSugGet(key, "hel", new GetArgs().max(1).withScores())).hasSize(1) + .anySatisfy(s -> { + assertThat(s.suggestion()).isEqualTo("hell"); + assertThat(s.score()).isGreaterThan(0.0); + }); + + assertThat(auto.ftSugAdd(key, "hill", 3)).isEqualTo(4L); + + assertThat(auto.ftSugGet(key, "hell", new GetArgs().fuzzy())).hasSize(3) + .anySatisfy(s -> assertThat(s.suggestion()).isEqualTo("hell")) + .anySatisfy(s -> assertThat(s.suggestion()).isEqualTo("hello world")) + .anySatisfy(s -> assertThat(s.suggestion()).isEqualTo("hill")); + + assertThat(auto.ftSugGet(key, "hell", new GetArgs().fuzzy().withScores())).hasSize(3) + .anySatisfy(s -> { + assertThat(s.suggestion()).isEqualTo("hell"); + assertThat(s.score()).isGreaterThan(0.0); + }) + .anySatisfy(s -> { + assertThat(s.suggestion()).isEqualTo("hello world"); + assertThat(s.score()).isGreaterThan(0.0); + }) + .anySatisfy(s -> { + assertThat(s.suggestion()).isEqualTo("hill"); + assertThat(s.score()).isGreaterThan(0.0); + }); + } + +} diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalAutoSuggestCommandsTest.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalAutoSuggestCommandsTest.java new file mode 100644 index 0000000000000..8e79933d4759d --- /dev/null +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/datasource/TransactionalAutoSuggestCommandsTest.java @@ -0,0 +1,112 @@ +package io.quarkus.redis.datasource; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.quarkus.redis.datasource.autosuggest.GetArgs; +import io.quarkus.redis.datasource.autosuggest.Suggestion; +import io.quarkus.redis.datasource.transactions.TransactionResult; +import io.quarkus.redis.runtime.datasource.BlockingRedisDataSourceImpl; +import io.quarkus.redis.runtime.datasource.ReactiveRedisDataSourceImpl; + +@RequiresCommand("ft.create") +public class TransactionalAutoSuggestCommandsTest extends DatasourceTestBase { + + private RedisDataSource blocking; + private ReactiveRedisDataSource reactive; + + @BeforeEach + void initialize() { + blocking = new BlockingRedisDataSourceImpl(vertx, redis, api, Duration.ofSeconds(60)); + reactive = new ReactiveRedisDataSourceImpl(vertx, redis, api); + } + + @AfterEach + public void clear() { + blocking.flushall(); + } + + @Test + public void autoSuggestBlocking() { + + TransactionResult result = blocking.withTransaction(tx -> { + var auto = tx.autosuggest(); + assertThat(auto.getDataSource()).isEqualTo(tx); + auto.ftSugAdd(key, "abc", 1.0); + auto.ftSugAdd(key, "abcd", 1.0); + auto.ftSugAdd(key, "abcde", 2.0); + + auto.ftSugAdd(key, "boo", 20); + auto.ftSugDel(key, "boo"); + auto.ftSugLen(key); + + auto.ftSugget(key, "abcd"); + auto.ftSugget(key, "ab", new GetArgs().max(1).withScores()); + }); + + assertThat(result.size()).isEqualTo(8); + assertThat(result.discarded()).isFalse(); + assertThat((long) result.get(0)).isEqualTo(1); + assertThat((long) result.get(1)).isEqualTo(2); + assertThat((long) result.get(2)).isEqualTo(3); + assertThat((long) result.get(3)).isEqualTo(4); + assertThat((boolean) result.get(4)).isTrue(); + assertThat((long) result.get(5)).isEqualTo(3); + assertThat((List) result.get(6)).hasSize(2); + assertThat((List) result.get(7)).hasSize(1); + + List sug1 = result.get(6); + assertThat(sug1.stream().map(Suggestion::suggestion).collect(Collectors.toList())).containsExactlyInAnyOrder("abcd", + "abcde"); + List sug2 = result.get(7); + assertThat(sug2.get(0).suggestion()).isEqualTo("abcde"); + assertThat(sug2.get(0).score()).isEqualTo(1.0); + } + + @Test + public void autoSuggestReactive() { + TransactionResult result = reactive.withTransaction(tx -> { + var auto = tx.autosuggest(); + assertThat(auto.getDataSource()).isEqualTo(tx); + var u1 = auto.ftSugAdd(key, "abc", 1.0); + var u2 = auto.ftSugAdd(key, "abcd", 1.0); + var u3 = auto.ftSugAdd(key, "abcde", 2.0); + + var u4 = auto.ftSugAdd(key, "boo", 20); + var u5 = auto.ftSugDel(key, "boo"); + var u6 = auto.ftSugLen(key); + + var u7 = auto.ftSugget(key, "abcd"); + var u8 = auto.ftSugget(key, "ab", new GetArgs().max(1).withScores()); + + return u1.chain(() -> u2).chain(() -> u3).chain(() -> u4).chain(() -> u5) + .chain(() -> u6).chain(() -> u7).chain(() -> u8); + }).await().indefinitely(); + + assertThat(result.size()).isEqualTo(8); + assertThat(result.discarded()).isFalse(); + assertThat((long) result.get(0)).isEqualTo(1); + assertThat((long) result.get(1)).isEqualTo(2); + assertThat((long) result.get(2)).isEqualTo(3); + assertThat((long) result.get(3)).isEqualTo(4); + assertThat((boolean) result.get(4)).isTrue(); + assertThat((long) result.get(5)).isEqualTo(3); + assertThat((List) result.get(6)).hasSize(2); + assertThat((List) result.get(7)).hasSize(1); + + List sug1 = result.get(6); + assertThat(sug1.stream().map(Suggestion::suggestion).collect(Collectors.toList())).containsExactlyInAnyOrder("abcd", + "abcde"); + List sug2 = result.get(7); + assertThat(sug2.get(0).suggestion()).isEqualTo("abcde"); + assertThat(sug2.get(0).score()).isEqualTo(1.0); + } + +} diff --git a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/generator/RedisApiGenerator.java b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/generator/RedisApiGenerator.java index 83a943689f1a6..cd85cb21e7ae4 100644 --- a/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/generator/RedisApiGenerator.java +++ b/extensions/redis-client/runtime/src/test/java/io/quarkus/redis/generator/RedisApiGenerator.java @@ -39,7 +39,7 @@ import io.quarkus.redis.datasource.RedisCommands; import io.quarkus.redis.datasource.RedisDataSource; import io.quarkus.redis.datasource.TransactionalRedisCommands; -import io.quarkus.redis.datasource.search.ReactiveSearchCommands; +import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands; import io.quarkus.redis.datasource.transactions.ReactiveTransactionalRedisDataSource; import io.quarkus.redis.datasource.transactions.TransactionalRedisDataSource; import io.quarkus.redis.runtime.datasource.AbstractRedisCommandGroup; @@ -63,7 +63,7 @@ public class RedisApiGenerator { public static void main(String[] args) throws FileNotFoundException { // PARAMETERS - String reactiveApi = ReactiveSearchCommands.class.getName(); + String reactiveApi = ReactiveAutoSuggestCommands.class.getName(); String prefix = "ft"; // ---------