Skip to content

Commit

Permalink
Deprecate the STRALGO command and implement the LCS in its place
Browse files Browse the repository at this point in the history
  • Loading branch information
Dltmd202 authored and 이승환 committed Nov 3, 2024
1 parent 114755d commit 48dfeb9
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 49 deletions.
5 changes: 5 additions & 0 deletions src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,11 @@ public RedisFuture<StringMatchResult> stralgoLcs(StrAlgoArgs args) {
return dispatch(commandBuilder.stralgoLcs(args));
}

@Override
public RedisFuture<StringMatchResult> lcs(LcsArgs args) {
return dispatch(commandBuilder.lcs(args));
}

@Override
public RedisFuture<Set<V>> sunion(K... keys) {
return dispatch(commandBuilder.sunion(keys));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2499,6 +2499,11 @@ public Mono<StringMatchResult> stralgoLcs(StrAlgoArgs strAlgoArgs) {
return createMono(() -> commandBuilder.stralgoLcs(strAlgoArgs));
}

@Override
public Mono<StringMatchResult> lcs(LcsArgs lcsArgs) {
return createMono(() -> commandBuilder.lcs(lcsArgs));
}

@Override
public Flux<V> sunion(K... keys) {
return createDissolvingFlux(() -> commandBuilder.sunion(keys));
Expand Down
141 changes: 141 additions & 0 deletions src/main/java/io/lettuce/core/LcsArgs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2011-Present, Redis Ltd. and Contributors
* All rights reserved.
*
* Licensed under the MIT License.
*
* This file contains contributions from third-party contributors
* licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.lettuce.core;

import io.lettuce.core.internal.LettuceAssert;
import io.lettuce.core.protocol.CommandArgs;

/**
* Argument list builder for the Redis <a href="https://redis.io/commands/lcs">LCS</a> command. Static import the methods from
* {@link LcsArgs.Builder} and call the methods: {@code keys(…)} .
* <p>
* {@link LcsArgs} is a mutable object and instances should be used only once to avoid shared mutable state.
*
* @author Dltmd202
* @since 7.0
*/
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 strings 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 <K, V> void build(CommandArgs<K, V> args) {
for (String key : keys) {
args.add(key);
}
if (justLen) {
args.add("LEN");
}
if (withIdx) {
args.add("IDX");
}

if (minMatchLen > 0) {
args.add("MINMATCHLEN");
args.add(minMatchLen);
}

if (withMatchLen) {
args.add("WITHMATCHLEN");
}
}

}
8 changes: 8 additions & 0 deletions src/main/java/io/lettuce/core/RedisCommandBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2912,6 +2912,14 @@ Command<K, V, StringMatchResult> stralgoLcs(StrAlgoArgs strAlgoArgs) {
return createCommand(STRALGO, new StringMatchResultOutput<>(codec), args);
}

Command<K, V, StringMatchResult> lcs(LcsArgs lcsArgs) {
LettuceAssert.notNull(lcsArgs, "lcsArgs" + MUST_NOT_BE_NULL);

CommandArgs<K, V> args = new CommandArgs<>(codec);
lcsArgs.build(args);
return createCommand(LCS, new StringMatchResultOutput<>(codec), args);
}

Command<K, V, Set<V>> sunion(K... keys) {
notEmpty(keys);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -424,8 +425,26 @@ public interface RedisStringAsyncCommands<K, V> {
* @return StringMatchResult.
* @since 6.0
*/
@Deprecated
RedisFuture<StringMatchResult> stralgoLcs(StrAlgoArgs strAlgoArgs);

/**
* The LCS command implements the longest common subsequence algorithm.
*
* <ul>
* <li>Without modifiers the string representing the longest common substring is returned.</li>
* <li>When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.</li>
* <li>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.</li>
* </ul>
*
* @param lcsArgs command arguments.
* @return StringMatchResult.
* @since 7.0
*/
RedisFuture<StringMatchResult> lcs(LcsArgs lcsArgs);

/**
* Get the length of the value stored in a key.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -428,8 +429,26 @@ public interface RedisStringReactiveCommands<K, V> {
* @return StringMatchResult.
* @since 6.0
*/
@Deprecated
Mono<StringMatchResult> stralgoLcs(StrAlgoArgs strAlgoArgs);

/**
* The LCS command implements the longest common subsequence algorithm.
*
* <ul>
* <li>Without modifiers the string representing the longest common substring is returned.</li>
* <li>When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.</li>
* <li>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.</li>
* </ul>
*
* @param lcsArgs command arguments.
* @return StringMatchResult.
* @since 7.0
*/
Mono<StringMatchResult> lcs(LcsArgs lcsArgs);

/**
* Get the length of the value stored in a key.
*
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/io/lettuce/core/api/sync/RedisStringCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -423,8 +424,26 @@ public interface RedisStringCommands<K, V> {
* @return StringMatchResult.
* @since 6.0
*/
@Deprecated
StringMatchResult stralgoLcs(StrAlgoArgs strAlgoArgs);

/**
* The LCS command implements the longest common subsequence algorithm.
*
* <ul>
* <li>Without modifiers the string representing the longest common substring is returned.</li>
* <li>When {@link LcsArgs#justLen() LEN} is given the command returns the length of the longest common substring.</li>
* <li>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.</li>
* </ul>
*
* @param lcsArgs command arguments.
* @return StringMatchResult.
* @since 7.0
*/
StringMatchResult lcs(LcsArgs lcsArgs);

/**
* Get the length of the value stored in a key.
*
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/lettuce/core/protocol/CommandType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,34 +385,21 @@ void lcs() {
redis.set("key1", "ohmytext");
redis.set("key2", "mynewtext");

// LCS key1 key2
CustomStringCommands commands = CustomStringCommands.instance(getConnection());
StringMatchResult matchResult = commands.lcs("key1", "key2");
assertThat(matchResult.getMatchString()).isEqualTo("mytext");
// LCS key1 key2 IDX MINMATCHLEN 4 WITHMATCHLEN
StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("key1", "key2").minMatchLen(4).withIdx().withMatchLen());

// LCS a b IDX MINMATCHLEN 4 WITHMATCHLEN
// Keys don't exist.
matchResult = commands.lcsMinMatchLenWithMatchLen("a", "b", 4);
assertThat(matchResult.getMatchString()).isNullOrEmpty();
assertThat(matchResult.getLen()).isEqualTo(0);
assertThat(matchResult.getMatchString()).isEqualTo("mytext");
}

@Test
@EnabledOnCommand("LCS")
void lcsUsingKeys() {

redis.set("key1{k}", "ohmytext");
redis.set("key2{k}", "mynewtext");

CustomStringCommands commands = CustomStringCommands.instance(getConnection());
StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("key1{k}", "key2{k}"));

StringMatchResult matchResult = commands.lcs("key1{k}", "key2{k}");
assertThat(matchResult.getMatchString()).isEqualTo("mytext");

// STRALGO LCS STRINGS a b
matchResult = commands.lcsMinMatchLenWithMatchLen("a", "b", 4);
assertThat(matchResult.getMatchString()).isNullOrEmpty();
assertThat(matchResult.getLen()).isEqualTo(0);
}

@Test
Expand All @@ -421,9 +408,7 @@ void lcsJustLen() {
redis.set("one", "ohmytext");
redis.set("two", "mynewtext");

CustomStringCommands commands = CustomStringCommands.instance(getConnection());

StringMatchResult matchResult = commands.lcsLen("one", "two");
StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("one", "two").justLen());

assertThat(matchResult.getLen()).isEqualTo(6);
}
Expand All @@ -434,9 +419,7 @@ void lcsWithMinMatchLen() {
redis.set("key1", "ohmytext");
redis.set("key2", "mynewtext");

CustomStringCommands commands = CustomStringCommands.instance(getConnection());

StringMatchResult matchResult = commands.lcsMinMatchLen("key1", "key2", 4);
StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("key1", "key2").minMatchLen(4));

assertThat(matchResult.getMatchString()).isEqualTo("mytext");
}
Expand All @@ -447,10 +430,7 @@ void lcsMinMatchLenIdxMatchLen() {
redis.set("key1", "ohmytext");
redis.set("key2", "mynewtext");

CustomStringCommands commands = CustomStringCommands.instance(getConnection());

// LCS key1 key2 IDX MINMATCHLEN 4 WITHMATCHLEN
StringMatchResult matchResult = commands.lcsMinMatchLenWithMatchLen("key1", "key2", 4);
StringMatchResult matchResult = redis.lcs(LcsArgs.Builder.keys("key1", "key2").minMatchLen(4).withMatchLen());

assertThat(matchResult.getMatches()).hasSize(1);
assertThat(matchResult.getMatches().get(0).getMatchLen()).isEqualTo(4);
Expand All @@ -471,25 +451,4 @@ protected StatefulConnection<String, String> getConnection() {
return src;
}

private interface CustomStringCommands extends Commands {

@Command("LCS :k1 :k2")
StringMatchResult lcs(@Param("k1") String k1, @Param("k2") String k2);

@Command("LCS :k1 :k2 LEN")
StringMatchResult lcsLen(@Param("k1") String k1, @Param("k2") String k2);

@Command("LCS :k1 :k2 MINMATCHLEN :mml")
StringMatchResult lcsMinMatchLen(@Param("k1") String k1, @Param("k2") String k2, @Param("mml") int mml);

@Command("LCS :k1 :k2 IDX MINMATCHLEN :mml WITHMATCHLEN")
StringMatchResult lcsMinMatchLenWithMatchLen(@Param("k1") String k1, @Param("k2") String k2, @Param("mml") int mml);

static CustomStringCommands instance(StatefulConnection<String, String> conn) {
RedisCommandFactory factory = new RedisCommandFactory(conn);
return factory.getCommands(CustomStringCommands.class);
}

}

}

0 comments on commit 48dfeb9

Please sign in to comment.