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

Support GlideString for sdiff commands #1722

Merged
merged 1 commit into from
Jun 30, 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
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
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
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
Loading