Skip to content

Commit fa009cc

Browse files
SanHalacogluImprovingYury-Fridlyandaaron-congo
authored andcommitted
Java: Add Zrandmember command. (Sorted Set Commands) (valkey-io#1238)
* Java: Add Zrandmember command. (Sorted Set Commands) (#175) Signed-off-by: Yury-Fridlyand <[email protected]> Co-authored-by: Yury-Fridlyand <[email protected]> Co-authored-by: Aaron <[email protected]>
1 parent 5f4dbd7 commit fa009cc

File tree

10 files changed

+355
-2
lines changed

10 files changed

+355
-2
lines changed

glide-core/src/client/value_conversion.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
408408
b"HRANDFIELD" => cmd
409409
.position(b"WITHVALUES")
410410
.map(|_| ExpectedReturnType::ArrayOfKeyValuePairs),
411+
b"ZRANDMEMBER" => cmd
412+
.position(b"WITHSCORES")
413+
.map(|_| ExpectedReturnType::ArrayOfKeyValuePairs),
411414
b"ZADD" => cmd
412415
.position(b"INCR")
413416
.map(|_| ExpectedReturnType::DoubleOrNull),
@@ -514,7 +517,7 @@ mod tests {
514517
}
515518

516519
#[test]
517-
fn convert_hrandfield() {
520+
fn convert_array_of_kv_pairs() {
518521
assert!(matches!(
519522
expected_type_for_cmd(
520523
redis::cmd("HRANDFIELD")
@@ -528,6 +531,19 @@ mod tests {
528531
assert!(expected_type_for_cmd(redis::cmd("HRANDFIELD").arg("key").arg("1")).is_none());
529532
assert!(expected_type_for_cmd(redis::cmd("HRANDFIELD").arg("key")).is_none());
530533

534+
assert!(matches!(
535+
expected_type_for_cmd(
536+
redis::cmd("ZRANDMEMBER")
537+
.arg("key")
538+
.arg("1")
539+
.arg("withscores")
540+
),
541+
Some(ExpectedReturnType::ArrayOfKeyValuePairs)
542+
));
543+
544+
assert!(expected_type_for_cmd(redis::cmd("ZRANDMEMBER").arg("key").arg("1")).is_none());
545+
assert!(expected_type_for_cmd(redis::cmd("ZRANDMEMBER").arg("key")).is_none());
546+
531547
let flat_array = Value::Array(vec![
532548
Value::BulkString(b"key1".to_vec()),
533549
Value::BulkString(b"value1".to_vec()),

glide-core/src/protobuf/redis_request.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ enum RequestType {
180180
ZUnion = 136;
181181
BZPopMin = 137;
182182
FlushAll = 138;
183+
ZRandMember = 139;
183184
}
184185

185186
message Command {

glide-core/src/request_type.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub enum RequestType {
148148
ZUnion = 136,
149149
BZPopMin = 137,
150150
FlushAll = 138,
151+
ZRandMember = 139,
151152
}
152153

153154
fn get_two_word_command(first: &str, second: &str) -> Cmd {
@@ -299,6 +300,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
299300
ProtobufRequestType::ZUnion => RequestType::ZUnion,
300301
ProtobufRequestType::BZPopMin => RequestType::BZPopMin,
301302
ProtobufRequestType::FlushAll => RequestType::FlushAll,
303+
ProtobufRequestType::ZRandMember => RequestType::ZRandMember,
302304
}
303305
}
304306
}
@@ -446,6 +448,7 @@ impl RequestType {
446448
RequestType::ZUnion => Some(cmd("ZUNION")),
447449
RequestType::BZPopMin => Some(cmd("BZPOPMIN")),
448450
RequestType::FlushAll => Some(cmd("FLUSHALL")),
451+
RequestType::ZRandMember => Some(cmd("ZRANDMEMBER")),
449452
}
450453
}
451454
}

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import static redis_request.RedisRequestOuterClass.RequestType.ZMScore;
9292
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
9393
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
94+
import static redis_request.RedisRequestOuterClass.RequestType.ZRandMember;
9495
import static redis_request.RedisRequestOuterClass.RequestType.ZRangeStore;
9596
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex;
9697
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank;
@@ -1037,6 +1038,30 @@ public CompletableFuture<Map<String, Double>> zunionWithScores(
10371038
return commandManager.submitNewCommand(ZUnion, arguments, this::handleMapResponse);
10381039
}
10391040

1041+
@Override
1042+
public CompletableFuture<String> zrandmember(@NonNull String key) {
1043+
return commandManager.submitNewCommand(
1044+
ZRandMember, new String[] {key}, this::handleStringOrNullResponse);
1045+
}
1046+
1047+
@Override
1048+
public CompletableFuture<String[]> zrandmemberWithCount(@NonNull String key, long count) {
1049+
return commandManager.submitNewCommand(
1050+
ZRandMember,
1051+
new String[] {key, Long.toString(count)},
1052+
response -> castArray(handleArrayResponse(response), String.class));
1053+
}
1054+
1055+
@Override
1056+
public CompletableFuture<Object[][]> zrandmemberWithCountWithScores(
1057+
@NonNull String key, long count) {
1058+
String[] arguments = new String[] {key, Long.toString(count), WITH_SCORES_REDIS_API};
1059+
return commandManager.submitNewCommand(
1060+
ZRandMember,
1061+
arguments,
1062+
response -> castArray(handleArrayResponse(response), Object[].class));
1063+
}
1064+
10401065
@Override
10411066
public CompletableFuture<String> xadd(@NonNull String key, @NonNull Map<String, String> values) {
10421067
return xadd(key, values, StreamAddOptions.builder().build());

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

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,4 +1065,68 @@ CompletableFuture<Map<String, Double>> zunionWithScores(
10651065
* }</pre>
10661066
*/
10671067
CompletableFuture<Map<String, Double>> zunionWithScores(KeysOrWeightedKeys keysOrWeightedKeys);
1068+
1069+
/**
1070+
* Returns a random element from the sorted set stored at <code>key</code>.
1071+
*
1072+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1073+
* @param key The key of the sorted set.
1074+
* @return A <code>String</code> representing a random element from the sorted set.<br>
1075+
* If the sorted set does not exist or is empty, the response will be <code>null</code>.
1076+
* @example
1077+
* <pre>{@code
1078+
* String payload1 = client.zrandmember("mySortedSet").get();
1079+
* assert payload1.equals("GLIDE");
1080+
*
1081+
* String payload2 = client.zrandmember("nonExistingSortedSet").get();
1082+
* assert payload2 == null;
1083+
* }</pre>
1084+
*/
1085+
CompletableFuture<String> zrandmember(String key);
1086+
1087+
/**
1088+
* Retrieves random elements from the sorted set stored at <code>key</code>.
1089+
*
1090+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1091+
* @param key The key of the sorted set.
1092+
* @param count The number of elements to return.<br>
1093+
* If <code>count</code> is positive, returns unique elements.<br>
1094+
* If negative, allows for duplicates.<br>
1095+
* @return An <code>array</code> of elements from the sorted set.<br>
1096+
* If the sorted set does not exist or is empty, the response will be an empty <code>array
1097+
* </code>.
1098+
* @example
1099+
* <pre>{@code
1100+
* String[] payload1 = client.zrandmember("mySortedSet", -3).get();
1101+
* assert payload1.equals(new String[] {"GLIDE", "GLIDE", "JAVA"});
1102+
*
1103+
* String[] payload2 = client.zrandmember("nonExistingSortedSet", 3).get();
1104+
* assert payload2.length == 0;
1105+
* }</pre>
1106+
*/
1107+
CompletableFuture<String[]> zrandmemberWithCount(String key, long count);
1108+
1109+
/**
1110+
* Retrieves random elements along with their scores from the sorted set stored at <code>key
1111+
* </code>.
1112+
*
1113+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1114+
* @param key The key of the sorted set.
1115+
* @param count The number of elements to return.<br>
1116+
* If <code>count</code> is positive, returns unique elements.<br>
1117+
* If negative, allows duplicates.<br>
1118+
* @return An <code>array</code> of <code>[element, score]</code> <code>arrays</code>, where
1119+
* element is a <code>String</code> and score is a <code>Double</code>.<br>
1120+
* If the sorted set does not exist or is empty, the response will be an empty <code>array
1121+
* </code>.
1122+
* @example
1123+
* <pre>{@code
1124+
* Object[][] data = client.zrandmemberWithCountWithScores(key1, -3).get();
1125+
* assert data.length == 3;
1126+
* for (Object[] memberScorePair : data) {
1127+
* System.out.printf("Member: '%s', score: %d", memberScorePair[0], memberScorePair[1]);
1128+
* }
1129+
* }</pre>
1130+
*/
1131+
CompletableFuture<Object[][]> zrandmemberWithCountWithScores(String key, long count);
10681132
}

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import static redis_request.RedisRequestOuterClass.RequestType.ZMScore;
107107
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
108108
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
109+
import static redis_request.RedisRequestOuterClass.RequestType.ZRandMember;
109110
import static redis_request.RedisRequestOuterClass.RequestType.ZRangeStore;
110111
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex;
111112
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank;
@@ -1601,6 +1602,61 @@ public T zpopmin(@NonNull String key) {
16011602
return getThis();
16021603
}
16031604

1605+
/**
1606+
* Returns a random element from the sorted set stored at <code>key</code>.
1607+
*
1608+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1609+
* @param key The key of the sorted set.
1610+
* @return Command Response - A <code>String</code> representing a random element from the sorted
1611+
* set.<br>
1612+
* If the sorted set does not exist or is empty, the response will be <code>null</code>.
1613+
*/
1614+
public T zrandmember(@NonNull String key) {
1615+
ArgsArray commandArgs = buildArgs(key);
1616+
protobufTransaction.addCommands(buildCommand(ZRandMember, commandArgs));
1617+
return getThis();
1618+
}
1619+
1620+
/**
1621+
* Retrieves random elements from the sorted set stored at <code>key</code>.
1622+
*
1623+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1624+
* @param key The key of the sorted set.
1625+
* @param count The number of elements to return.<br>
1626+
* If <code>count</code> is positive, returns unique elements.<br>
1627+
* If negative, allows for duplicates.<br>
1628+
* @return Command Response - An <code>array</code> of elements from the sorted set.<br>
1629+
* If the sorted set does not exist or is empty, the response will be an empty <code>array
1630+
* </code>.
1631+
*/
1632+
public T zrandmemberWithCount(@NonNull String key, long count) {
1633+
ArgsArray commandArgs = buildArgs(key, Long.toString(count));
1634+
protobufTransaction.addCommands(buildCommand(ZRandMember, commandArgs));
1635+
return getThis();
1636+
}
1637+
1638+
/**
1639+
* Retrieves random elements along with their scores from the sorted set stored at <code>key
1640+
* </code>.
1641+
*
1642+
* @see <a href="https://redis.io/commands/zrandmember/">redis.io</a> for more details.
1643+
* @param key The key of the sorted set.
1644+
* @param count The number of elements to return.<br>
1645+
* If <code>count</code> is positive, returns unique elements.<br>
1646+
* If negative, allows duplicates.<br>
1647+
* @return Command Response - An <code>array</code> of <code>[element, score]</code> <code>arrays
1648+
* </code>, where element is a <code>String</code> and score is a <code>Double</code>.<br>
1649+
* If the sorted set does not exist or is empty, the response will be an empty <code>array
1650+
* </code>.
1651+
*/
1652+
public T zrandmemberWithCountWithScores(String key, long count) {
1653+
String[] arguments = new String[] {key, Long.toString(count), WITH_SCORES_REDIS_API};
1654+
1655+
ArgsArray commandArgs = buildArgs(arguments);
1656+
protobufTransaction.addCommands(buildCommand(ZRandMember, commandArgs));
1657+
return getThis();
1658+
}
1659+
16041660
/**
16051661
* Blocks the connection until it removes and returns a member with the lowest score from the
16061662
* sorted sets stored at the specified <code>keys</code>. The sorted sets are checked in the order

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

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
import static redis_request.RedisRequestOuterClass.RequestType.ZMScore;
129129
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
130130
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
131+
import static redis_request.RedisRequestOuterClass.RequestType.ZRandMember;
131132
import static redis_request.RedisRequestOuterClass.RequestType.ZRangeStore;
132133
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex;
133134
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank;
@@ -3252,11 +3253,84 @@ public void xadd_returns_success() {
32523253

32533254
// exercise
32543255
CompletableFuture<String> response = service.xadd(key, fieldValues);
3256+
3257+
// verify
3258+
assertEquals(testResponse, response);
3259+
assertEquals(returnId, response.get());
3260+
}
3261+
3262+
@SneakyThrows
3263+
@Test
3264+
public void zrandmember_returns_success() {
3265+
// setup
3266+
String key = "testKey";
3267+
String[] arguments = new String[] {key};
3268+
String value = "testValue";
3269+
3270+
CompletableFuture<String> testResponse = new CompletableFuture<>();
3271+
testResponse.complete(value);
3272+
3273+
// match on protobuf request
3274+
when(commandManager.<String>submitNewCommand(eq(ZRandMember), eq(arguments), any()))
3275+
.thenReturn(testResponse);
3276+
3277+
// exercise
3278+
CompletableFuture<String> response = service.zrandmember(key);
32553279
String payload = response.get();
32563280

32573281
// verify
32583282
assertEquals(testResponse, response);
3259-
assertEquals(returnId, payload);
3283+
assertEquals(value, payload);
3284+
}
3285+
3286+
@SneakyThrows
3287+
@Test
3288+
public void zrandmemberWithCount_returns_success() {
3289+
// setup
3290+
String key = "testKey";
3291+
long count = 2L;
3292+
String[] arguments = new String[] {key, Long.toString(count)};
3293+
String[] value = new String[] {"member1", "member2"};
3294+
3295+
CompletableFuture<String[]> testResponse = new CompletableFuture<>();
3296+
testResponse.complete(value);
3297+
3298+
// match on protobuf request
3299+
when(commandManager.<String[]>submitNewCommand(eq(ZRandMember), eq(arguments), any()))
3300+
.thenReturn(testResponse);
3301+
3302+
// exercise
3303+
CompletableFuture<String[]> response = service.zrandmemberWithCount(key, count);
3304+
String[] payload = response.get();
3305+
3306+
// verify
3307+
assertEquals(testResponse, response);
3308+
assertEquals(value, payload);
3309+
}
3310+
3311+
@SneakyThrows
3312+
@Test
3313+
public void zrandmemberWithCountWithScores_returns_success() {
3314+
// setup
3315+
String key = "testKey";
3316+
long count = 2L;
3317+
String[] arguments = new String[] {key, Long.toString(count), WITH_SCORES_REDIS_API};
3318+
Object[][] value = new Object[][] {{"member1", 2.0}, {"member2", 3.0}};
3319+
3320+
CompletableFuture<Object[][]> testResponse = new CompletableFuture<>();
3321+
testResponse.complete(value);
3322+
3323+
// match on protobuf request
3324+
when(commandManager.<Object[][]>submitNewCommand(eq(ZRandMember), eq(arguments), any()))
3325+
.thenReturn(testResponse);
3326+
3327+
// exercise
3328+
CompletableFuture<Object[][]> response = service.zrandmemberWithCountWithScores(key, count);
3329+
Object[] payload = response.get();
3330+
3331+
// verify
3332+
assertEquals(testResponse, response);
3333+
assertEquals(value, payload);
32603334
}
32613335

32623336
private static List<Arguments> getStreamAddOptions() {

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import static redis_request.RedisRequestOuterClass.RequestType.ZMScore;
117117
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMax;
118118
import static redis_request.RedisRequestOuterClass.RequestType.ZPopMin;
119+
import static redis_request.RedisRequestOuterClass.RequestType.ZRandMember;
119120
import static redis_request.RedisRequestOuterClass.RequestType.ZRangeStore;
120121
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByLex;
121122
import static redis_request.RedisRequestOuterClass.RequestType.ZRemRangeByRank;
@@ -595,6 +596,22 @@ InfScoreBound.NEGATIVE_INFINITY, new ScoreBoundary(3, false), new Limit(1, 2)),
595596
transaction.persist("key");
596597
results.add(Pair.of(Persist, buildArgs("key")));
597598

599+
transaction.zrandmember("key");
600+
results.add(Pair.of(ZRandMember, ArgsArray.newBuilder().addArgs("key").build()));
601+
602+
transaction.zrandmemberWithCount("key", 5);
603+
results.add(Pair.of(ZRandMember, ArgsArray.newBuilder().addArgs("key").addArgs("5").build()));
604+
605+
transaction.zrandmemberWithCountWithScores("key", 5);
606+
results.add(
607+
Pair.of(
608+
ZRandMember,
609+
ArgsArray.newBuilder()
610+
.addArgs("key")
611+
.addArgs("5")
612+
.addArgs(WITH_SCORES_REDIS_API)
613+
.build()));
614+
598615
transaction.type("key");
599616
results.add(Pair.of(Type, buildArgs("key")));
600617

0 commit comments

Comments
 (0)