From 5c211aeb808277c2ccedf17e8684f5140101b62f Mon Sep 17 00:00:00 2001 From: dltmd202 <75921696+Dltmd202@users.noreply.github.com> Date: Mon, 2 Dec 2024 04:11:19 +0900 Subject: [PATCH] Deprecate the STRALGO command and implement the LCS in its place (#3037) * Deprecate the STRALGO command and implement the LCS in its place * Polishing --------- Co-authored-by: Tihomir Mateev --- .../core/AbstractRedisAsyncCommands.java | 5 + .../core/AbstractRedisReactiveCommands.java | 5 + src/main/java/io/lettuce/core/LcsArgs.java | 130 +++++++++++++++++ .../io/lettuce/core/RedisCommandBuilder.java | 9 ++ .../java/io/lettuce/core/StrAlgoArgs.java | 3 + .../api/async/RedisStringAsyncCommands.java | 23 +++ .../reactive/RedisStringReactiveCommands.java | 23 +++ .../core/api/sync/RedisStringCommands.java | 23 +++ .../NodeSelectionStringAsyncCommands.java | 23 +++ .../api/sync/NodeSelectionStringCommands.java | 23 +++ .../core/output/StringMatchResultOutput.java | 3 +- .../lettuce/core/protocol/CommandKeyword.java | 6 +- .../io/lettuce/core/protocol/CommandType.java | 2 +- .../RedisStringCoroutinesCommands.kt | 25 ++-- .../RedisStringCoroutinesCommandsImpl.kt | 2 +- .../lettuce/core/api/RedisStringCommands.java | 22 +++ .../StringCommandIntegrationTests.java | 137 +++++++++--------- 17 files changed, 374 insertions(+), 90 deletions(-) create mode 100644 src/main/java/io/lettuce/core/LcsArgs.java diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 1c2ae525be..8a479e6cc1 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -2412,6 +2412,11 @@ public RedisFuture stralgoLcs(StrAlgoArgs args) { return dispatch(commandBuilder.stralgoLcs(args)); } + @Override + public RedisFuture lcs(LcsArgs args) { + return dispatch(commandBuilder.lcs(args)); + } + @Override public RedisFuture> sunion(K... keys) { return dispatch(commandBuilder.sunion(keys)); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 6d6c82e1ae..0000a01b3d 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -2495,6 +2495,11 @@ public Mono stralgoLcs(StrAlgoArgs strAlgoArgs) { return createMono(() -> commandBuilder.stralgoLcs(strAlgoArgs)); } + @Override + public Mono lcs(LcsArgs lcsArgs) { + return createMono(() -> commandBuilder.lcs(lcsArgs)); + } + @Override public Flux sunion(K... keys) { return createDissolvingFlux(() -> commandBuilder.sunion(keys)); diff --git a/src/main/java/io/lettuce/core/LcsArgs.java b/src/main/java/io/lettuce/core/LcsArgs.java new file mode 100644 index 0000000000..a1b671ad13 --- /dev/null +++ b/src/main/java/io/lettuce/core/LcsArgs.java @@ -0,0 +1,130 @@ +/* + * Copyright 2011-Present, Redis Ltd. and Contributors + * All rights reserved. + * + * Licensed under the MIT License. + */ +package io.lettuce.core; + +import io.lettuce.core.internal.LettuceAssert; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis LCS command. Static import the methods from + * {@link LcsArgs.Builder} and call the methods: {@code keys(…)} . + *

+ * {@link LcsArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Seonghwan Lee + * @since 6.6 + * @see LCS command refference + */ +public class LcsArgs implements CompositeArgument { + + private boolean justLen; + + private int minMatchLen; + + private boolean withMatchLen; + + private boolean withIdx; + + private String[] keys; + + /** + * Builder entry points for {@link LcsArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates new {@link LcsArgs} by keys. + * + * @return new {@link LcsArgs} with {@literal By KEYS} set. + */ + public static LcsArgs keys(String... keys) { + return new LcsArgs().by(keys); + } + + } + + /** + * restrict the list of matches to the ones of a given minimal length. + * + * @return {@code this} {@link LcsArgs}. + */ + public LcsArgs minMatchLen(int minMatchLen) { + this.minMatchLen = minMatchLen; + return this; + } + + /** + * Request just the length of the match for results. + * + * @return {@code this} {@link LcsArgs}. + */ + public LcsArgs justLen() { + justLen = true; + return this; + } + + /** + * Request match len for results. + * + * @return {@code this} {@link LcsArgs}. + */ + public LcsArgs withMatchLen() { + withMatchLen = true; + return this; + } + + /** + * Request match position in each string for results. + * + * @return {@code this} {@link LcsArgs}. + */ + public LcsArgs withIdx() { + withIdx = true; + return this; + } + + public LcsArgs by(String... keys) { + LettuceAssert.notEmpty(keys, "Keys must not be empty"); + + this.keys = keys; + return this; + } + + public boolean isWithIdx() { + return withIdx; + } + + @Override + public void build(CommandArgs args) { + for (String key : keys) { + args.add(key); + } + if (justLen) { + args.add(CommandKeyword.LEN); + } + if (withIdx) { + args.add(CommandKeyword.IDX); + } + + if (minMatchLen > 0) { + args.add(CommandKeyword.MINMATCHLEN); + args.add(minMatchLen); + } + + if (withMatchLen) { + args.add(CommandKeyword.WITHMATCHLEN); + } + } + +} diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index ff403c6dda..aa1ce4435b 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -58,6 +58,7 @@ * @author Mikhael Sokolov * @author Tihomir Mateev * @author Ali Takavci + * @author Seonghwan Lee */ @SuppressWarnings({ "unchecked", "varargs" }) class RedisCommandBuilder extends BaseRedisCommandBuilder { @@ -2912,6 +2913,14 @@ Command stralgoLcs(StrAlgoArgs strAlgoArgs) { return createCommand(STRALGO, new StringMatchResultOutput<>(codec), args); } + Command lcs(LcsArgs lcsArgs) { + LettuceAssert.notNull(lcsArgs, "lcsArgs" + MUST_NOT_BE_NULL); + + CommandArgs args = new CommandArgs<>(codec); + lcsArgs.build(args); + return createCommand(LCS, new StringMatchResultOutput<>(codec), args); + } + Command> sunion(K... keys) { notEmpty(keys); diff --git a/src/main/java/io/lettuce/core/StrAlgoArgs.java b/src/main/java/io/lettuce/core/StrAlgoArgs.java index 2df6daccac..2aed2c9bad 100644 --- a/src/main/java/io/lettuce/core/StrAlgoArgs.java +++ b/src/main/java/io/lettuce/core/StrAlgoArgs.java @@ -32,8 +32,11 @@ * {@link StrAlgoArgs} is a mutable object and instances should be used only once to avoid shared mutable state. * * @author dengliming + * @deprecated As of 6.6 in favor of {@link LcsArgs} + * @see LCS command refference * @since 6.0 */ +@Deprecated public class StrAlgoArgs implements CompositeArgument { private boolean justLen; diff --git a/src/main/java/io/lettuce/core/api/async/RedisStringAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisStringAsyncCommands.java index 1e5b1d3d05..7b524c0925 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisStringAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisStringAsyncCommands.java @@ -27,6 +27,7 @@ import io.lettuce.core.KeyValue; import io.lettuce.core.RedisFuture; import io.lettuce.core.SetArgs; +import io.lettuce.core.LcsArgs; import io.lettuce.core.StrAlgoArgs; import io.lettuce.core.StringMatchResult; import io.lettuce.core.output.KeyValueStreamingChannel; @@ -410,6 +411,8 @@ public interface RedisStringAsyncCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

+ * Command is no longer available in Redis server versions 7.0.x and later. * *

    *
  • Without modifiers the string representing the longest common substring is returned.
  • @@ -422,10 +425,30 @@ public interface RedisStringAsyncCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated RedisFuture stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
      + *
    • Without modifiers, the string representing the longest common substring is returned.
    • + *
    • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
    • + *
    • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
    • + *
    + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + RedisFuture lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisStringReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisStringReactiveCommands.java index 817c235375..e7722b4394 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisStringReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisStringReactiveCommands.java @@ -28,6 +28,7 @@ import io.lettuce.core.KeyValue; import io.lettuce.core.SetArgs; import io.lettuce.core.StrAlgoArgs; +import io.lettuce.core.LcsArgs; import io.lettuce.core.StringMatchResult; import io.lettuce.core.Value; import io.lettuce.core.output.KeyValueStreamingChannel; @@ -414,6 +415,8 @@ public interface RedisStringReactiveCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

    + * Command is no longer available in Redis server versions 7.0.x and later. * *

      *
    • Without modifiers the string representing the longest common substring is returned.
    • @@ -426,10 +429,30 @@ public interface RedisStringReactiveCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated Mono stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
        + *
      • Without modifiers, the string representing the longest common substring is returned.
      • + *
      • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
      • + *
      • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
      • + *
      + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + Mono lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/main/java/io/lettuce/core/api/sync/RedisStringCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisStringCommands.java index 03280bf924..df13a42aef 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisStringCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisStringCommands.java @@ -27,6 +27,7 @@ import io.lettuce.core.KeyValue; import io.lettuce.core.SetArgs; import io.lettuce.core.StrAlgoArgs; +import io.lettuce.core.LcsArgs; import io.lettuce.core.StringMatchResult; import io.lettuce.core.output.KeyValueStreamingChannel; @@ -409,6 +410,8 @@ public interface RedisStringCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

      + * Command is no longer available in Redis server versions 7.0.x and later. * *

        *
      • Without modifiers the string representing the longest common substring is returned.
      • @@ -421,10 +424,30 @@ public interface RedisStringCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated StringMatchResult stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
          + *
        • Without modifiers, the string representing the longest common substring is returned.
        • + *
        • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
        • + *
        • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
        • + *
        + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + StringMatchResult lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionStringAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionStringAsyncCommands.java index 1ee831ab69..93e1701b1f 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionStringAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionStringAsyncCommands.java @@ -28,6 +28,7 @@ import io.lettuce.core.SetArgs; import io.lettuce.core.StrAlgoArgs; import io.lettuce.core.StringMatchResult; +import io.lettuce.core.LcsArgs; import io.lettuce.core.output.KeyValueStreamingChannel; /** @@ -409,6 +410,8 @@ public interface NodeSelectionStringAsyncCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

        + * Command is no longer available in Redis server versions 7.0.x and later. * *

          *
        • Without modifiers the string representing the longest common substring is returned.
        • @@ -421,10 +424,30 @@ public interface NodeSelectionStringAsyncCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated AsyncExecutions stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
            + *
          • Without modifiers, the string representing the longest common substring is returned.
          • + *
          • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
          • + *
          • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
          • + *
          + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + AsyncExecutions lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionStringCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionStringCommands.java index 47c80b004d..dcf3f27acc 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionStringCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionStringCommands.java @@ -28,6 +28,7 @@ import io.lettuce.core.SetArgs; import io.lettuce.core.StrAlgoArgs; import io.lettuce.core.StringMatchResult; +import io.lettuce.core.LcsArgs; import io.lettuce.core.output.KeyValueStreamingChannel; /** @@ -409,6 +410,8 @@ public interface NodeSelectionStringCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

          + * Command is no longer available in Redis server versions 7.0.x and later. * *

            *
          • Without modifiers the string representing the longest common substring is returned.
          • @@ -421,10 +424,30 @@ public interface NodeSelectionStringCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated Executions stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
              + *
            • Without modifiers, the string representing the longest common substring is returned.
            • + *
            • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
            • + *
            • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
            • + *
            + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + Executions lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/main/java/io/lettuce/core/output/StringMatchResultOutput.java b/src/main/java/io/lettuce/core/output/StringMatchResultOutput.java index 2217217139..62c6f35aaa 100644 --- a/src/main/java/io/lettuce/core/output/StringMatchResultOutput.java +++ b/src/main/java/io/lettuce/core/output/StringMatchResultOutput.java @@ -21,6 +21,7 @@ import io.lettuce.core.StringMatchResult; import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.protocol.CommandKeyword; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -38,7 +39,7 @@ */ public class StringMatchResultOutput extends CommandOutput { - private static final ByteBuffer LEN = StandardCharsets.US_ASCII.encode("len"); + private static final ByteBuffer LEN = StandardCharsets.US_ASCII.encode(CommandKeyword.LEN.toString().toLowerCase()); private String matchString; diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index 8cd1f0f9d3..597816edfe 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -37,11 +37,11 @@ public enum CommandKeyword implements ProtocolKeyword { BY, BYLEX, BYSCORE, CACHING, CAT, CH, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DELSLOTSRANGE, DELUSER, DESC, DRYRUN, SOFT, HARD, ENCODING, - FAILOVER, FORGET, FIELDS, FLAGS, FLUSH, FORCE, FREQ, FLUSHSLOTS, GENPASS, GETNAME, GETUSER, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, INFO, + FAILOVER, FORGET, FIELDS, FLAGS, FLUSH, FORCE, FREQ, FLUSHSLOTS, GENPASS, GETNAME, GETUSER, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, IDX, INFO, IDLETIME, JUSTID, KILL, KEYSLOT, LEFT, LEN, LIMIT, LINKS, LIST, LOAD, LOG, MATCH, - MAX, MAXLEN, MEET, MIN, MINID, MOVED, NO, NOACK, NOCOMMANDS, NODE, NODES, NOMKSTREAM, NOPASS, NOSAVE, NOT, NOVALUES, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NUMPAT, NX, OFF, ON, ONE, OR, PAUSE, PREFIXES, + MAX, MAXLEN, MEET, MIN, MINID, MINMATCHLEN, MOVED, NO, NOACK, NOCOMMANDS, NODE, NODES, NOMKSTREAM, NOPASS, NOSAVE, NOT, NOVALUES, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NUMPAT, NX, OFF, ON, ONE, OR, PAUSE, PREFIXES, REFCOUNT, REMOVE, RELOAD, REPLACE, REDIRECT, REPLICATE, REPLICAS, REV, RESET, RESETCHANNELS, RESETKEYS, RESETPASS, @@ -49,7 +49,7 @@ public enum CommandKeyword implements ProtocolKeyword { MIGRATING, IMPORTING, SAVE, SKIPME, SLAVES, STREAM, STORE, SUM, SEGFAULT, SETUSER, TAKEOVER, TRACKING, TRACKINGINFO, TYPE, UNBLOCK, USERS, USAGE, WEIGHTS, WHOAMI, - WITHSCORE, WITHSCORES, WITHVALUES, XOR, XX, YES, INDENT, NEWLINE, SPACE; + WITHMATCHLEN, WITHSCORE, WITHSCORES, WITHVALUES, XOR, XX, YES, INDENT, NEWLINE, SPACE; public final byte[] bytes; diff --git a/src/main/java/io/lettuce/core/protocol/CommandType.java b/src/main/java/io/lettuce/core/protocol/CommandType.java index 7eb949e5d3..9a2fcf83f6 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandType.java +++ b/src/main/java/io/lettuce/core/protocol/CommandType.java @@ -50,7 +50,7 @@ public enum CommandType implements ProtocolKeyword { // String - APPEND, GET, GETDEL, GETEX, GETRANGE, GETSET, MGET, MSET, MSETNX, SET, SETEX, PSETEX, SETNX, SETRANGE, STRLEN, STRALGO, + APPEND, GET, GETDEL, GETEX, GETRANGE, GETSET, MGET, MSET, MSETNX, SET, SETEX, PSETEX, SETNX, SETRANGE, STRLEN, STRALGO, LCS, // Numeric diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommands.kt index 72df1f67fb..f6500da836 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommands.kt @@ -1,5 +1,5 @@ /* - * Copyright 2020-Present, Redis Ltd. and Contributors + * Copyright 2017-Present, Redis Ltd. and Contributors * All rights reserved. * * Licensed under the MIT License. @@ -392,23 +392,22 @@ interface RedisStringCoroutinesCommands { suspend fun setrange(key: K, offset: Long, value: V): Long? /** - * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest - * common substring). + * The LCS command implements the longest common subsequence algorithm. * *
              - *
            • Without modifiers the string representing the longest common substring is returned.
            • - *
            • When [StrAlgoArgs#justLen LEN] is given the command returns the length of the longest common substring.
            • - *
            • When [StrAlgoArgs#withIdx IDX] is given the command returns an array with the LCS length and all the ranges - * in both the strings, start and end offset for each string, where there are matches. When - * [StrAlgoArgs#withMatchLen WITHMATCHLEN] is given each array representing a match will also have the length of the - * match.
            • + *
            • Without modifiers, the string representing the longest common substring is returned.
            • + *
            • When [LcsArgs#justLen LEN] is given the command returns the length of the longest common substring.
            • + *
            • When [LcsArgs#withIdx IDX] is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When [LcsArgs#withMatchLen + * WITHMATCHLEN] is given each array representing a match will also have the length of the match.
            • *
            * - * @param strAlgoArgs command arguments. - * @return StringMatchResult. - * @since 6.0 + * @param lcsArgs command arguments supplied by the [LcsArgs]. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 */ - suspend fun stralgoLcs(strAlgoArgs: StrAlgoArgs): StringMatchResult? + suspend fun lcs(lcsArgs: LcsArgs): StringMatchResult? /** * Get the length of the value stored in a key. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommandsImpl.kt index 86586c6899..8bfbb56d4d 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisStringCoroutinesCommandsImpl.kt @@ -116,7 +116,7 @@ internal class RedisStringCoroutinesCommandsImpl(internal val override suspend fun setrange(key: K, offset: Long, value: V): Long? = ops.setrange(key, offset, value).awaitFirstOrNull() - override suspend fun stralgoLcs(strAlgoArgs: StrAlgoArgs): StringMatchResult? = ops.stralgoLcs(strAlgoArgs).awaitFirstOrNull() + override suspend fun lcs(lcsArgs: LcsArgs): StringMatchResult? = ops.lcs(lcsArgs).awaitFirstOrNull() override suspend fun strlen(key: K): Long? = ops.strlen(key).awaitFirstOrNull() diff --git a/src/main/templates/io/lettuce/core/api/RedisStringCommands.java b/src/main/templates/io/lettuce/core/api/RedisStringCommands.java index 05f6264228..02a8a2562a 100644 --- a/src/main/templates/io/lettuce/core/api/RedisStringCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisStringCommands.java @@ -403,6 +403,8 @@ public interface RedisStringCommands { /** * The STRALGO command implements complex algorithms that operate on strings. This method uses the LCS algorithm (longest * common substring). + *

            + * Command is no longer available in Redis server versions 7.0.x and later. * *

              *
            • Without modifiers the string representing the longest common substring is returned.
            • @@ -415,10 +417,30 @@ public interface RedisStringCommands { * * @param strAlgoArgs command arguments. * @return StringMatchResult. + * @deprecated since 6.6 in favor of {@link #lcs(LcsArgs)}. * @since 6.0 */ + @Deprecated StringMatchResult stralgoLcs(StrAlgoArgs strAlgoArgs); + /** + * The LCS command implements the longest common subsequence algorithm. + * + *
                + *
              • Without modifiers, the string representing the longest common substring is returned.
              • + *
              • When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.
              • + *
              • When {@link LcsArgs#withIdx() IDX} is given the command returns an array with the LCS length and all the ranges in + * both the strings, start and end offset for each string, where there are matches. When {@link LcsArgs#withMatchLen() + * WITHMATCHLEN} is given each array representing a match will also have the length of the match.
              • + *
              + * + * @param lcsArgs command arguments supplied by the {@link LcsArgs}. + * @return StringMatchResult + * @see LCS command refference + * @since 6.6 + */ + StringMatchResult lcs(LcsArgs lcsArgs); + /** * Get the length of the value stored in a key. * diff --git a/src/test/java/io/lettuce/core/commands/StringCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/StringCommandIntegrationTests.java index b32a9beb86..a90c3c27ad 100644 --- a/src/test/java/io/lettuce/core/commands/StringCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/StringCommandIntegrationTests.java @@ -68,6 +68,10 @@ public class StringCommandIntegrationTests extends TestSupport { private final RedisCommands redis; + protected final String KEY_1 = "key1{k}"; + + protected final String KEY_2 = "key2{k}"; + @Inject protected StringCommandIntegrationTests(RedisCommands redis) { this.redis = redis; @@ -328,10 +332,10 @@ void strAlgo() { @EnabledOnCommand("STRALGO") void strAlgoUsingKeys() { - redis.set("key1{k}", "ohmytext"); - redis.set("key2{k}", "mynewtext"); + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); - StringMatchResult matchResult = redis.stralgoLcs(StrAlgoArgs.Builder.keys("key1{k}", "key2{k}")); + StringMatchResult matchResult = redis.stralgoLcs(StrAlgoArgs.Builder.keys(KEY_1, KEY_2)); assertThat(matchResult.getMatchString()).isEqualTo("mytext"); // STRALGO LCS STRINGS a b @@ -382,114 +386,105 @@ void strAlgoWithIdx() { @Test @EnabledOnCommand("LCS") void lcs() { - redis.set("key1", "ohmytext"); - redis.set("key2", "mynewtext"); + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); - // LCS key1 key2 - CustomStringCommands commands = CustomStringCommands.instance(getConnection()); - StringMatchResult matchResult = commands.lcs("key1", "key2"); + // > LCS key1 key2 + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys(KEY_1, KEY_2)); assertThat(matchResult.getMatchString()).isEqualTo("mytext"); - - // LCS a b IDX MINMATCHLEN 4 WITHMATCHLEN - // Keys don't exist. - matchResult = commands.lcsMinMatchLenWithMatchLen("a", "b", 4); - assertThat(matchResult.getMatchString()).isNullOrEmpty(); + assertThat(matchResult.getMatches().size()).isEqualTo(0); assertThat(matchResult.getLen()).isEqualTo(0); + } @Test @EnabledOnCommand("LCS") - void lcsUsingKeys() { + void lcsNonExistantKeys() { - redis.set("key1{k}", "ohmytext"); - redis.set("key2{k}", "mynewtext"); - - CustomStringCommands commands = CustomStringCommands.instance(getConnection()); - - StringMatchResult matchResult = commands.lcs("key1{k}", "key2{k}"); - assertThat(matchResult.getMatchString()).isEqualTo("mytext"); - - // STRALGO LCS STRINGS a b - matchResult = commands.lcsMinMatchLenWithMatchLen("a", "b", 4); + // > LCS a b IDX MINMATCHLEN 4 WITHMATCHLEN + // Keys don't exist. + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("a{k}", "b{k}").minMatchLen(4).withMatchLen()); assertThat(matchResult.getMatchString()).isNullOrEmpty(); + assertThat(matchResult.getMatches()).isNullOrEmpty(); assertThat(matchResult.getLen()).isEqualTo(0); } @Test @EnabledOnCommand("LCS") void lcsJustLen() { - redis.set("one", "ohmytext"); - redis.set("two", "mynewtext"); - - CustomStringCommands commands = CustomStringCommands.instance(getConnection()); - - StringMatchResult matchResult = commands.lcsLen("one", "two"); + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); + // > LCS key1 key2 LEN + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys(KEY_1, KEY_2).justLen()); assertThat(matchResult.getLen()).isEqualTo(6); + assertThat(matchResult.getMatchString()).isNullOrEmpty(); + assertThat(matchResult.getMatches()).isNullOrEmpty(); } @Test @EnabledOnCommand("LCS") - void lcsWithMinMatchLen() { - redis.set("key1", "ohmytext"); - redis.set("key2", "mynewtext"); + void lcsIdx() { + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); - CustomStringCommands commands = CustomStringCommands.instance(getConnection()); + // > LCS key1 key2 IDX + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys(KEY_1, KEY_2).withIdx()); - StringMatchResult matchResult = commands.lcsMinMatchLen("key1", "key2", 4); + assertThat(matchResult.getMatches().size()).isEqualTo(2); + assertThat(matchResult.getMatches().get(0).getA().getStart()).isEqualTo(4); + assertThat(matchResult.getMatches().get(0).getA().getEnd()).isEqualTo(7); + assertThat(matchResult.getMatches().get(0).getB().getStart()).isEqualTo(5); + assertThat(matchResult.getMatches().get(0).getB().getEnd()).isEqualTo(8); - assertThat(matchResult.getMatchString()).isEqualTo("mytext"); + assertThat(matchResult.getMatches().get(1).getA().getStart()).isEqualTo(2); + assertThat(matchResult.getMatches().get(1).getA().getEnd()).isEqualTo(3); + assertThat(matchResult.getMatches().get(1).getB().getStart()).isEqualTo(0); + assertThat(matchResult.getMatches().get(1).getB().getEnd()).isEqualTo(1); + + assertThat(matchResult.getLen()).isEqualTo(6); + + assertThat(matchResult.getMatchString()).isNullOrEmpty(); } @Test @EnabledOnCommand("LCS") - void lcsMinMatchLenIdxMatchLen() { - redis.set("key1", "ohmytext"); - redis.set("key2", "mynewtext"); - - CustomStringCommands commands = CustomStringCommands.instance(getConnection()); + void lcsWithMinMatchLen() { + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); - // LCS key1 key2 IDX MINMATCHLEN 4 WITHMATCHLEN - StringMatchResult matchResult = commands.lcsMinMatchLenWithMatchLen("key1", "key2", 4); + // > LCS key1 key2 IDX MINMATCHLEN 4 + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys(KEY_1, KEY_2).withIdx().minMatchLen(4)); - assertThat(matchResult.getMatches()).hasSize(1); - assertThat(matchResult.getMatches().get(0).getMatchLen()).isEqualTo(4); + assertThat(matchResult.getMatches().get(0).getA().getStart()).isEqualTo(4); + assertThat(matchResult.getMatches().get(0).getA().getEnd()).isEqualTo(7); + assertThat(matchResult.getMatches().get(0).getB().getStart()).isEqualTo(5); + assertThat(matchResult.getMatches().get(0).getB().getEnd()).isEqualTo(8); - Position a = matchResult.getMatches().get(0).getA(); - Position b = matchResult.getMatches().get(0).getB(); - - assertThat(a.getStart()).isEqualTo(4); - assertThat(a.getEnd()).isEqualTo(7); - assertThat(b.getStart()).isEqualTo(5); - assertThat(b.getEnd()).isEqualTo(8); assertThat(matchResult.getLen()).isEqualTo(6); - } - protected StatefulConnection getConnection() { - StatefulRedisConnection src = redis.getStatefulConnection(); - Assumptions.assumeFalse(Proxy.isProxyClass(src.getClass()), "Redis connection is proxy, skipping."); - return src; + assertThat(matchResult.getMatchString()).isNullOrEmpty(); } - private interface CustomStringCommands extends Commands { - - @Command("LCS :k1 :k2") - StringMatchResult lcs(@Param("k1") String k1, @Param("k2") String k2); + @Test + @EnabledOnCommand("LCS") + void lcsMinMatchLenIdxMatchLen() { + redis.set(KEY_1, "ohmytext"); + redis.set(KEY_2, "mynewtext"); - @Command("LCS :k1 :k2 LEN") - StringMatchResult lcsLen(@Param("k1") String k1, @Param("k2") String k2); + // > LCS key1 key2 IDX MINMATCHLEN 4 WITHMATCHLEN + StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys(KEY_1, KEY_2).minMatchLen(4).withMatchLen().withIdx()); - @Command("LCS :k1 :k2 MINMATCHLEN :mml") - StringMatchResult lcsMinMatchLen(@Param("k1") String k1, @Param("k2") String k2, @Param("mml") int mml); + assertThat(matchResult.getMatches().get(0).getA().getStart()).isEqualTo(4); + assertThat(matchResult.getMatches().get(0).getA().getEnd()).isEqualTo(7); + assertThat(matchResult.getMatches().get(0).getB().getStart()).isEqualTo(5); + assertThat(matchResult.getMatches().get(0).getB().getEnd()).isEqualTo(8); - @Command("LCS :k1 :k2 IDX MINMATCHLEN :mml WITHMATCHLEN") - StringMatchResult lcsMinMatchLenWithMatchLen(@Param("k1") String k1, @Param("k2") String k2, @Param("mml") int mml); + assertThat(matchResult.getMatches().get(0).getMatchLen()).isEqualTo(4); - static CustomStringCommands instance(StatefulConnection conn) { - RedisCommandFactory factory = new RedisCommandFactory(conn); - return factory.getCommands(CustomStringCommands.class); - } + assertThat(matchResult.getLen()).isEqualTo(6); + assertThat(matchResult.getMatchString()).isNullOrEmpty(); } }