From 6b054fb60c0c4c63a8ac31aee8055e018d11addf Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving <144171266+SanHalacogluImproving@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:16:22 -0700 Subject: [PATCH] Java: Add Zpopmax command. (Sorted Set Commands) (#1164) * Java: Add Zpopmax command. (Sorted Set Commands) (#149) * Minor documentation update. * Minor test update. * Spotless Signed-off-by: Andrew Carbonetto * Minor documentation update. * Rebase + Spotless --------- Signed-off-by: Andrew Carbonetto Co-authored-by: Andrew Carbonetto --- .../src/main/java/glide/api/BaseClient.java | 12 +++++ .../api/commands/SortedSetBaseCommands.java | 38 ++++++++++++++ .../glide/api/models/BaseTransaction.java | 37 ++++++++++++++ .../test/java/glide/api/RedisClientTest.java | 50 +++++++++++++++++++ .../glide/api/models/TransactionTests.java | 7 +++ .../test/java/glide/SharedCommandTests.java | 19 +++++++ .../java/glide/TransactionTestUtilities.java | 2 + 7 files changed, 165 insertions(+) diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index a99596a4a2..3b6e98164d 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -48,6 +48,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.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -617,6 +618,17 @@ public CompletableFuture zcard(@NonNull String key) { return commandManager.submitNewCommand(Zcard, new String[] {key}, this::handleLongResponse); } + @Override + public CompletableFuture> zpopmax(@NonNull String key, long count) { + return commandManager.submitNewCommand( + ZPopMax, new String[] {key, Long.toString(count)}, this::handleMapResponse); + } + + @Override + public CompletableFuture> zpopmax(@NonNull String key) { + return commandManager.submitNewCommand(ZPopMax, new String[] {key}, this::handleMapResponse); + } + @Override public CompletableFuture zscore(@NonNull String key, @NonNull String member) { return commandManager.submitNewCommand( diff --git a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java index 1846fac41f..33a58ce849 100644 --- a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java @@ -184,6 +184,44 @@ CompletableFuture zaddIncr( */ CompletableFuture zcard(String key); + /** + * Removes and returns up to count members with the highest scores from the sorted + * set stored at the specified key. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param count Specifies the quantity of members to pop.
+ * If count is higher than the sorted set's cardinality, returns all members and + * their scores, ordered from highest to lowest. + * @return A map of the removed members and their scores, ordered from the one with the highest + * score to the one with the lowest.
+ * If key doesn't exist, it will be treated as an empty sorted set and the + * command returns an empty Map. + * @example + *
{@code
+     * Map payload = client.zpopmax("mySortedSet", 2).get();
+     * assert payload.equals(Map.of('member2', 8.0, 'member3', 7.5)); // Indicates that 'member2' with a score of 8.0 and 'member3' with a score of 7.5 have been removed from the sorted set.
+     * }
+ */ + CompletableFuture> zpopmax(String key, long count); + + /** + * Removes and returns the member with the highest score from the sorted set stored at the + * specified key. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @return A map containing the removed member and its corresponding score.
+ * If key doesn't exist, it will be treated as an empty sorted set and the + * command returns an empty Map. + * @example + *
{@code
+     * Map payload = client.zpopmax("mySortedSet").get();
+     * assert payload.equals(Map.of('member1', 10.0)); // Indicates that 'member1' with a score of 10.0 has been removed from the sorted set.
+     * }
+ */ + CompletableFuture> zpopmax(String key); + /** * Returns the score of member in the sorted set stored at key. * diff --git a/java/client/src/main/java/glide/api/models/BaseTransaction.java b/java/client/src/main/java/glide/api/models/BaseTransaction.java index 44b2414f43..2e286df400 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -56,6 +56,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Time; import static redis_request.RedisRequestOuterClass.RequestType.Type; import static redis_request.RedisRequestOuterClass.RequestType.Unlink; +import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.XAdd; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; @@ -1278,6 +1279,42 @@ public T zcard(@NonNull String key) { return getThis(); } + /** + * Removes and returns up to count members with the highest scores from the sorted + * set stored at the specified key. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param count Specifies the quantity of members to pop.
+ * If count is higher than the sorted set's cardinality, returns all members and + * their scores, ordered from highest to lowest. + * @return Command Response - A map of the removed members and their scores, ordered from the one + * with the highest score to the one with the lowest.
+ * If key doesn't exist, it will be treated as an empty sorted set and the + * command returns an empty Map. + */ + public T zpopmax(@NonNull String key, long count) { + ArgsArray commandArgs = buildArgs(new String[] {key, Long.toString(count)}); + protobufTransaction.addCommands(buildCommand(ZPopMax, commandArgs)); + return getThis(); + } + + /** + * Removes and returns the member with the highest score from the sorted set stored at the + * specified key. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @return Command Response - A map containing the removed member and its corresponding score.
+ * If key doesn't exist, it will be treated as an empty sorted set and the + * command returns an empty Map. + */ + public T zpopmax(@NonNull String key) { + ArgsArray commandArgs = buildArgs(new String[] {key}); + protobufTransaction.addCommands(buildCommand(ZPopMax, commandArgs)); + return getThis(); + } + /** * Returns the score of member in the sorted set stored at key. * diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index 283057a428..5a508c1c5a 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -76,6 +76,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.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -1802,6 +1803,55 @@ public void zcard_returns_success() { assertEquals(value, payload); } + @SneakyThrows + @Test + public void zpopmax_returns_success() { + // setup + String key = "testKey"; + String[] arguments = new String[] {key}; + Map value = Map.of("member1", 2.5); + + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.>submitNewCommand(eq(ZPopMax), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture> response = service.zpopmax(key); + Map payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + + @SneakyThrows + @Test + public void zpopmax_with_count_returns_success() { + // setup + String key = "testKey"; + long count = 2L; + String[] arguments = new String[] {key, Long.toString(count)}; + Map value = Map.of("member1", 3.0, "member2", 1.0); + + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.>submitNewCommand(eq(ZPopMax), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture> response = service.zpopmax(key, count); + Map payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + @SneakyThrows @Test public void zscore_returns_success() { diff --git a/java/client/src/test/java/glide/api/models/TransactionTests.java b/java/client/src/test/java/glide/api/models/TransactionTests.java index 62091d0953..4088370968 100644 --- a/java/client/src/test/java/glide/api/models/TransactionTests.java +++ b/java/client/src/test/java/glide/api/models/TransactionTests.java @@ -55,6 +55,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.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -390,6 +391,12 @@ public void transaction_builds_protobuf_request(BaseTransaction transaction) transaction.zcard("key"); results.add(Pair.of(Zcard, ArgsArray.newBuilder().addArgs("key").build())); + transaction.zpopmax("key"); + results.add(Pair.of(ZPopMax, ArgsArray.newBuilder().addArgs("key").build())); + + transaction.zpopmax("key", 2); + results.add(Pair.of(ZPopMax, ArgsArray.newBuilder().addArgs("key").addArgs("2").build())); + transaction.zscore("key", "member"); results.add(Pair.of(ZScore, ArgsArray.newBuilder().addArgs("key").addArgs("member").build())); diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index 201ba65819..ce04672a13 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -1114,6 +1114,25 @@ public void zcard(BaseClient client) { assertTrue(executionException.getCause() instanceof RequestException); } + @SneakyThrows + @ParameterizedTest + @MethodSource("getClients") + public void zpopmax(BaseClient client) { + String key = UUID.randomUUID().toString(); + Map membersScores = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + assertEquals(3, client.zadd(key, membersScores).get()); + assertEquals(Map.of("c", 3.0), client.zpopmax(key).get()); + assertEquals(Map.of("b", 2.0, "a", 1.0), client.zpopmax(key, 3).get()); + assertTrue(client.zpopmax(key).get().isEmpty()); + assertTrue(client.zpopmax("non_existing_key").get().isEmpty()); + + // Key exists, but it is not a set + assertEquals(OK, client.set(key, "value").get()); + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> client.zpopmax(key).get()); + assertTrue(executionException.getCause() instanceof RequestException); + } + @SneakyThrows @ParameterizedTest @MethodSource("getClients") diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java index 1d2d0f9978..ef84438630 100644 --- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java +++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java @@ -89,6 +89,7 @@ public static BaseTransaction transactionTest(BaseTransaction baseTransact baseTransaction.zrem(key8, new String[] {"one"}); baseTransaction.zcard(key8); baseTransaction.zscore(key8, "two"); + baseTransaction.zpopmax(key8); baseTransaction.configSet(Map.of("timeout", "1000")); baseTransaction.configGet(new String[] {"timeout"}); @@ -149,6 +150,7 @@ public static Object[] transactionTestResult() { 1L, 2L, 2.0, // zscore(key8, "two") + Map.of("three", 3.0), // zpopmax(key8) OK, Map.of("timeout", "1000"), OK,