Skip to content

Commit

Permalink
Added docs and remaining tests
Browse files Browse the repository at this point in the history
  • Loading branch information
GumpacG committed Jun 21, 2024
1 parent 80cc5c9 commit 64d9423
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 34 deletions.
6 changes: 3 additions & 3 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1902,9 +1902,9 @@ public CompletableFuture<Map<String, Object>> lcsIdx(@NonNull String key1, @NonN
@Override
public CompletableFuture<Map<String, Object>> lcsIdx(
@NonNull String key1, @NonNull String key2, @NonNull LcsOptions lcsOptions) {
String[] arguments = new String[] {key1, key2, IDX_COMMAND_STRING};
return commandManager.submitNewCommand(
LCS, concatenateArrays(arguments, lcsOptions.toArgs()), this::handleMapResponse);
String[] arguments =
concatenateArrays(new String[] {key1, key2, IDX_COMMAND_STRING}, lcsOptions.toArgs());
return commandManager.submitNewCommand(LCS, arguments, this::handleMapResponse);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,65 @@ public interface StringBaseCommands {
* @example
* <pre>{@code
* // testKey1 = abcd, testKey2 = axcd
* Long result = client.lcs("testKey1", "testKey2").get();
* Long result = client.lcsLen("testKey1", "testKey2").get();
* assert result.equals(3L);
* }</pre>
*/
CompletableFuture<Long> lcsLen(String key1, String key2);

/**
* Returns the indices and 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 A <code>HashMap<String, Object</code> containing the indices of longest common
* subsequence between the 2 strings. The <code>Object</code> mapped to the <code>"matches"
* </code> String contains a two-dimensional Long array that stores the pair of start and end
* indices of the first and second Strings that match. An empty <code>Object</code> in the
* <code>HashMap</code> is returned if the keys do not exist or have no common subsequences.
* @example
* <pre>{@code
* // testKey1 = "abcd", testKey2 = "bcde"
* Map<String, Object> result = client.lcsIdx("testKey1", "testKey2").get();
* Map<String, Object> expectedLcsIdxObject =Map.of("matches", new Object[] {new Long[][] {{1L, 3L}, {0L, 2L}}},
* "len", 3L);
* // result is equal to expectedLcsIdxObject
* }</pre>
*/
CompletableFuture<Map<String, Object>> lcsIdx(String key1, String key2);

/**
* Returns the indices and 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.
* @param lcsOptions The {@link LcsOptions}.
* @return A <code>HashMap<String, Object</code> containing the indices of longest common
* subsequence between the 2 strings. The <code>Object</code> mapped to the <code>"matches"
* </code> String contains a two-dimensional Long array that stores the pair of start and end
* indices of the first and second Strings that match. An empty <code>Object</code> in the
* <code>HashMap</code> is returned if the keys do not exist or have no common subsequences.
* @example
* <pre>{@code
* // testKey1 = "abcd", testKey2 = "bcde"
* Map<String, Object> result = client.lcsIdx("testKey1", "testKey2").get();
* Map<String, Object> expectedLcsIdxObject =Map.of("matches",
* new Object[] {new Object[] {new Long[] {1L, 3L}, new Long[] {0L, 2L}, 3L}},
* "len", 3L);
* // result is equal to expectedLcsIdxObject
* }</pre>
*/
CompletableFuture<Map<String, Object>> lcsIdx(String key1, String key2, LcsOptions lcsOptions);
}
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 @@ -4636,12 +4636,49 @@ public T lcsLen(@NonNull String key1, @NonNull String key2) {
return getThis();
}

/**
* Returns the indices and 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 - A <code>HashMap<String, Object</code> containing the indices of
* longest common subsequence between the 2 strings. The <code>Object</code> mapped to the
* <code>"matches"
* </code> String contains a two-dimensional Long array that stores the pair of start and end
* indices of the first and second Strings that match. An empty <code>Object</code> in the
* <code>HashMap</code> is returned if the keys do not exist or have no common subsequences.
*/
public T lcsIdx(@NonNull String key1, @NonNull String key2) {
ArgsArray args = buildArgs(key1, key2, IDX_COMMAND_STRING);
protobufTransaction.addCommands(buildCommand(LCS, args));
return getThis();
}

/**
* Returns the indices and 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.
* @param lcsOptions The {@link LcsOptions}.
* @return Command Response - A <code>HashMap<String, Object</code> containing the indices of
* longest common subsequence between the 2 strings. The <code>Object</code> mapped to the
* <code>"matches"
* </code> String contains a two-dimensional Long array that stores the pair of start and end
* indices of the first and second Strings that match. An empty <code>Object</code> in the
* <code>HashMap</code> is returned if the keys do not exist or have no common subsequences.
*/
public T lcsIdx(@NonNull String key1, @NonNull String key2, @NonNull LcsOptions lcsOptions) {
ArgsArray args =
buildArgs(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,51 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models.commands;

import glide.api.commands.StringBaseCommands;
import java.util.ArrayList;
import java.util.List;
import lombok.Builder;

/**
* Optional arguments to {@link StringBaseCommands#lcsIdx(String, String, LcsOptions)}
*
* @see <a href="https://valkey.io/commands/lcs/">valkey.io</a>.
*/
@Builder
public final class LcsOptions {
/** <code>IDX</code> option string to include in the <code>LCS</code> command. */
public static final String IDX_COMMAND_STRING = "IDX";

/** <code>MINMATCHLEN</code> option string to include in the <code>LCS</code> command. */
public static final String MINMATCHLEN_COMMAND_STRING = "MINMATCHLEN";

/** <code>WITHMATCHLEN</code> option string to include in the <code>LCS</code> command. */
public static final String WITHMATCHLEN_COMMAND_STRING = "WITHMATCHLEN";

/** Minimum length of matches to include in the result. */
private final Long minMatchLen;

/** Will include match lengths in the result if set to <code>true</code>. */
private boolean isWithMatchLen;

/** Sets <code>isWithMatchLen</code> to <code>true</code>. */
public static class LcsOptionsBuilder {

/** If the stream doesn't exist, this creates a new stream with a length of <code>0</code>. */
/** Sets <code>isWithMatchLen</code> to <code>true</code>. */
public LcsOptionsBuilder withMatchLen() {
return isWithMatchLen(true);
}
}

/**
* Converts LcsOptions into a String[].
*
* @return String[]
*/
public String[] toArgs() {
List<String> optionArgs = new ArrayList<>();

if (minMatchLen != null) {
optionArgs.add(WITHMATCHLEN_COMMAND_STRING);
optionArgs.add(MINMATCHLEN_COMMAND_STRING);
optionArgs.add(minMatchLen.toString());
}

Expand Down
19 changes: 12 additions & 7 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import static glide.api.models.commands.FlushMode.SYNC;
import static glide.api.models.commands.LInsertOptions.InsertPosition.BEFORE;
import static glide.api.models.commands.LcsOptions.IDX_COMMAND_STRING;
import static glide.api.models.commands.LcsOptions.MINMATCHLEN_COMMAND_STRING;
import static glide.api.models.commands.LcsOptions.WITHMATCHLEN_COMMAND_STRING;
import static glide.api.models.commands.ScoreFilter.MAX;
import static glide.api.models.commands.SetOptions.ConditionalSet.ONLY_IF_DOES_NOT_EXIST;
Expand Down Expand Up @@ -6467,11 +6468,7 @@ public void lcsIdx() {
String key2 = "testKey2";
String[] arguments = new String[] {key1, key2, IDX_COMMAND_STRING};
Map<String, Object> value =
Map.of(
"matches",
new Object[] {new Object[] {new Long[] {1L, 3L}, new Long[] {0L, 2L}}},
"len",
3L);
Map.of("matches", new Object[] {new Long[][] {{1L, 3L}, {0L, 2L}}}, "len", 3L);

CompletableFuture<Map<String, Object>> testResponse = new CompletableFuture<>();
testResponse.complete(value);
Expand All @@ -6495,7 +6492,15 @@ public void lcsIdx_with_options() {
// setup
String key1 = "testKey1";
String key2 = "testKey2";
String[] arguments = new String[] {key1, key2, IDX_COMMAND_STRING, WITHMATCHLEN_COMMAND_STRING};
String[] arguments =
new String[] {
key1,
key2,
IDX_COMMAND_STRING,
MINMATCHLEN_COMMAND_STRING,
"2",
WITHMATCHLEN_COMMAND_STRING
};
Map<String, Object> value =
Map.of(
"matches",
Expand All @@ -6512,7 +6517,7 @@ public void lcsIdx_with_options() {

// exercise
CompletableFuture<Map<String, Object>> response =
service.lcsIdx(key1, key2, LcsOptions.builder().withMatchLen().build());
service.lcsIdx(key1, key2, LcsOptions.builder().minMatchLen(2L).withMatchLen().build());
Map<String, Object> payload = response.get();

// verify
Expand Down
72 changes: 59 additions & 13 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5432,36 +5432,82 @@ public void lcsIdx(BaseClient client) {
// 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();

client.set(key3, "wxyz");
// keys does not exist or is empty
assertEquals(0, client.lcsLen(key1, key2).get());
Map<String, Object> result = client.lcsIdx(key1, key2).get();
assertDeepEquals(new Object[0], result.get("matches"));
assertEquals(0L, result.get("len"));
result = client.lcsIdx(key1, key2, LcsOptions.builder().withMatchLen().build()).get();
assertDeepEquals(new Object[0], result.get("matches"));
assertEquals(0L, result.get("len"));

// setting string values
client.set(key1, "abcdefghijk");
client.set(key2, "defjkjuighijk");

// LCS with only IDX
Object expectedMatchesObject =
new Object[] {
new Object[] {new Long[] {6L, 10L}, new Long[] {8L, 12L}},
new Object[] {new Long[] {3L, 5L}, new Long[] {0L, 2L}}
new Long[][] {{6L, 10L}, {8L, 12L}},
new Long[][] {{3L, 5L}, {0L, 2L}}
};
result = client.lcsIdx(key1, key2).get();
assertDeepEquals(expectedMatchesObject, result.get("matches"));
assertEquals(8L, result.get("len"));

assertDeepEquals(expectedMatchesObject, client.lcsIdx(key1, key2).get().get("matches"));
assertEquals(8L, client.lcsIdx(key1, key2).get().get("len"));
// LCS with only IDX and WITHMATCHLEN
expectedMatchesObject =
new Object[] {
new Object[] {new Long[] {6L, 10L}, new Long[] {8L, 12L}, 5L},
new Object[] {new Long[] {3L, 5L}, new Long[] {0L, 2L}, 3L}
};
result = client.lcsIdx(key1, key2, LcsOptions.builder().withMatchLen().build()).get();
assertDeepEquals(expectedMatchesObject, result.get("matches"));
assertEquals(8L, result.get("len"));

// LCS with only IDX and WITHMATCHLEN
expectedMatchesObject =
new Object[] {
new Object[] {new Long[] {6L, 10L}, new Long[] {8L, 12L}, 5L},
new Object[] {new Long[] {3L, 5L}, new Long[] {0L, 2L}, 3L}
};
assertDeepEquals(
expectedMatchesObject,
result = client.lcsIdx(key1, key2, LcsOptions.builder().withMatchLen().build()).get();
assertDeepEquals(expectedMatchesObject, result.get("matches"));
assertEquals(8L, result.get("len"));

// LCS with only IDX, and MINMATCHLEN
expectedMatchesObject =
new Object[] {
new Long[][] {{6L, 10L}, {8L, 12L}},
};
result = client.lcsIdx(key1, key2, LcsOptions.builder().minMatchLen(4L).build()).get();
assertDeepEquals(expectedMatchesObject, result.get("matches"));
assertEquals(8L, result.get("len"));

// LCS with only IDX, MINMATCHLEN, and WITHMATCHLEN
expectedMatchesObject =
new Object[] {new Object[] {new Long[] {6L, 10L}, new Long[] {8L, 12L}, 5L}};
result =
client
.lcsIdx(key1, key2, LcsOptions.builder().withMatchLen().build())
.get()
.get("matches"));
assertEquals(8L, client.lcsIdx(key1, key2).get().get("len"));
.lcsIdx(key1, key2, LcsOptions.builder().minMatchLen(4L).withMatchLen().build())
.get();
assertDeepEquals(expectedMatchesObject, result.get("matches"));
assertEquals(8L, result.get("len"));

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

executionException =
assertThrows(
ExecutionException.class,
() ->
client
.lcsIdx(nonStringKey, key1, LcsOptions.builder().withMatchLen().build())
.get());
assertInstanceOf(RequestException.class, executionException.getCause());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,7 @@ private static Object[] stringCommands(BaseTransaction<?> transaction) {
String stringKey8 = "{StringKey}-8-" + UUID.randomUUID();

Map<String, Object> expectedLcsIdxObject =
Map.of(
"matches",
new Object[] {new Object[] {new Long[] {1L, 3L}, new Long[] {0L, 2L}}},
"len",
3L);
Map.of("matches", new Object[] {new Long[][] {{1L, 3L}, {0L, 2L}}}, "len", 3L);

Map<String, Object> expectedLcsIdxWithMatchLenObject =
Map.of(
Expand Down Expand Up @@ -299,8 +295,7 @@ private static Object[] stringCommands(BaseTransaction<?> transaction) {
3L, // lcsLEN(stringKey6, stringKey7)
0L, // lcsLEN(stringKey6, stringKey8)
expectedLcsIdxObject, // lcsIdx(stringKey6, stringKey7)
expectedLcsIdxWithMatchLenObject, // lcsIdx(stringKey6, stringKey7,
// LcsOptions.builder().withMatchLen().build())
expectedLcsIdxWithMatchLenObject, // lcsIdx(stringKey6, stringKey7, withMatchLen())
});
}

Expand Down

0 comments on commit 64d9423

Please sign in to comment.