Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: Adding command LCS (No IDX option) #1558

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -214,6 +214,7 @@ enum RequestType {
XRevRange = 176;
Copy = 178;
MSetNX = 179;
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 @@ -184,6 +184,7 @@ pub enum RequestType {
XRevRange = 176,
Copy = 178,
MSetNX = 179,
LCS = 181,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -371,6 +372,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::Sort => RequestType::Sort,
ProtobufRequestType::XRevRange => RequestType::XRevRange,
ProtobufRequestType::MSetNX => RequestType::MSetNX,
ProtobufRequestType::LCS => RequestType::LCS,
}
}
}
Expand Down Expand Up @@ -554,6 +556,7 @@ impl RequestType {
RequestType::Sort => Some(cmd("SORT")),
RequestType::XRevRange => Some(cmd("XREVRANGE")),
RequestType::MSetNX => Some(cmd("MSETNX")),
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 @@ -1804,4 +1805,16 @@ public CompletableFuture<Boolean> msetnx(@NonNull Map<String, String> keyValueMa
String[] args = convertMapToKeyValueStringArray(keyValueMap);
return commandManager.submitNewCommand(MSetNX, args, 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 @@ -333,4 +336,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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it not easier to just include: client.mset({"testKey1", "testKey2"}, {"abcd", "axcd"}).get();

* 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 @@ -4327,6 +4329,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 @@ -6015,4 +6017,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 @@ -979,6 +980,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 @@ -5157,4 +5157,64 @@ public void msetnx(BaseClient client) {
assertFalse(client.msetnx(keyValueMap2).get());
assertNull(client.get(key3).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
Loading