Skip to content

Commit

Permalink
Java: Adding command LCS (No IDX option) (#351)
Browse files Browse the repository at this point in the history
* Java: Adding command LCS (default & LEN option)
  • Loading branch information
tjzhang-BQ authored Jun 12, 2024
1 parent 24fb145 commit 562c53b
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 18 deletions.
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ enum RequestType {
SInterCard = 175;
XRevRange = 176;
Copy = 178;
LCS = 181;
}

message Command {
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ pub enum RequestType {
SInterCard = 175,
XRevRange = 176,
Copy = 178,
LCS = 181,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -369,6 +370,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::Copy => RequestType::Copy,
ProtobufRequestType::Sort => RequestType::Sort,
ProtobufRequestType::XRevRange => RequestType::XRevRange,
ProtobufRequestType::LCS => RequestType::LCS,
}
}
}
Expand Down Expand Up @@ -551,6 +553,7 @@ impl RequestType {
RequestType::Copy => Some(cmd("COPY")),
RequestType::Sort => Some(cmd("SORT")),
RequestType::XRevRange => Some(cmd("XREVRANGE")),
RequestType::LCS => Some(cmd("LCS")),
}
}
}
13 changes: 13 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Incr;
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.LCS;
import static redis_request.RedisRequestOuterClass.RequestType.LIndex;
import static redis_request.RedisRequestOuterClass.RequestType.LInsert;
import static redis_request.RedisRequestOuterClass.RequestType.LLen;
Expand Down Expand Up @@ -1797,4 +1798,16 @@ public CompletableFuture<Boolean> copy(@NonNull String source, @NonNull String d
String[] arguments = new String[] {source, destination};
return commandManager.submitNewCommand(Copy, arguments, this::handleBooleanResponse);
}

@Override
public CompletableFuture<String> lcs(@NonNull String key1, @NonNull String key2) {
String[] arguments = new String[] {key1, key2};
return commandManager.submitNewCommand(LCS, arguments, this::handleStringResponse);
}

