Skip to content

Commit 961a1bb

Browse files
Java: Added Zpopmin command. (Sorted Set Commands) (#1165)
* Java: Added Zpopmin command. (Sorted Set Commands) (#150)
1 parent a38b45f commit 961a1bb

7 files changed

Lines changed: 165 additions & 0 deletions

File tree

java/client/src/main/java/glide/api/BaseClient.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import static redis_request.RedisRequestOuterClass.RequestType.Type;
4949
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
5050
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
51+
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
5152
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
5253
import static redis_request.RedisRequestOuterClass.RequestType.Zadd;
5354
import static redis_request.RedisRequestOuterClass.RequestType.Zcard;
@@ -614,6 +615,17 @@ public CompletableFuture<Long> zcard(@NonNull String key) {
614615
return commandManager.submitNewCommand(Zcard, new String[] {key}, this::handleLongResponse);
615616
}
616617

618+
@Override
619+
public CompletableFuture<Map<String, Double>> zpopmin(@NonNull String key, long count) {
620+
return commandManager.submitNewCommand(
621+
ZPopMin, new String[] {key, Long.toString(count)}, this::handleMapResponse);
622+
}
623+
624+
@Override
625+
public CompletableFuture<Map<String, Double>> zpopmin(@NonNull String key) {
626+
return commandManager.submitNewCommand(ZPopMin, new String[] {key}, this::handleMapResponse);
627+
}
628+
617629
@Override
618630
public CompletableFuture<Map<String, Double>> zpopmax(@NonNull String key, long count) {
619631
return commandManager.submitNewCommand(

java/client/src/main/java/glide/api/commands/SortedSetBaseCommands.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,44 @@ CompletableFuture<Double> zaddIncr(
184184
*/
185185
CompletableFuture<Long> zcard(String key);
186186

187+
/**
188+
* Removes and returns up to <code>count</code> members with the lowest scores from the sorted set
189+
* stored at the specified <code>key</code>.
190+
*
191+
* @see <a href="https://redis.io/commands/zpopmin/">redis.io</a> for more details.
192+
* @param key The key of the sorted set.
193+
* @param count Specifies the quantity of members to pop.<br>
194+
* If <code>count</code> is higher than the sorted set's cardinality, returns all members and
195+
* their scores, ordered from lowest to highest.
196+
* @return A map of the removed members and their scores, ordered from the one with the lowest
197+
* score to the one with the highest.<br>
198+
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
199+
* command returns an empty <code>Map</code>.
200+
* @example
201+
* <pre>{@code
202+
* Map<String, Double> payload = client.zpopmax("mySortedSet", 2).get();
203+
* assert payload.equals(Map.of('member3', 7.5 , 'member2', 8.0)); // Indicates that 'member3' with a score of 7.5 and 'member2' with a score of 8.0 have been removed from the sorted set.
204+
* }</pre>
205+
*/
206+
CompletableFuture<Map<String, Double>> zpopmin(String key, long count);
207+
208+
/**
209+
* Removes and returns the member with the lowest score from the sorted set stored at the
210+
* specified <code>key</code>.
211+
*
212+
* @see <a href="https://redis.io/commands/zpopmin/">redis.io</a> for more details.
213+
* @param key The key of the sorted set.
214+
* @return A map containing the removed member and its corresponding score.<br>
215+
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
216+
* command returns an empty <code>Map</code>.
217+
* @example
218+
* <pre>{@code
219+
* Map<String, Double> payload = client.zpopmin("mySortedSet").get();
220+
* assert payload.equals(Map.of('member1', 5.0)); // Indicates that 'member1' with a score of 5.0 has been removed from the sorted set.
221+
* }</pre>
222+
*/
223+
CompletableFuture<Map<String, Double>> zpopmin(String key);
224+
187225
/**
188226
* Removes and returns up to <code>count</code> members with the highest scores from the sorted
189227
* set stored at the specified <code>key</code>.

java/client/src/main/java/glide/api/models/BaseTransaction.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import static redis_request.RedisRequestOuterClass.RequestType.Type;
5858
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
5959
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
60+
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
6061
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
6162
import static redis_request.RedisRequestOuterClass.RequestType.Zadd;
6263
import static redis_request.RedisRequestOuterClass.RequestType.Zcard;
@@ -1277,6 +1278,42 @@ public T zcard(@NonNull String key) {
12771278
return getThis();
12781279
}
12791280

1281+
/**
1282+
* Removes and returns up to <code>count</code> members with the lowest scores from the sorted set
1283+
* stored at the specified <code>key</code>.
1284+
*
1285+
* @see <a href="https://redis.io/commands/zpopmin/">redis.io</a> for more details.
1286+
* @param key The key of the sorted set.
1287+
* @param count Specifies the quantity of members to pop.<br>
1288+
* If <code>count</code> is higher than the sorted set's cardinality, returns all members and
1289+
* their scores, ordered from lowest to highest.
1290+
* @return Command Response - A map of the removed members and their scores, ordered from the one
1291+
* with the lowest score to the one with the highest.<br>
1292+
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
1293+
* command returns an empty <code>Map</code>.
1294+
*/
1295+
public T zpopmin(@NonNull String key, long count) {
1296+
ArgsArray commandArgs = buildArgs(new String[] {key, Long.toString(count)});
1297+
protobufTransaction.addCommands(buildCommand(ZPopMin, commandArgs));
1298+
return getThis();
1299+
}
1300+
1301+
/**
1302+
* Removes and returns the member with the lowest score from the sorted set stored at the
1303+
* specified <code>key</code>.
1304+
*
1305+
* @see <a href="https://redis.io/commands/zpopmin/">redis.io</a> for more details.
1306+
* @param key The key of the sorted set.
1307+
* @return Command Response - A map containing the removed member and its corresponding score.<br>
1308+
* If <code>key</code> doesn't exist, it will be treated as an empty sorted set and the
1309+
* command returns an empty <code>Map</code>.
1310+
*/
1311+
public T zpopmin(@NonNull String key) {
1312+
ArgsArray commandArgs = buildArgs(new String[] {key});
1313+
protobufTransaction.addCommands(buildCommand(ZPopMin, commandArgs));
1314+
return getThis();
1315+
}
1316+
12801317
/**
12811318
* Removes and returns up to <code>count</code> members with the highest scores from the sorted
12821319
* set stored at the specified <code>key</code>.

java/client/src/test/java/glide/api/RedisClientTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import static redis_request.RedisRequestOuterClass.RequestType.Type;
7070
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
7171
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
72+
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
7273
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
7374
import static redis_request.RedisRequestOuterClass.RequestType.Zadd;
7475
import static redis_request.RedisRequestOuterClass.RequestType.Zcard;
@@ -1790,6 +1791,55 @@ public void zcard_returns_success() {
17901791
assertEquals(value, payload);
17911792
}
17921793

1794+
@SneakyThrows
1795+
@Test
1796+
public void zpopmin_returns_success() {
1797+
// setup
1798+
String key = "testKey";
1799+
String[] arguments = new String[] {key};
1800+
Map<String, Double> value = Map.of("member1", 2.5);
1801+
1802+
CompletableFuture<Map<String, Double>> testResponse = new CompletableFuture<>();
1803+
testResponse.complete(value);
1804+
1805+
// match on protobuf request
1806+
when(commandManager.<Map<String, Double>>submitNewCommand(eq(ZPopMin), eq(arguments), any()))
1807+
.thenReturn(testResponse);
1808+
1809+
// exercise
1810+
CompletableFuture<Map<String, Double>> response = service.zpopmin(key);
1811+
Map<String, Double> payload = response.get();
1812+
1813+
// verify
1814+
assertEquals(testResponse, response);
1815+
assertEquals(value, payload);
1816+
}
1817+
1818+
@SneakyThrows
1819+
@Test
1820+
public void zpopmin_with_count_returns_success() {
1821+
// setup
1822+
String key = "testKey";
1823+
long count = 2L;
1824+
String[] arguments = new String[] {key, Long.toString(count)};
1825+
Map<String, Double> value = Map.of("member1", 2.0, "member2", 3.0);
1826+
1827+
CompletableFuture<Map<String, Double>> testResponse = new CompletableFuture<>();
1828+
testResponse.complete(value);
1829+
1830+
// match on protobuf request
1831+
when(commandManager.<Map<String, Double>>submitNewCommand(eq(ZPopMin), eq(arguments), any()))
1832+
.thenReturn(testResponse);
1833+
1834+
// exercise
1835+
CompletableFuture<Map<String, Double>> response = service.zpopmin(key, count);
1836+
Map<String, Double> payload = response.get();
1837+
1838+
// verify
1839+
assertEquals(testResponse, response);
1840+
assertEquals(value, payload);
1841+
}
1842+
17931843
@SneakyThrows
17941844
@Test
17951845
public void zpopmax_returns_success() {

java/client/src/test/java/glide/api/models/TransactionTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import static redis_request.RedisRequestOuterClass.RequestType.Type;
5656
import static redis_request.RedisRequestOuterClass.RequestType.Unlink;
5757
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
58+
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
5859
import static redis_request.RedisRequestOuterClass.RequestType.ZScore;
5960
import static redis_request.RedisRequestOuterClass.RequestType.Zadd;
6061
import static redis_request.RedisRequestOuterClass.RequestType.Zcard;
@@ -389,6 +390,12 @@ public void transaction_builds_protobuf_request(BaseTransaction<?> transaction)
389390
transaction.zcard("key");
390391
results.add(Pair.of(Zcard, ArgsArray.newBuilder().addArgs("key").build()));
391392

393+
transaction.zpopmin("key");
394+
results.add(Pair.of(ZPopMin, ArgsArray.newBuilder().addArgs("key").build()));
395+
396+
transaction.zpopmin("key", 2);
397+
results.add(Pair.of(ZPopMin, ArgsArray.newBuilder().addArgs("key").addArgs("2").build()));
398+
392399
transaction.zpopmax("key");
393400
results.add(Pair.of(ZPopMax, ArgsArray.newBuilder().addArgs("key").build()));
394401

java/integTest/src/test/java/glide/SharedCommandTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,25 @@ public void zcard(BaseClient client) {
11131113
assertTrue(executionException.getCause() instanceof RequestException);
11141114
}
11151115

1116+
@SneakyThrows
1117+
@ParameterizedTest
1118+
@MethodSource("getClients")
1119+
public void zpopmin(BaseClient client) {
1120+
String key = UUID.randomUUID().toString();
1121+
Map<String, Double> membersScores = Map.of("a", 1.0, "b", 2.0, "c", 3.0);
1122+
assertEquals(3, client.zadd(key, membersScores).get());
1123+
assertEquals(Map.of("a", 1.0), client.zpopmin(key).get());
1124+
assertEquals(Map.of("b", 2.0, "c", 3.0), client.zpopmin(key, 3).get());
1125+
assertTrue(client.zpopmin(key).get().isEmpty());
1126+
assertTrue(client.zpopmin("non_existing_key").get().isEmpty());
1127+
1128+
// Key exists, but it is not a set
1129+
assertEquals(OK, client.set(key, "value").get());
1130+
ExecutionException executionException =
1131+
assertThrows(ExecutionException.class, () -> client.zpopmin(key).get());
1132+
assertTrue(executionException.getCause() instanceof RequestException);
1133+
}
1134+
11161135
@SneakyThrows
11171136
@ParameterizedTest
11181137
@MethodSource("getClients")

java/integTest/src/test/java/glide/TransactionTestUtilities.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public static BaseTransaction<?> transactionTest(BaseTransaction<?> baseTransact
8989
baseTransaction.zrem(key8, new String[] {"one"});
9090
baseTransaction.zcard(key8);
9191
baseTransaction.zscore(key8, "two");
92+
baseTransaction.zpopmin(key8);
9293
baseTransaction.zpopmax(key8);
9394

9495
baseTransaction.configSet(Map.of("timeout", "1000"));
@@ -150,6 +151,7 @@ public static Object[] transactionTestResult() {
150151
1L,
151152
2L,
152153
2.0, // zscore(key8, "two")
154+
Map.of("two", 2.0), // zpopmin(key8)
153155
Map.of("three", 3.0), // zpopmax(key8)
154156
OK,
155157
Map.of("timeout", "1000"),

0 commit comments

Comments
 (0)