Skip to content

Commit

Permalink
Java: Add Zdiff command. (Sorted Set Commands) (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
SanHalacogluImproving committed Apr 9, 2024
1 parent 10490fd commit 7b22a82
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
* Core: Enabled Cluster Mode periodic checks by default ([#1089](https://github.com/aws/glide-for-redis/pull/1089))
* Node: Added Rename command. ([#1124](https://github.com/aws/glide-for-redis/pull/1124))
* Python: Added JSON.TOGGLE command ([#1184](https://github.com/aws/glide-for-redis/pull/1184))
* Core: Added `ZDIFF` command (TODO add PR # here)

#### Features

Expand Down Expand Up @@ -84,4 +85,4 @@

Preview release of **GLIDE for Redis** a Polyglot Redis client.

See the [README](README.md) for additional information.
See the [README](README.md) for additional information.
3 changes: 2 additions & 1 deletion glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ enum RequestType {
Blpop = 100;
RPushX = 102;
LPushX = 103;
ZDiff = 104;
}

message Command {
Expand All @@ -168,7 +169,7 @@ message Transaction {

message RedisRequest {
uint32 callback_idx = 1;

oneof command {
Command single_command = 2;
Transaction transaction = 3;
Expand Down
15 changes: 15 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZDiff;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
Expand Down Expand Up @@ -707,6 +708,20 @@ public CompletableFuture<Object[]> zrankWithScore(@NonNull String key, @NonNull
Zrank, new String[] {key, member, WITH_SCORE_REDIS_API}, this::handleArrayOrNullResponse);
}

@Override
public CompletableFuture<String[]> zdiff(@NonNull String[] keys) {
String[] arguments = ArrayUtils.addFirst(keys, Long.toString(keys.length));
return commandManager.submitNewCommand(
ZDiff, arguments, response -> castArray(handleArrayResponse(response), String.class));
}

@Override
public CompletableFuture<Map<String, Double>> zdiffWithScores(@NonNull String[] keys) {
String[] arguments = ArrayUtils.addFirst(keys, Long.toString(keys.length));
arguments = ArrayUtils.add(arguments, WITH_SCORES_REDIS_API);
return commandManager.submitNewCommand(ZDiff, arguments, this::handleMapResponse);
}

@Override
public CompletableFuture<String> xadd(@NonNull String key, @NonNull Map<String, String> values) {
return xadd(key, values, StreamAddOptions.builder().build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,4 +456,38 @@ CompletableFuture<Map<String, Double>> zrangeWithScores(
* }</pre>
*/
CompletableFuture<Object[]> zrankWithScore(String key, String member);

/**
* Returns the difference between the first sorted set and all the successive sorted sets.
*
* @see <a href="https://redis.io/commands/zdiff/">redis.io</a> for more details.
* @param keys The keys of the sorted sets.
* @return An <code>array</code> of elements representing the difference between the sorted sets.
* <br>
* If the first <code>key</code> does not exist, it is treated as an empty sorted set, and the
* command returns an empty <code>array</code>.
* @example
* <pre>{@code
* String[] payload = client.zdiff(new String[] {"sortedSet1", "sortedSet2", "sortedSet3"}).get();
* assert payload.equals(new String[]{"element1"});
* }</pre>
*/
CompletableFuture<String[]> zdiff(String[] keys);

/**
* Returns the difference between the first sorted set and all the successive sorted sets.
*
* @see <a href="https://redis.io/commands/zdiff/">redis.io</a> for more details.
* @param keys The keys of the sorted sets.
* @return A <code>Map</code> of elements and their scores representing the difference between the
* sorted sets.<br>
* If the first <code>key</code> does not exist, it is treated as an empty sorted set, and the
* command returns an empty <code>Map</code>.
* @example
* <pre>{@code
* Map<String, Double> payload = client.zdiffWithScores(new String[] {"sortedSet1", "sortedSet2", "sortedSet3"}).get();
* assert payload.equals(Map.of("element1", 1.0));
* }</pre>
*/
CompletableFuture<Map<String, Double>> zdiffWithScores(String[] keys);
}
36 changes: 36 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models;

import static glide.api.commands.SortedSetBaseCommands.WITH_SCORES_REDIS_API;
import static glide.api.commands.SortedSetBaseCommands.WITH_SCORE_REDIS_API;
import static glide.api.models.commands.RangeOptions.createZrangeArgs;
import static glide.utils.ArrayTransformUtils.concatenateArrays;
Expand Down Expand Up @@ -70,6 +71,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZDiff;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
Expand Down Expand Up @@ -1491,6 +1493,40 @@ public T zrankWithScore(@NonNull String key, @NonNull String member) {
return getThis();
}

/**
* Returns the difference between the first sorted set and all the successive sorted sets.
*
* @see <a href="https://redis.io/commands/zdiff/">redis.io</a> for more details.
* @param keys The keys of the sorted sets.
* @return Command Response - An <code>array</code> of elements representing the difference
* between the sorted sets.<br>
* If the first <code>key</code> does not exist, it is treated as an empty sorted set, and the
* command returns an empty <code>array</code>.
*/
public T zdiff(@NonNull String[] keys) {
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(keys, Long.toString(keys.length)));
protobufTransaction.addCommands(buildCommand(ZDiff, commandArgs));
return getThis();
}

/**
* Returns the difference between the first sorted set and all the successive sorted sets.
*
* @see <a href="https://redis.io/commands/zdiff/">redis.io</a> for more details.
* @param keys The keys of the sorted sets.
* @return Command Response - A <code>Map</code> of elements and their scores representing the
* difference between the sorted sets.<br>
* If the first <code>key</code> does not exist, it is treated as an empty sorted set, and the
* command returns an empty <code>Map</code>.
*/
public T zdiffWithScores(@NonNull String[] keys) {
String[] arguments = ArrayUtils.addFirst(keys, Long.toString(keys.length));
arguments = ArrayUtils.add(arguments, WITH_SCORES_REDIS_API);
ArgsArray commandArgs = buildArgs(arguments);
protobufTransaction.addCommands(buildCommand(ZDiff, commandArgs));
return getThis();
}

/**
* Adds an entry to the specified stream.
*
Expand Down
53 changes: 53 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static glide.api.BaseClient.OK;
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.SortedSetBaseCommands.WITH_SCORES_REDIS_API;
import static glide.api.models.commands.SetOptions.ConditionalSet.ONLY_IF_DOES_NOT_EXIST;
import static glide.api.models.commands.SetOptions.ConditionalSet.ONLY_IF_EXISTS;
import static glide.api.models.commands.SetOptions.RETURN_OLD_VALUE;
Expand Down Expand Up @@ -90,6 +91,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZDiff;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
Expand Down Expand Up @@ -2244,6 +2246,57 @@ public void zrankWithScore_returns_success() {
assertEquals(value, payload);
}

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

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

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

// exercise
CompletableFuture<String[]> response = service.zdiff(new String[] {key1, key2});
String[] payload = response.get();

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

@SneakyThrows
@Test
public void zdiffWithScores_returns_success() {
// setup
String key1 = "testKey1";
String key2 = "testKey2";
String[] arguments = new String[] {"2", key1, key2, WITH_SCORES_REDIS_API};
Map<String, Double> value = Map.of("element1", 2.0);

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

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

// exercise
CompletableFuture<Map<String, Double>> response =
service.zdiffWithScores(new String[] {key1, key2});
Map<String, Double> payload = response.get();

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

@SneakyThrows
@Test
public void xadd_returns_success() {
Expand Down
18 changes: 18 additions & 0 deletions java/client/src/test/java/glide/api/models/TransactionTests.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.models;

import static glide.api.commands.SortedSetBaseCommands.WITH_SCORES_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.models.commands.SetOptions.RETURN_OLD_VALUE;
Expand Down Expand Up @@ -68,6 +69,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.Type;
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
import static redis_request.RedisRequestOuterClass.RequestType.XAdd;
import static redis_request.RedisRequestOuterClass.RequestType.ZDiff;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
Expand Down Expand Up @@ -455,6 +457,22 @@ public void transaction_builds_protobuf_request(BaseTransaction<?> transaction)
.addArgs(WITH_SCORE_REDIS_API)
.build()));

transaction.zdiff(new String[] {"key1", "key2"});
results.add(
Pair.of(
ZDiff, ArgsArray.newBuilder().addArgs("2").addArgs("key1").addArgs("key2").build()));

transaction.zdiffWithScores(new String[] {"key1", "key2"});
results.add(
Pair.of(
ZDiff,
ArgsArray.newBuilder()
.addArgs("2")
.addArgs("key1")
.addArgs("key2")
.addArgs(WITH_SCORES_REDIS_API)
.build()));

transaction.xadd("key", Map.of("field1", "foo1"));
results.add(
Pair.of(
Expand Down
44 changes: 44 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,50 @@ public void zrank(BaseClient client) {
assertTrue(executionException.getCause() instanceof RequestException);
}


@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void zdiff(BaseClient client) {
String key1 = "{testKey}:" + UUID.randomUUID();
String key2 = "{testKey}:" + UUID.randomUUID();
String key3 = "{testKey}:" + UUID.randomUUID();
String nonExistentKey = "{testKey}:" + UUID.randomUUID();

Map<String, Double> membersScores1 = Map.of("one", 1.0, "two", 2.0, "three", 3.0);
Map<String, Double> membersScores2 = Map.of("two", 2.0);
Map<String, Double> membersScores3 = Map.of("one", 0.5, "two", 2.0, "three", 3.0, "four", 4.0);

assertEquals(3, client.zadd(key1, membersScores1).get());
assertEquals(1, client.zadd(key2, membersScores2).get());
assertEquals(4, client.zadd(key3, membersScores3).get());

assertArrayEquals(new String[] {"one", "three"}, client.zdiff(new String[] {key1, key2}).get());
assertArrayEquals(new String[] {}, client.zdiff(new String[] {key1, key3}).get());
assertArrayEquals(new String[] {}, client.zdiff(new String[] {nonExistentKey, key3}).get());

assertEquals(
Map.of("one", 1.0, "three", 3.0), client.zdiffWithScores(new String[] {key1, key2}).get());
assertEquals(Map.of(), client.zdiffWithScores(new String[] {key1, key3}).get());
assertTrue(client.zdiffWithScores(new String[] {nonExistentKey, key3}).get().isEmpty());

// Key exists, but it is not a set
assertEquals(OK, client.set(nonExistentKey, "bar").get());

ExecutionException executionException =
assertThrows(
ExecutionException.class,
() -> client.zdiff(new String[] {nonExistentKey, key2}).get());
assertTrue(executionException.getCause() instanceof RequestException);

executionException =
assertThrows(
ExecutionException.class,
() -> client.zdiffWithScores(new String[] {nonExistentKey, key2}).get());
assertTrue(executionException.getCause() instanceof RequestException);
}


@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ public static BaseTransaction<?> transactionTest(BaseTransaction<?> baseTransact
baseTransaction.zpopmin(key8);
baseTransaction.zpopmax(key8);

baseTransaction.zadd(key8, Map.of("one", 1.0, "two", 2.0, "three", 3.0));
baseTransaction.zadd(key9, Map.of("two", 2.2));
baseTransaction.zdiff(new String[] {key8, key9});
baseTransaction.zdiffWithScores(new String[] {key8, key9});

baseTransaction.xadd(
key9, Map.of("field1", "value1"), StreamAddOptions.builder().id("0-1").build());
baseTransaction.xadd(
Expand Down Expand Up @@ -195,6 +200,10 @@ public static Object[] transactionTestResult() {
2.0, // zscore(key8, "two")
Map.of("two", 2.0), // zpopmin(key8)
Map.of("three", 3.0), // zpopmax(key8)
3L, // zadd(key8, Map.of("one", 1.0, "two", 2.0, "three", 3.0))
1L, // zadd(key9, Map.of("two", 2.2))
new String[] {"one", "three"}, // zdiff(new String[] {key8, key9})
Map.of("one", 1.0, "three", 3.0), // zdiffWithScores(new String[] {key8, key9})
"0-1", // xadd(key9, Map.of("field1", "value1"),
// StreamAddOptions.builder().id("0-1").build());
"0-2", // xadd(key9, Map.of("field2", "value2"),
Expand Down

0 comments on commit 7b22a82

Please sign in to comment.