From 2b81092701efb6eff7c34f3ecd09e7b4136fd29e Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving <144171266+SanHalacogluImproving@users.noreply.github.com> Date: Fri, 22 Mar 2024 13:22:18 -0700 Subject: [PATCH 1/6] Java: Add Zpopmax command. (Sorted Set Commands) (#149) --- .../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 784f63f474..01c1e3b8e6 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.ZScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; @@ -613,6 +614,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..acec967162 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 'member1' with a score of 10.0 has 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 a0cd91ea89..88003304f0 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -57,6 +57,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Type; import static redis_request.RedisRequestOuterClass.RequestType.Unlink; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; @@ -1276,6 +1277,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 8647d6a741..8bf7b5b8dc 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -69,6 +69,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.Type; import static redis_request.RedisRequestOuterClass.RequestType.Unlink; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; @@ -1789,6 +1790,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 = mock(CompletableFuture.class); + when(testResponse.get()).thenReturn(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 = mock(CompletableFuture.class); + when(testResponse.get()).thenReturn(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 1aa39e169f..2c7fff8fc9 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.ZScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; @@ -388,6 +389,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 84d59a0fe5..4fe535d7b3 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -1113,6 +1113,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..2db5709ed0 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), OK, Map.of("timeout", "1000"), OK, From e8189776b4d81e659899099e1896fc8befa176b9 Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving Date: Wed, 27 Mar 2024 15:59:09 -0700 Subject: [PATCH 2/6] Minor documentation update. --- java/client/src/test/java/glide/api/RedisClientTest.java | 4 ++-- .../src/test/java/glide/TransactionTestUtilities.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index 8bf7b5b8dc..8a04afbb55 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -1823,8 +1823,8 @@ public void zpopmax_with_count_returns_success() { String[] arguments = new String[] {key, Long.toString(count)}; Map value = Map.of("member1", 3.0, "member2", 1.0); - CompletableFuture> testResponse = mock(CompletableFuture.class); - when(testResponse.get()).thenReturn(value); + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); // match on protobuf request when(commandManager.>submitNewCommand(eq(ZPopMax), eq(arguments), any())) diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java index 2db5709ed0..ef84438630 100644 --- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java +++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java @@ -150,7 +150,7 @@ public static Object[] transactionTestResult() { 1L, 2L, 2.0, // zscore(key8, "two") - Map.of("three", 3.0), + Map.of("three", 3.0), // zpopmax(key8) OK, Map.of("timeout", "1000"), OK, From 9190a037aeeacae20a897ae093432893219f15fd Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving Date: Thu, 28 Mar 2024 09:34:38 -0700 Subject: [PATCH 3/6] Minor test update. --- java/client/src/test/java/glide/api/RedisClientTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index 8a04afbb55..aacbd63971 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -1798,8 +1798,8 @@ public void zpopmax_returns_success() { String[] arguments = new String[] {key}; Map value = Map.of("member1", 2.5); - CompletableFuture> testResponse = mock(CompletableFuture.class); - when(testResponse.get()).thenReturn(value); + CompletableFuture> testResponse = new CompletableFuture<>(); + testResponse.complete(value); // match on protobuf request when(commandManager.>submitNewCommand(eq(ZPopMax), eq(arguments), any())) From 778c8f3da81105035a3d8f70e62d7be4a6ca5f2b Mon Sep 17 00:00:00 2001 From: Andrew Carbonetto Date: Mon, 1 Apr 2024 08:35:50 -0700 Subject: [PATCH 4/6] Spotless Signed-off-by: Andrew Carbonetto --- java/client/src/main/java/glide/api/BaseClient.java | 2 +- java/integTest/src/test/java/glide/SharedCommandTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index 01c1e3b8e6..6490289b30 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -617,7 +617,7 @@ public CompletableFuture zcard(@NonNull String key) { @Override public CompletableFuture> zpopmax(@NonNull String key, long count) { return commandManager.submitNewCommand( - ZPopMax, new String[] {key, Long.toString(count)}, this::handleMapResponse); + ZPopMax, new String[] {key, Long.toString(count)}, this::handleMapResponse); } @Override diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index 4fe535d7b3..d648080f1f 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -1128,7 +1128,7 @@ public void zpopmax(BaseClient client) { // Key exists, but it is not a set assertEquals(OK, client.set(key, "value").get()); ExecutionException executionException = - assertThrows(ExecutionException.class, () -> client.zpopmax(key).get()); + assertThrows(ExecutionException.class, () -> client.zpopmax(key).get()); assertTrue(executionException.getCause() instanceof RequestException); } From b40a6aa9c495cc537c71ccb1279b7b2437a98604 Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving Date: Mon, 1 Apr 2024 09:30:02 -0700 Subject: [PATCH 5/6] Minor documentation update. --- .../src/main/java/glide/api/commands/SortedSetBaseCommands.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 acec967162..33a58ce849 100644 --- a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java @@ -200,7 +200,7 @@ CompletableFuture zaddIncr( * @example *
{@code
      * Map payload = client.zpopmax("mySortedSet", 2).get();
-     * assert payload.equals(Map.of('member2', 8.0, 'member3', 7.5)); // Indicates that 'member1' with a score of 10.0 has been removed from the sorted set.
+     * 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); From 1b4987e3ea5cefd8c26424907f5ccbd380a813e7 Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving Date: Mon, 1 Apr 2024 11:43:28 -0700 Subject: [PATCH 6/6] Rebase + Spotless --- java/client/src/main/java/glide/api/BaseClient.java | 2 +- java/client/src/main/java/glide/api/models/BaseTransaction.java | 2 +- java/client/src/test/java/glide/api/RedisClientTest.java | 2 +- .../client/src/test/java/glide/api/models/TransactionTests.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index 6490289b30..085b44c175 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -47,8 +47,8 @@ import static redis_request.RedisRequestOuterClass.RequestType.TTL; import static redis_request.RedisRequestOuterClass.RequestType.Type; import static redis_request.RedisRequestOuterClass.RequestType.Unlink; -import static redis_request.RedisRequestOuterClass.RequestType.ZScore; 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; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; 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 88003304f0..7993877399 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -56,8 +56,8 @@ 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.ZScore; 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; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; diff --git a/java/client/src/test/java/glide/api/RedisClientTest.java b/java/client/src/test/java/glide/api/RedisClientTest.java index aacbd63971..6bb6bbc75e 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -68,8 +68,8 @@ 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.ZScore; 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; import static redis_request.RedisRequestOuterClass.RequestType.Zrem; 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 2c7fff8fc9..65df81f957 100644 --- a/java/client/src/test/java/glide/api/models/TransactionTests.java +++ b/java/client/src/test/java/glide/api/models/TransactionTests.java @@ -54,8 +54,8 @@ 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.ZScore; 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; import static redis_request.RedisRequestOuterClass.RequestType.Zrem;