Skip to content

Commit

Permalink
Java: Add Zpopmax command. (Sorted Set Commands) (valkey-io#1164)
Browse files Browse the repository at this point in the history
* Java: Add Zpopmax command. (Sorted Set Commands) (#149)

* Minor documentation update.

* Minor test update.

* Spotless

Signed-off-by: Andrew Carbonetto <[email protected]>

* Minor documentation update.

* Rebase + Spotless

---------

Signed-off-by: Andrew Carbonetto <[email protected]>
Co-authored-by: Andrew Carbonetto <[email protected]>
  • Loading branch information
SanHalacogluImproving and acarbonetto committed Apr 1, 2024
1 parent ceefa79 commit 6b054fb
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 0 deletions.
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 @@ -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;
Expand Down Expand Up @@ -617,6 +618,17 @@ public CompletableFuture<Long> zcard(@NonNull String key) {
return commandManager.submitNewCommand(Zcard, new String[] {key}, this::handleLongResponse);
}

@Override
public CompletableFuture<Map<String, Double>> zpopmax(@NonNull String key, long count) {
return commandManager.submitNewCommand(
ZPopMax, new String[] {key, Long.toString(count)}, this::handleMapResponse);
}

@Override
public CompletableFuture<Map<String, Double>> zpopmax(@NonNull String key) {
return commandManager.submitNewCommand(ZPopMax, new String[] {key}, this::handleMapResponse);
}

@Override
public CompletableFuture<Double> zscore(@NonNull String key, @NonNull String member) {
return commandManager.submitNewCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,44 @@ CompletableFuture<Double> zaddIncr(
*/
CompletableFuture<Long> zcard(String key);

/**
* Removes and returns up to <code>count</code> members with the highest scores from the sorted
* set stored at the specified <code>key</code>.
*
* @see <a href="https://redis.io/commands/zpopmax/">redis.io</a> for more details.
* @param key The key of the sorted set.
* @param count Specifies the quantity of members to pop.<br>
* If <code>count</code> 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.<br>
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
* command returns an empty <code>Map</code>.
* @example
* <pre>{@code
* Map<String, Double> 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.
* }</pre>
*/
CompletableFuture<Map<String, Double>> zpopmax(String key, long count);

/**
* Removes and returns the member with the highest score from the sorted set stored at the
* specified <code>key</code>.
*
* @see <a href="https://redis.io/commands/zpopmax/">redis.io</a> for more details.
* @param key The key of the sorted set.
* @return A map containing the removed member and its corresponding score.<br>
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
* command returns an empty <code>Map</code>.
* @example
* <pre>{@code
* Map<String, Double> 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.
* }</pre>
*/
CompletableFuture<Map<String, Double>> zpopmax(String key);

/**
* Returns the score of <code>member</code> in the sorted set stored at <code>key</code>.
*
Expand Down
37 changes: 37 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1278,6 +1279,42 @@ public T zcard(@NonNull String key) {
return getThis();
}

/**
* Removes and returns up to <code>count</code> members with the highest scores from the sorted
* set stored at the specified <code>key</code>.
*
* @see <a href="https://redis.io/commands/zpopmax/">redis.io</a> for more details.
* @param key The key of the sorted set.
* @param count Specifies the quantity of members to pop.<br>
* If <code>count</code> 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.<br>
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
* command returns an empty <code>Map</code>.
*/
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 <code>key</code>.
*
* @see <a href="https://redis.io/commands/zpopmax/">redis.io</a> for more details.
* @param key The key of the sorted set.
* @return Command Response - A map containing the removed member and its corresponding score.<br>
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
* command returns an empty <code>Map</code>.
*/
public T zpopmax(@NonNull String key) {
ArgsArray commandArgs = buildArgs(new String[] {key});
protobufTransaction.addCommands(buildCommand(ZPopMax, commandArgs));
return getThis();
}

/**
* Returns the score of <code>member</code> in the sorted set stored at <code>key</code>.
*
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 @@ -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;
Expand Down Expand Up @@ -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<String, Double> value = Map.of("member1", 2.5);

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

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

// exercise
CompletableFuture<Map<String, Double>> response = service.zpopmax(key);
Map<String, Double> 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<String, Double> value = Map.of("member1", 3.0, "member2", 1.0);

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

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

// exercise
CompletableFuture<Map<String, Double>> response = service.zpopmax(key, count);
Map<String, Double> payload = response.get();

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

@SneakyThrows
@Test
public void zscore_returns_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()));

Expand Down
19 changes: 19 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Double> 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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"});
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 6b054fb

Please sign in to comment.