@Override
public CompletableFuture<Long> lcsLen(@NonNull String key1, @NonNull String key2) {
String[] arguments = new String[] {key1, key2, LEN_REDIS_API};
return commandManager.submitNewCommand(LCS, arguments, this::handleLongResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
public interface StringBaseCommands {

/** Redis API keyword used to indicate that the length of the lcs should be returned. */
public static final String LEN_REDIS_API = "LEN";

/**
* Gets the value associated with the given <code>key</code>, or <code>null</code> if no such
* value exists.
Expand Down Expand Up @@ -316,4 +319,46 @@ public interface StringBaseCommands {
* }</pre>
*/
CompletableFuture<Long> append(String key, String value);

/**
* Returns the longest common subsequence between strings stored at <code>key1</code> and <code>
* key2</code>.
*
* @since Redis 7.0 and above.
* @apiNote When in cluster mode, <code>key1</code> and <code>key2</code> must map to the same
* hash slot.
* @see <a href="https://valkey.io/commands/lcs/">valkey.io</a> for details.
* @param key1 The key that stores the first string.
* @param key2 The key that stores the second string.
* @return A <code>String</code> containing the longest common subsequence between the 2 strings.
* An empty <code>String</code> is returned if the keys do not exist or have no common
* subsequences.
* @example
* <pre>{@code
* // testKey1 = abcd, testKey2 = axcd
* String result = client.lcs("testKey1", "testKey2").get();
* assert result.equals("acd");
* }</pre>
*/
CompletableFuture<String> lcs(String key1, String key2);

/**
* Returns the length of the longest common subsequence between strings stored at <code>key1
* </code> and <code>key2</code>.
*
* @since Redis 7.0 and above.
* @apiNote When in cluster mode, <code>key1</code> and <code>key2</code> must map to the same
* hash slot.
* @see <a href="https://valkey.io/commands/lcs/">valkey.io</a> for details.
* @param key1 The key that stores the first string.
* @param key2 The key that stores the second string.
* @return The length of the longest common subsequence between the 2 strings.
* @example
* <pre>{@code
* // testKey1 = abcd, testKey2 = axcd
* Long result = client.lcs("testKey1", "testKey2").get();
* assert result.equals(3L);
* }</pre>
*/
CompletableFuture<Long> lcsLen(String key1, String key2);
}
37 changes: 37 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static glide.api.commands.SortedSetBaseCommands.LIMIT_REDIS_API;
import static glide.api.commands.SortedSetBaseCommands.WITH_SCORES_REDIS_API;
import static glide.api.commands.SortedSetBaseCommands.WITH_SCORE_REDIS_API;
import static glide.api.commands.StringBaseCommands.LEN_REDIS_API;
import static glide.api.models.commands.RangeOptions.createZRangeArgs;
import static glide.api.models.commands.bitmap.BitFieldOptions.createBitFieldArgs;
import static glide.api.models.commands.function.FunctionListOptions.LIBRARY_NAME_REDIS_API;
Expand Down Expand Up @@ -81,6 +82,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LCS;
import static redis_request.RedisRequestOuterClass.RequestType.LIndex;
import static redis_request.RedisRequestOuterClass.RequestType.LInsert;
import static redis_request.RedisRequestOuterClass.RequestType.LLen;
Expand Down Expand Up @@ -4309,6 +4311,41 @@ public T functionDelete(@NonNull String libName) {
return getThis();
}

/**
* Returns the longest common subsequence between strings stored at <code>key1</code> and <code>
* key2</code>.
*
* @since Redis 7.0 and above.
* @see <a href="https://valkey.io/commands/lcs/">valkey.io</a> for details.
* @param key1 The key that stores the first string.
* @param key2 The key that stores the second string.
* @return Command Response - A <code>String</code> containing the longest common subsequence
* between the 2 strings. An empty <code>String</code> is returned if the keys do not exist or
* have no common subsequences.
*/
public T lcs(@NonNull String key1, @NonNull String key2) {
protobufTransaction.addCommands(buildCommand(LCS, buildArgs(key1, key2)));
return getThis();
}

/**
* Returns the length of the longest common subsequence between strings stored at <code>key1
* </code> and <code>key2</code>.
*
* @since Redis 7.0 and above.
* @apiNote When in cluster mode, <code>key1</code> and <code>key2</code> must map to the same
* hash slot.
* @see <a href="https://valkey.io/commands/lcs/">valkey.io</a> for details.
* @param key1 The key that stores the first string.
* @param key2 The key that stores the second string.
* @return Command Response - The length of the longest common subsequence between the 2 strings.
*/
public T lcsLen(@NonNull String key1, @NonNull String key2) {
ArgsArray args = buildArgs(key1, key2, LEN_REDIS_API);
protobufTransaction.addCommands(buildCommand(LCS, args));
return getThis();
}

/** Build protobuf {@link Command} object for given command and arguments. */
protected Command buildCommand(RequestType requestType) {
return buildCommand(requestType, buildArgs());
Expand Down
52 changes: 52 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static glide.api.commands.SortedSetBaseCommands.LIMIT_REDIS_API;
import static glide.api.commands.SortedSetBaseCommands.WITH_SCORES_REDIS_API;
import static glide.api.commands.SortedSetBaseCommands.WITH_SCORE_REDIS_API;
import static glide.api.commands.StringBaseCommands.LEN_REDIS_API;
import static glide.api.models.commands.FlushMode.ASYNC;
import static glide.api.models.commands.FlushMode.SYNC;
import static glide.api.models.commands.LInsertOptions.InsertPosition.BEFORE;
Expand Down Expand Up @@ -113,6 +114,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LCS;
import static redis_request.RedisRequestOuterClass.RequestType.LIndex;
import static redis_request.RedisRequestOuterClass.RequestType.LInsert;
import static redis_request.RedisRequestOuterClass.RequestType.LLen;
Expand Down Expand Up @@ -5988,4 +5990,54 @@ public void copy_with_destinationDB_returns_success() {
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void lcs() {
// setup
String key1 = "testKey1";
String key2 = "testKey2";
String[] arguments = new String[] {key1, key2};
String value = "foo";

CompletableFuture<String> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<String>submitNewCommand(eq(LCS), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<String> response = service.lcs(key1, key2);
String payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void lcs_with_len_option() {
// setup
String key1 = "testKey1";
String key2 = "testKey2";
String[] arguments = new String[] {key1, key2, LEN_REDIS_API};
Long value = 3L;

CompletableFuture<Long> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Long>submitNewCommand(eq(LCS), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.lcsLen(key1, key2);
Long payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.IncrBy;
import static redis_request.RedisRequestOuterClass.RequestType.IncrByFloat;
import static redis_request.RedisRequestOuterClass.RequestType.Info;
import static redis_request.RedisRequestOuterClass.RequestType.LCS;
import static redis_request.RedisRequestOuterClass.RequestType.LIndex;
import static redis_request.RedisRequestOuterClass.RequestType.LInsert;
import static redis_request.RedisRequestOuterClass.RequestType.LLen;
Expand Down Expand Up @@ -975,6 +976,12 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
transaction.copy("key1", "key2", true);
results.add(Pair.of(Copy, buildArgs("key1", "key2", REPLACE_REDIS_API)));

transaction.lcs("key1", "key2");
results.add(Pair.of(LCS, buildArgs("key1", "key2")));

transaction.lcsLen("key1", "key2");
results.add(Pair.of(LCS, buildArgs("key1", "key2", "LEN")));

var protobufTransaction = transaction.getProtobufTransaction().build();

for (int idx = 0; idx < protobufTransaction.getCommandsCount(); idx++) {
Expand Down
60 changes: 60 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5133,4 +5133,64 @@ public void copy(BaseClient client) {
assertTrue(client.copy(source, destination, true).get());
assertEquals("two", client.get(destination).get());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void lcs(BaseClient client) {
assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.0.0"), "This feature added in redis 7.0.0");
// setup
String key1 = "{key}-1" + UUID.randomUUID();
String key2 = "{key}-2" + UUID.randomUUID();
String key3 = "{key}-3" + UUID.randomUUID();
String nonStringKey = "{key}-4" + UUID.randomUUID();

// keys does not exist or is empty
assertEquals("", client.lcs(key1, key2).get());

// setting string values
client.set(key1, "abcd");
client.set(key2, "bcde");
client.set(key3, "wxyz");

// getting the lcs
assertEquals("", client.lcs(key1, key3).get());
assertEquals("bcd", client.lcs(key1, key2).get());

// non set keys are used
client.sadd(nonStringKey, new String[] {"setmember"}).get();
ExecutionException executionException =
assertThrows(ExecutionException.class, () -> client.lcs(nonStringKey, key1).get());
assertInstanceOf(RequestException.class, executionException.getCause());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void lcs_with_len_option(BaseClient client) {
assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.0.0"), "This feature added in redis 7.0.0");
// setup
String key1 = "{key}-1" + UUID.randomUUID();
String key2 = "{key}-2" + UUID.randomUUID();
String key3 = "{key}-3" + UUID.randomUUID();
String nonStringKey = "{key}-4" + UUID.randomUUID();

// keys does not exist or is empty
assertEquals(0, client.lcsLen(key1, key2).get());

// setting string values
client.set(key1, "abcd");
client.set(key2, "bcde");
client.set(key3, "wxyz");

// getting the lcs
assertEquals(0, client.lcsLen(key1, key3).get());
assertEquals(3, client.lcsLen(key1, key2).get());

// non set keys are used
client.sadd(nonStringKey, new String[] {"setmember"}).get();
ExecutionException executionException =
assertThrows(ExecutionException.class, () -> client.lcs(nonStringKey, key1).get());
assertInstanceOf(RequestException.class, executionException.getCause());
}
}
Loading

0 comments on commit 562c53b

Please sign in to comment.