From 057a432dd1c53b1714d7ca2132ec9106a2d6411e Mon Sep 17 00:00:00 2001 From: SanHalacogluImproving <144171266+SanHalacogluImproving@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:00:39 -0700 Subject: [PATCH] Java: Add `Zrevrank` command. (#1337) * Java: Add `Zrevrank` command. (#241) * Minor documentation updates. * Minor documentation updates. --------- Co-authored-by: Yury-Fridlyand --- glide-core/src/protobuf/redis_request.proto | 1 + glide-core/src/request_type.rs | 3 ++ .../src/main/java/glide/api/BaseClient.java | 16 ++++++ .../api/commands/SortedSetBaseCommands.java | 54 +++++++++++++++++-- .../glide/api/models/BaseTransaction.java | 43 ++++++++++++++- .../test/java/glide/api/RedisClientTest.java | 51 ++++++++++++++++++ .../glide/api/models/TransactionTests.java | 7 +++ .../test/java/glide/SharedCommandTests.java | 24 +++++++++ .../java/glide/TransactionTestUtilities.java | 2 + .../cluster/ClusterTransactionTests.java | 20 +++++++ .../glide/standalone/TransactionTests.java | 18 +++++++ 11 files changed, 233 insertions(+), 6 deletions(-) diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 4b8a9dba5f..4f293f26f0 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -173,6 +173,7 @@ enum RequestType { ObjectFreq = 130; RenameNx = 131; Touch = 132; + ZRevRank = 133; } message Command { diff --git a/glide-core/src/request_type.rs b/glide-core/src/request_type.rs index 7e1eb3537e..5b7d394bdc 100644 --- a/glide-core/src/request_type.rs +++ b/glide-core/src/request_type.rs @@ -141,6 +141,7 @@ pub enum RequestType { ObjectFreq = 130, RenameNx = 131, Touch = 132, + ZRevRank = 133, } fn get_two_word_command(first: &str, second: &str) -> Cmd { @@ -285,6 +286,7 @@ impl From<::protobuf::EnumOrUnknown> for RequestType { ProtobufRequestType::BZPopMax => RequestType::BZPopMax, ProtobufRequestType::RenameNx => RequestType::RenameNx, ProtobufRequestType::Touch => RequestType::Touch, + ProtobufRequestType::ZRevRank => RequestType::ZRevRank, } } } @@ -425,6 +427,7 @@ impl RequestType { RequestType::BZPopMax => Some(cmd("BZPOPMAX")), RequestType::RenameNx => Some(cmd("RENAMENX")), RequestType::Touch => Some(cmd("TOUCH")), + RequestType::ZRevRank => Some(cmd("ZREVRANK")), } } } diff --git a/java/client/src/main/java/glide/api/BaseClient.java b/java/client/src/main/java/glide/api/BaseClient.java index 9d54194896..7920c05208 100644 --- a/java/client/src/main/java/glide/api/BaseClient.java +++ b/java/client/src/main/java/glide/api/BaseClient.java @@ -89,6 +89,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZRevRank; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -844,6 +845,21 @@ public CompletableFuture zrankWithScore(@NonNull String key, @NonNull Zrank, new String[] {key, member, WITH_SCORE_REDIS_API}, this::handleArrayOrNullResponse); } + @Override + public CompletableFuture zrevrank(@NonNull String key, @NonNull String member) { + return commandManager.submitNewCommand( + ZRevRank, new String[] {key, member}, this::handleLongOrNullResponse); + } + + @Override + public CompletableFuture zrevrankWithScore( + @NonNull String key, @NonNull String member) { + return commandManager.submitNewCommand( + ZRevRank, + new String[] {key, member, WITH_SCORE_REDIS_API}, + this::handleArrayOrNullResponse); + } + @Override public CompletableFuture zmscore(@NonNull String key, @NonNull String[] members) { String[] arguments = ArrayUtils.addFirst(members, key); 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 25f1b9993d..c5022bd11e 100644 --- a/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java +++ b/java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java @@ -507,7 +507,7 @@ CompletableFuture zrangestore( /** * Returns the rank of member in the sorted set stored at key, with - * scores ordered from low to high.
+ * scores ordered from low to high, starting from 0.
* To get the rank of member with its score, see {@link #zrankWithScore}. * * @see redis.io for more details. @@ -529,7 +529,7 @@ CompletableFuture zrangestore( /** * Returns the rank of member in the sorted set stored at key with its - * score, where scores are ordered from the lowest to highest. + * score, where scores are ordered from the lowest to highest, starting from 0.
* * @see redis.io for more details. * @param key The key of the sorted set. @@ -541,14 +541,60 @@ CompletableFuture zrangestore( * @example *
{@code
      * Object[] result1 = client.zrankWithScore("mySortedSet", "member2").get();
-     * assert ((Long)result1[0]) == 1L && ((Double)result1[1]) == 6.0; // Indicates that "member2" with score 6.0 has the second-lowest score in the sorted set "mySortedSet".
+     * assert ((Long) result1[0]) == 1L && ((Double) result1[1]) == 6.0; // Indicates that "member2" with score 6.0 has the second-lowest score in the sorted set "mySortedSet".
      *
      * Object[] result2 = client.zrankWithScore("mySortedSet", "nonExistingMember").get();
-     * assert num2 == null; // Indicates that "nonExistingMember" is not present in the sorted set "mySortedSet".
+     * assert result2 == null; // Indicates that "nonExistingMember" is not present in the sorted set "mySortedSet".
      * }
*/ CompletableFuture zrankWithScore(String key, String member); + /** + * Returns the rank of member in the sorted set stored at key, where + * scores are ordered from the highest to lowest, starting from 0.
+ * To get the rank of member with its score, see {@link #zrevrankWithScore}. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param member The member whose rank is to be retrieved. + * @return The rank of member in the sorted set, where ranks are ordered from high to + * low based on scores.
+ * If key doesn't exist, or if member is not present in the set, + * null will be returned. + * @example + *
{@code
+     * Long num1 = client.zrevrank("mySortedSet", "member2").get();
+     * assert num1 == 1L; // Indicates that "member2" has the second-highest score in the sorted set "mySortedSet".
+     *
+     * Long num2 = client.zrevrank("mySortedSet", "nonExistingMember").get();
+     * assert num2 == null; // Indicates that "nonExistingMember" is not present in the sorted set "mySortedSet".
+     * }
+ */ + CompletableFuture zrevrank(String key, String member); + + /** + * Returns the rank of member in the sorted set stored at key with its + * score, where scores are ordered from the highest to lowest, starting from 0. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param member The member whose rank is to be retrieved. + * @return An array containing the rank (as Long) and score (as Double) + * of member in the sorted set, where ranks are ordered from high to low based on + * scores.
+ * If key doesn't exist, or if member is not present in the set, + * null will be returned. + * @example + *
{@code
+     * Object[] result1 = client.zrevrankWithScore("mySortedSet", "member2").get();
+     * assert ((Long) result1[0]) == 1L && ((Double) result1[1]) == 6.0; // Indicates that "member2" with score 6.0 has the second-highest score in the sorted set "mySortedSet".
+     *
+     * Object[] result2 = client.zrevrankWithScore("mySortedSet", "nonExistingMember").get();
+     * assert result2 == null; // Indicates that "nonExistingMember" is not present in the sorted set "mySortedSet".
+     * }
+ */ + CompletableFuture zrevrankWithScore(String key, String member); + /** * Returns the scores associated with the specified members 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 2156d74c50..35b5c68100 100644 --- a/java/client/src/main/java/glide/api/models/BaseTransaction.java +++ b/java/client/src/main/java/glide/api/models/BaseTransaction.java @@ -104,6 +104,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZRevRank; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -1663,7 +1664,7 @@ public T zscore(@NonNull String key, @NonNull String member) { /** * Returns the rank of member in the sorted set stored at key, with - * scores ordered from low to high.
+ * scores ordered from low to high, starting from 0.
* To get the rank of member with its score, see {@link #zrankWithScore}. * * @see redis.io for more details. @@ -1681,7 +1682,7 @@ public T zrank(@NonNull String key, @NonNull String member) { /** * Returns the rank of member in the sorted set stored at key with its - * score, where scores are ordered from the lowest to highest. + * score, where scores are ordered from the lowest to highest, starting from 0.
* * @see redis.io for more details. * @param key The key of the sorted set. @@ -1697,6 +1698,44 @@ public T zrankWithScore(@NonNull String key, @NonNull String member) { return getThis(); } + /** + * Returns the rank of member in the sorted set stored at key, where + * scores are ordered from the highest to lowest, starting from 0.
+ * To get the rank of member with its score, see {@link #zrevrankWithScore}. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param member The member whose rank is to be retrieved. + * @return Command Response - The rank of member in the sorted set, where ranks are + * ordered from high to low based on scores.
+ * If key doesn't exist, or if member is not present in the set, + * null will be returned. + */ + public T zrevrank(@NonNull String key, @NonNull String member) { + ArgsArray commandArgs = buildArgs(key, member); + protobufTransaction.addCommands(buildCommand(ZRevRank, commandArgs)); + return getThis(); + } + + /** + * Returns the rank of member in the sorted set stored at key with its + * score, where scores are ordered from the highest to lowest, starting from 0. + * + * @see redis.io for more details. + * @param key The key of the sorted set. + * @param member The member whose rank is to be retrieved. + * @return Command Response - An array containing the rank (as Long) and score (as + * Double) of member in the sorted set, where ranks are ordered from + * high to low based on scores.
+ * If key doesn't exist, or if member is not present in the set, + * null will be returned. + */ + public T zrevrankWithScore(@NonNull String key, @NonNull String member) { + ArgsArray commandArgs = buildArgs(key, member, WITH_SCORE_REDIS_API); + protobufTransaction.addCommands(buildCommand(ZRevRank, commandArgs)); + return getThis(); + } + /** * Returns the scores associated with the specified members 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 6dcc9b2d22..5a6a9d1ba9 100644 --- a/java/client/src/test/java/glide/api/RedisClientTest.java +++ b/java/client/src/test/java/glide/api/RedisClientTest.java @@ -125,6 +125,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZRevRank; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -2601,6 +2602,56 @@ public void zrankWithScore_returns_success() { assertEquals(value, payload); } + @SneakyThrows + @Test + public void zrevrank_returns_success() { + // setup + String key = "testKey"; + String member = "testMember"; + String[] arguments = new String[] {key, member}; + Long value = 3L; + + CompletableFuture testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.submitNewCommand(eq(ZRevRank), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture response = service.zrevrank(key, member); + Long payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + + @SneakyThrows + @Test + public void zrevrankWithScore_returns_success() { + // setup + String key = "testKey"; + String member = "testMember"; + String[] arguments = new String[] {key, member, WITH_SCORE_REDIS_API}; + Object[] value = new Object[] {1, 6.0}; + + CompletableFuture testResponse = new CompletableFuture<>(); + testResponse.complete(value); + + // match on protobuf request + when(commandManager.submitNewCommand(eq(ZRevRank), eq(arguments), any())) + .thenReturn(testResponse); + + // exercise + CompletableFuture response = service.zrevrankWithScore(key, member); + Object[] payload = response.get(); + + // verify + assertEquals(testResponse, response); + assertEquals(value, payload); + } + @SneakyThrows @Test public void zmscore_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 21dfceed2c..69f9a0c469 100644 --- a/java/client/src/test/java/glide/api/models/TransactionTests.java +++ b/java/client/src/test/java/glide/api/models/TransactionTests.java @@ -111,6 +111,7 @@ import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank; import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByScore; +import static redis_request.RedisRequestOuterClass.RequestType.ZRevRank; import static redis_request.RedisRequestOuterClass.RequestType.ZScore; import static redis_request.RedisRequestOuterClass.RequestType.Zadd; import static redis_request.RedisRequestOuterClass.RequestType.Zcard; @@ -413,6 +414,12 @@ public void transaction_builds_protobuf_request(BaseTransaction transaction) transaction.zrankWithScore("key", "member"); results.add(Pair.of(Zrank, buildArgs("key", "member", WITH_SCORE_REDIS_API))); + transaction.zrevrank("key", "member"); + results.add(Pair.of(ZRevRank, buildArgs("key", "member"))); + + transaction.zrevrankWithScore("key", "member"); + results.add(Pair.of(ZRevRank, buildArgs("key", "member", WITH_SCORE_REDIS_API))); + transaction.zmscore("key", new String[] {"member1", "member2"}); results.add(Pair.of(ZMScore, buildArgs("key", "member1", "member2"))); diff --git a/java/integTest/src/test/java/glide/SharedCommandTests.java b/java/integTest/src/test/java/glide/SharedCommandTests.java index 06f7d8d351..5afa93b540 100644 --- a/java/integTest/src/test/java/glide/SharedCommandTests.java +++ b/java/integTest/src/test/java/glide/SharedCommandTests.java @@ -1785,6 +1785,30 @@ public void zrank(BaseClient client) { assertTrue(executionException.getCause() instanceof RequestException); } + @SneakyThrows + @ParameterizedTest(autoCloseArguments = false) + @MethodSource("getClients") + public void zrevrank(BaseClient client) { + String key = UUID.randomUUID().toString(); + Map membersScores = Map.of("one", 1.5, "two", 2.0, "three", 3.0); + assertEquals(3, client.zadd(key, membersScores).get()); + assertEquals(0, client.zrevrank(key, "three").get()); + + if (REDIS_VERSION.isGreaterThanOrEqualTo("7.2.0")) { + assertArrayEquals(new Object[] {2L, 1.5}, client.zrevrankWithScore(key, "one").get()); + assertNull(client.zrevrankWithScore(key, "nonExistingMember").get()); + assertNull(client.zrevrankWithScore("nonExistingKey", "nonExistingMember").get()); + } + assertNull(client.zrevrank(key, "nonExistingMember").get()); + assertNull(client.zrevrank("nonExistingKey", "nonExistingMember").get()); + + // Key exists, but it is not a set + assertEquals(OK, client.set(key, "value").get()); + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> client.zrevrank(key, "one").get()); + assertTrue(executionException.getCause() instanceof RequestException); + } + @SneakyThrows @ParameterizedTest(autoCloseArguments = false) @MethodSource("getClients") diff --git a/java/integTest/src/test/java/glide/TransactionTestUtilities.java b/java/integTest/src/test/java/glide/TransactionTestUtilities.java index 4c015c9b19..d6610495b1 100644 --- a/java/integTest/src/test/java/glide/TransactionTestUtilities.java +++ b/java/integTest/src/test/java/glide/TransactionTestUtilities.java @@ -124,6 +124,7 @@ public static BaseTransaction transactionTest(BaseTransaction baseTransact baseTransaction.zadd(key8, Map.of("one", 1.0, "two", 2.0, "three", 3.0)); baseTransaction.zrank(key8, "one"); + baseTransaction.zrevrank(key8, "one"); baseTransaction.zaddIncr(key8, "one", 3); baseTransaction.zrem(key8, new String[] {"one"}); baseTransaction.zcard(key8); @@ -244,6 +245,7 @@ public static Object[] transactionTestResult() { true, // smove(key7, setKey2, "baz") 3L, 0L, // zrank(key8, "one") + 2L, // zrevrank(key8, "one") 4.0, 1L, 2L, diff --git a/java/integTest/src/test/java/glide/cluster/ClusterTransactionTests.java b/java/integTest/src/test/java/glide/cluster/ClusterTransactionTests.java index 86168f7c43..fbea1e4fcc 100644 --- a/java/integTest/src/test/java/glide/cluster/ClusterTransactionTests.java +++ b/java/integTest/src/test/java/glide/cluster/ClusterTransactionTests.java @@ -1,6 +1,7 @@ /** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */ package glide.cluster; +import static glide.TestConfiguration.REDIS_VERSION; import static glide.TransactionTestUtilities.transactionTest; import static glide.TransactionTestUtilities.transactionTestResult; import static glide.api.BaseClient.OK; @@ -9,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import glide.TestConfiguration; import glide.api.RedisClusterClient; @@ -17,6 +19,8 @@ import glide.api.models.configuration.RedisClusterClientConfiguration; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Map; +import java.util.UUID; import lombok.SneakyThrows; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -140,4 +144,20 @@ public void lastsave() { // assertEquals(OK, response[0]); // assertTrue((long) response[1] >= 0L); // } + + @Test + @SneakyThrows + public void zrank_zrevrank_withscores() { + assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.2.0")); + String zSetKey1 = "{key}:zsetKey1-" + UUID.randomUUID(); + ClusterTransaction transaction = new ClusterTransaction(); + transaction.zadd(zSetKey1, Map.of("one", 1.0, "two", 2.0, "three", 3.0)); + transaction.zrankWithScore(zSetKey1, "one"); + transaction.zrevrankWithScore(zSetKey1, "one"); + + Object[] result = clusterClient.exec(transaction).get(); + assertEquals(3L, result[0]); + assertArrayEquals(new Object[] {0L, 1.0}, (Object[]) result[1]); + assertArrayEquals(new Object[] {2L, 1.0}, (Object[]) result[2]); + } } diff --git a/java/integTest/src/test/java/glide/standalone/TransactionTests.java b/java/integTest/src/test/java/glide/standalone/TransactionTests.java index 2460f35e5e..9c420cd800 100644 --- a/java/integTest/src/test/java/glide/standalone/TransactionTests.java +++ b/java/integTest/src/test/java/glide/standalone/TransactionTests.java @@ -1,6 +1,7 @@ /** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */ package glide.standalone; +import static glide.TestConfiguration.REDIS_VERSION; import static glide.TransactionTestUtilities.transactionTest; import static glide.TransactionTestUtilities.transactionTestResult; import static glide.api.BaseClient.OK; @@ -8,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import glide.TestConfiguration; import glide.api.RedisClient; @@ -167,4 +169,20 @@ public void objectRefcount() { assertEquals(OK, response[0]); assertTrue((long) response[1] >= 0L); } + + @Test + @SneakyThrows + public void zrank_zrevrank_withscores() { + assumeTrue(REDIS_VERSION.isGreaterThanOrEqualTo("7.2.0")); + String zSetKey1 = "{key}:zsetKey1-" + UUID.randomUUID(); + Transaction transaction = new Transaction(); + transaction.zadd(zSetKey1, Map.of("one", 1.0, "two", 2.0, "three", 3.0)); + transaction.zrankWithScore(zSetKey1, "one"); + transaction.zrevrankWithScore(zSetKey1, "one"); + + Object[] result = client.exec(transaction).get(); + assertEquals(3L, result[0]); + assertArrayEquals(new Object[] {0L, 1.0}, (Object[]) result[1]); + assertArrayEquals(new Object[] {2L, 1.0}, (Object[]) result[2]); + } }