Skip to content

Commit

Permalink
Support GlideString for sdiff commands (#1722)
Browse files Browse the repository at this point in the history
Co-authored-by: Yulazari <[email protected]>
  • Loading branch information
yulazariy and Yulazari authored Jun 30, 2024
1 parent 1fe3dc2 commit d6088cd
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
12 changes: 12 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,11 @@ public CompletableFuture<Set<String>> sdiff(@NonNull String[] keys) {
return commandManager.submitNewCommand(SDiff, keys, this::handleSetResponse);
}

@Override
public CompletableFuture<Set<GlideString>> sdiff(@NonNull GlideString[] keys) {
return commandManager.submitNewCommand(SDiff, keys, this::handleSetBinaryResponse);
}

@Override
public CompletableFuture<Boolean[]> smismember(@NonNull String key, @NonNull String[] members) {
String[] arguments = ArrayUtils.addFirst(members, key);
Expand All @@ -1167,6 +1172,13 @@ public CompletableFuture<Long> sdiffstore(@NonNull String destination, @NonNull
return commandManager.submitNewCommand(SDiffStore, arguments, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> sdiffstore(
@NonNull GlideString destination, @NonNull GlideString[] keys) {
GlideString[] arguments = ArrayUtils.addFirst(keys, destination);
return commandManager.submitNewCommand(SDiffStore, arguments, this::handleLongResponse);
}

@Override
public CompletableFuture<Boolean> smove(
@NonNull String source, @NonNull String destination, @NonNull String member) {
Expand Down
34 changes: 34 additions & 0 deletions java/client/src/main/java/glide/api/commands/SetBaseCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,22 @@ public interface SetBaseCommands {
*/
CompletableFuture<Set<String>> sdiff(String[] keys);

/**
* Computes the difference between the first set and all the successive sets in <code>keys</code>.
*
* @apiNote When in cluster mode, all <code>keys</code> must map to the same hash slot.
* @see <a href="https://redis.io/commands/sdiff/">redis.io</a> for details.
* @param keys The keys of the sets to diff.
* @return A <code>Set</code> of elements representing the difference between the sets.<br>
* If the a <code>key</code> does not exist, it is treated as an empty set.
* @example
* <pre>{@code
* Set<GlideString> values = client.sdiff(new GlideString[] {gs("set1"), gs("set2")}).get();
* assert values.contains(gs("element")); // Indicates that "element" is present in "set1", but missing in "set2"
* }</pre>
*/
CompletableFuture<Set<GlideString>> sdiff(GlideString[] keys);

/**
* Stores the difference between the first set and all the successive sets in <code>keys</code>
* into a new set at <code>destination</code>.
Expand All @@ -294,6 +310,24 @@ public interface SetBaseCommands {
*/
CompletableFuture<Long> sdiffstore(String destination, String[] keys);

/**
* Stores the difference between the first set and all the successive sets in <code>keys</code>
* into a new set at <code>destination</code>.
*
* @apiNote When in cluster mode, <code>destination</code> and all <code>keys</code> must map to
* the same hash slot.
* @see <a href="https://redis.io/commands/sdiffstore/">redis.io</a> for details.
* @param destination The key of the destination set.
* @param keys The keys of the sets to diff.
* @return The number of elements in the resulting set.
* @example
* <pre>{@code
* Long length = client.sdiffstore(gs("mySet"), new GlideString[] { gs("set1"), gs("set2") }).get();
* assert length == 5L;
* }</pre>
*/
CompletableFuture<Long> sdiffstore(GlideString destination, GlideString[] keys);

/**
* Gets the intersection of all the given sets.
*
Expand Down
50 changes: 50 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3017,6 +3017,29 @@ public void sdiff_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void sdiff_binary_returns_success() {
// setup
GlideString[] keys = new GlideString[] {gs("key1"), gs("key2")};
Set<GlideString> value = Set.of(gs("1"), gs("2"));

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

// match on protobuf request
when(commandManager.<Set<GlideString>>submitNewCommand(eq(SDiff), eq(keys), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Set<GlideString>> response = service.sdiff(keys);
Set<GlideString> payload = response.get();

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

@SneakyThrows
@Test
public void smismember_returns_success() {
Expand Down Expand Up @@ -3094,6 +3117,33 @@ public void sdiffstore_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void sdiffstore_binary_returns_success() {
// setup
GlideString destination = gs("dest");
GlideString[] keys = new GlideString[] {gs("set1"), gs("set2")};
GlideString[] arguments = {gs("dest"), gs("set1"), gs("set2")};

Long value = 2L;

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

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

// exercise
CompletableFuture<Long> response = service.sdiffstore(destination, keys);

Long payload = response.get();

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

@SneakyThrows
@Test
public void smove_returns_success() {
Expand Down
82 changes: 82 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,35 @@ public void sdiff(BaseClient client) {
assertInstanceOf(RequestException.class, executionException.getCause());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void sdiff_gs(BaseClient client) {
GlideString key1 = gs("{key}-1-" + UUID.randomUUID());
GlideString key2 = gs("{key}-2-" + UUID.randomUUID());
GlideString key3 = gs("{key}-3-" + UUID.randomUUID());

assertEquals(3, client.sadd(key1, new GlideString[] {gs("a"), gs("b"), gs("c")}).get());
assertEquals(3, client.sadd(key2, new GlideString[] {gs("c"), gs("d"), gs("e")}).get());

assertEquals(Set.of(gs("a"), gs("b")), client.sdiff(new GlideString[] {key1, key2}).get());
assertEquals(Set.of(gs("d"), gs("e")), client.sdiff(new GlideString[] {key2, key1}).get());

// second set is empty
assertEquals(
Set.of(gs("a"), gs("b"), gs("c")), client.sdiff(new GlideString[] {key1, key3}).get());

// first set is empty
assertEquals(Set.of(), client.sdiff(new GlideString[] {key3, key1}).get());

// source key exists, but it is not a set
assertEquals(OK, client.set(key3, gs("value")).get());
ExecutionException executionException =
assertThrows(
ExecutionException.class, () -> client.sdiff(new GlideString[] {key1, key3}).get());
assertInstanceOf(RequestException.class, executionException.getCause());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
Expand Down Expand Up @@ -1852,6 +1881,59 @@ public void sdiffstore(BaseClient client) {
assertInstanceOf(RequestException.class, executionException.getCause());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
public void sdiffstore_gs(BaseClient client) {
GlideString key1 = gs("{key}-1-" + UUID.randomUUID());
GlideString key2 = gs("{key}-2-" + UUID.randomUUID());
GlideString key3 = gs("{key}-3-" + UUID.randomUUID());
GlideString key4 = gs("{key}-4-" + UUID.randomUUID());
GlideString key5 = gs("{key}-5-" + UUID.randomUUID());

assertEquals(3, client.sadd(key1, new GlideString[] {gs("a"), gs("b"), gs("c")}).get());
assertEquals(3, client.sadd(key2, new GlideString[] {gs("c"), gs("d"), gs("e")}).get());
assertEquals(3, client.sadd(key4, new GlideString[] {gs("e"), gs("f"), gs("g")}).get());

// create new
assertEquals(2, client.sdiffstore(key3, new GlideString[] {key1, key2}).get());
assertEquals(Set.of(gs("a"), gs("b")), client.smembers(key3).get());

// overwrite existing set
assertEquals(2, client.sdiffstore(key2, new GlideString[] {key3, key2}).get());
assertEquals(Set.of(gs("a"), gs("b")), client.smembers(key2).get());

// overwrite source
assertEquals(3, client.sdiffstore(key1, new GlideString[] {key1, key4}).get());
assertEquals(Set.of(gs("a"), gs("b"), gs("c")), client.smembers(key1).get());

// diff with empty set
assertEquals(3, client.sdiffstore(key1, new GlideString[] {key1, key5}).get());
assertEquals(Set.of(gs("a"), gs("b"), gs("c")), client.smembers(key1).get());

// diff empty with non-empty set
assertEquals(0, client.sdiffstore(key3, new GlideString[] {key5, key1}).get());
assertEquals(Set.of(), client.smembers(key3).get());

// source key exists, but it is not a set
assertEquals(OK, client.set(key5, gs("value")).get());
ExecutionException executionException =
assertThrows(
ExecutionException.class,
() -> client.sdiffstore(key1, new GlideString[] {key5}).get());
assertInstanceOf(RequestException.class, executionException.getCause());

// overwrite destination - not a set
assertEquals(1, client.sdiffstore(key5, new GlideString[] {key1, key2}).get());
assertEquals(Set.of(gs("c")), client.smembers(key5).get());

// wrong arguments
executionException =
assertThrows(
ExecutionException.class, () -> client.sdiffstore(key5, new GlideString[0]).get());
assertInstanceOf(RequestException.class, executionException.getCause());
}

@SneakyThrows
@ParameterizedTest(autoCloseArguments = false)
@MethodSource("getClients")
Expand Down
8 changes: 8 additions & 0 deletions java/integTest/src/test/java/glide/cluster/CommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -755,8 +755,16 @@ public static Stream<Arguments> callCrossSlotCommandsWhichShouldFail() {
null,
clusterClient.sinterstore(gs("abc"), new GlideString[] {gs("zxy"), gs("lkn")})),
Arguments.of("sdiff", null, clusterClient.sdiff(new String[] {"abc", "zxy", "lkn"})),
Arguments.of(
"sdiff_gs",
null,
clusterClient.sdiff(new GlideString[] {gs("abc"), gs("zxy"), gs("lkn")})),
Arguments.of(
"sdiffstore", null, clusterClient.sdiffstore("abc", new String[] {"zxy", "lkn"})),
Arguments.of(
"sdiffstore_gs",
null,
clusterClient.sdiffstore(gs("abc"), new GlideString[] {gs("zxy"), gs("lkn")})),
Arguments.of("sinter", null, clusterClient.sinter(new String[] {"abc", "zxy", "lkn"})),
Arguments.of(
"sinter_gs",
Expand Down

0 comments on commit d6088cd

Please sign in to comment.