Skip to content

Commit 69d0754

Browse files
authored
Java: Add SADD, SREM, SMEMBERS, and SCARD commands (Set Commands)
2 parents 98b1ed7 + a54b67d commit 69d0754

File tree

8 files changed

+378
-2
lines changed

8 files changed

+378
-2
lines changed

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
import static glide.ffi.resolvers.SocketListenerResolver.getSocket;
55
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
66
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
7+
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
8+
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
9+
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
10+
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
711
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
812

913
import glide.api.commands.ConnectionManagementCommands;
14+
import glide.api.commands.SetCommands;
1015
import glide.api.commands.StringCommands;
1116
import glide.api.models.commands.SetOptions;
1217
import glide.api.models.configuration.BaseClientConfiguration;
@@ -20,6 +25,7 @@
2025
import glide.managers.BaseCommandResponseResolver;
2126
import glide.managers.CommandManager;
2227
import glide.managers.ConnectionManager;
28+
import java.util.Set;
2329
import java.util.concurrent.CompletableFuture;
2430
import java.util.concurrent.ExecutionException;
2531
import java.util.function.BiFunction;
@@ -32,7 +38,7 @@
3238
/** Base Client class for Redis */
3339
@AllArgsConstructor
3440
public abstract class BaseClient
35-
implements AutoCloseable, ConnectionManagementCommands, StringCommands {
41+
implements AutoCloseable, ConnectionManagementCommands, StringCommands, SetCommands {
3642
/** Redis simple string response with "OK" */
3743
public static final String OK = ConstantResponse.OK.toString();
3844

@@ -149,10 +155,18 @@ protected String handleStringOrNullResponse(Response response) throws RedisExcep
149155
return handleRedisResponse(String.class, true, response);
150156
}
151157

158+
protected Long handleLongResponse(Response response) throws RedisException {
159+
return handleRedisResponse(Long.class, false, response);
160+
}
161+
152162
protected Object[] handleArrayResponse(Response response) {
153163
return handleRedisResponse(Object[].class, true, response);
154164
}
155165

166+
protected Set<String> handleSetResponse(Response response) {
167+
return handleRedisResponse(Set.class, false, response);
168+
}
169+
156170
@Override
157171
public CompletableFuture<String> ping() {
158172
return commandManager.submitNewCommand(Ping, new String[0], this::handleStringResponse);
@@ -181,4 +195,26 @@ public CompletableFuture<String> set(
181195
String[] arguments = ArrayUtils.addAll(new String[] {key, value}, options.toArgs());
182196
return commandManager.submitNewCommand(SetString, arguments, this::handleStringOrNullResponse);
183197
}
198+
199+
@Override
200+
public CompletableFuture<Long> sadd(String key, String[] members) {
201+
String[] arguments = ArrayUtils.addFirst(members, key);
202+
return commandManager.submitNewCommand(SAdd, arguments, this::handleLongResponse);
203+
}
204+
205+
@Override
206+
public CompletableFuture<Long> srem(String key, String[] members) {
207+
String[] arguments = ArrayUtils.addFirst(members, key);
208+
return commandManager.submitNewCommand(SRem, arguments, this::handleLongResponse);
209+
}
210+
211+
@Override
212+
public CompletableFuture<Set<String>> smembers(String key) {
213+
return commandManager.submitNewCommand(SMembers, new String[] {key}, this::handleSetResponse);
214+
}
215+
216+
@Override
217+
public CompletableFuture<Long> scard(String key) {
218+
return commandManager.submitNewCommand(SCard, new String[] {key}, this::handleLongResponse);
219+
}
184220
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/** Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 */
2+
package glide.api.commands;
3+
4+
import java.util.Set;
5+
import java.util.concurrent.CompletableFuture;
6+
7+
/**
8+
* Set Commands interface.
9+
*
10+
* @see <a href="https://redis.io/commands/?group=set">Set Commands</a>
11+
*/
12+
public interface SetCommands {
13+
/**
14+
* Add specified members to the set stored at <code>key</code>. Specified members that are already
15+
* a member of this set are ignored.
16+
*
17+
* @see <a href="https://redis.io/commands/sadd/">redis.io</a> for details.
18+
* @param key The <code>key</code> where members will be added to its set.
19+
* @param members A list of members to add to the set stored at <code>key</code>.
20+
* @return The number of members that were added to the set, excluding members already present.
21+
* @remarks If <code>key</code> does not exist, a new set is created before adding <code>members
22+
* </code>.
23+
* @example
24+
* <p><code>
25+
* int result = client.sadd("my_set", new String[]{"member1", "member2"}).get();
26+
* // result: 2
27+
* </code>
28+
*/
29+
CompletableFuture<Long> sadd(String key, String[] members);
30+
31+
/**
32+
* Remove specified members from the set stored at <code>key</code>. Specified members that are
33+
* not a member of this set are ignored.
34+
*
35+
* @see <a href="https://redis.io/commands/srem/">redis.io</a> for details.
36+
* @param key The <code>key</code> from which members will be removed.
37+
* @param members A list of members to remove from the set stored at <code>key</code>.
38+
* @return The number of members that were removed from the set, excluding non-existing members.
39+
* @remarks If <code>key</code> does not exist, it is treated as an empty set and this command
40+
* returns 0.
41+
* @example
42+
* <p><code>
43+
* int result = client.srem("my_set", new String[]{"member1", "member2"}).get();
44+
* // result: 2
45+
* </code>
46+
*/
47+
CompletableFuture<Long> srem(String key, String[] members);
48+
49+
/**
50+
* Retrieve all the members of the set value stored at <code>key</code>.
51+
*
52+
* @see <a href="https://redis.io/commands/smembers/">redis.io</a> for details.
53+
* @param key The key from which to retrieve the set members.
54+
* @return A <code>Set</code> of all members of the set.
55+
* @remarks If <code>key</code> does not exist an empty set will be returned.
56+
* @example
57+
* <p><code>
58+
* {@literal Set<String>} result = client.smembers("my_set").get();
59+
* // result: {"member1", "member2", "member3"}
60+
* </code>
61+
*/
62+
CompletableFuture<Set<String>> smembers(String key);
63+
64+
/**
65+
* Retrieve the set cardinality (number of elements) of the set stored at <code>key</code>.
66+
*
67+
* @see <a href="https://redis.io/commands/scard/">redis.io</a> for details.
68+
* @param key The key from which to retrieve the number of set members.
69+
* @return The cardinality (number of elements) of the set, or 0 if the key does not exist.
70+
* @example
71+
* <p><code>
72+
* int result = client.scard("my_set").get();
73+
* // result: 3
74+
* </code>
75+
*/
76+
CompletableFuture<Long> scard(String key);
77+
}

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
66
import static redis_request.RedisRequestOuterClass.RequestType.Info;
77
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
8+
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
9+
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
10+
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
11+
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
812
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
913

1014
import glide.api.models.commands.InfoOptions;
@@ -165,6 +169,74 @@ public T set(String key, String value, SetOptions options) {
165169
return getThis();
166170
}
167171

172+
/**
173+
* Add specified members to the set stored at <code>key</code>. Specified members that are already
174+
* a member of this set are ignored.
175+
*
176+
* @see <a href="https://redis.io/commands/sadd/">redis.io</a> for details.
177+
* @param key The <code>key</code> where members will be added to its set.
178+
* @param members A list of members to add to the set stored at <code>key</code>.
179+
* @return Command Response - The number of members that were added to the set, excluding members
180+
* already present.
181+
* @remarks If <code>key</code> does not exist, a new set is created before adding <code>members
182+
* </code>.
183+
*/
184+
public T sadd(String key, String[] members) {
185+
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(members, key));
186+
187+
protobufTransaction.addCommands(buildCommand(SAdd, commandArgs));
188+
return getThis();
189+
}
190+
191+
/**
192+
* Remove specified members from the set stored at <code>key</code>. Specified members that are
193+
* not a member of this set are ignored.
194+
*
195+
* @see <a href="https://redis.io/commands/srem/">redis.io</a> for details.
196+
* @param key The <code>key</code> from which members will be removed.
197+
* @param members A list of members to remove from the set stored at <code>key</code>.
198+
* @return Command Response - The number of members that were removed from the set, excluding
199+
* non-existing members.
200+
* @remarks If <code>key</code> does not exist, it is treated as an empty set and this command
201+
* returns 0.
202+
*/
203+
public T srem(String key, String[] members) {
204+
ArgsArray commandArgs = buildArgs(ArrayUtils.addFirst(members, key));
205+
206+
protobufTransaction.addCommands(buildCommand(SRem, commandArgs));
207+
return getThis();
208+
}
209+
210+
/**
211+
* Retrieve all the members of the set value stored at <code>key</code>.
212+
*
213+
* @see <a href="https://redis.io/commands/smembers/">redis.io</a> for details.
214+
* @param key The key from which to retrieve the set members.
215+
* @return Command Response - A <code>Set</code> of all members of the set.
216+
* @remarks If <code>key</code> does not exist an empty set will be returned.
217+
*/
218+
public T smembers(String key) {
219+
ArgsArray commandArgs = buildArgs(key);
220+
221+
protobufTransaction.addCommands(buildCommand(SMembers, commandArgs));
222+
return getThis();
223+
}
224+
225+
/**
226+
* Retrieve the set cardinality (number of elements) of the set stored at <code>key</code>.
227+
*
228+
* @see <a href="https://redis.io/commands/scard/">redis.io</a> for details.
229+
* @param key The key from which to retrieve the number of set members.
230+
* @return Command Response - The cardinality (number of elements) of the set, or 0 if the key
231+
* does not exist.
232+
*/
233+
public T scard(String key) {
234+
ArgsArray commandArgs = buildArgs(key);
235+
236+
protobufTransaction.addCommands(buildCommand(SCard, commandArgs));
237+
return getThis();
238+
}
239+
168240
/** Build protobuf {@link Command} object for given command and arguments. */
169241
protected Command buildCommand(RequestType requestType) {
170242
return buildCommand(requestType, buildArgs());

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

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,21 @@
1515
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
1616
import static redis_request.RedisRequestOuterClass.RequestType.Info;
1717
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
18+
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
19+
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
20+
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
21+
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
1822
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
1923

2024
import glide.api.models.commands.InfoOptions;
2125
import glide.api.models.commands.SetOptions;
2226
import glide.api.models.commands.SetOptions.Expiry;
2327
import glide.managers.CommandManager;
2428
import glide.managers.ConnectionManager;
29+
import java.util.Set;
2530
import java.util.concurrent.CompletableFuture;
2631
import lombok.SneakyThrows;
32+
import org.apache.commons.lang3.ArrayUtils;
2733
import org.junit.jupiter.api.BeforeEach;
2834
import org.junit.jupiter.api.Test;
2935

@@ -267,4 +273,100 @@ public void info_with_empty_InfoOptions_returns_success() {
267273
assertEquals(testResponse, response);
268274
assertEquals(testPayload, payload);
269275
}
276+
277+
@SneakyThrows
278+
@Test
279+
public void sadd_returns_success() {
280+
// setup
281+
String key = "testKey";
282+
String[] members = new String[] {"testMember1", "testMember2"};
283+
String[] arguments = ArrayUtils.addFirst(members, key);
284+
Long value = 2L;
285+
286+
CompletableFuture<Long> testResponse = mock(CompletableFuture.class);
287+
when(testResponse.get()).thenReturn(value);
288+
289+
// match on protobuf request
290+
when(commandManager.<Long>submitNewCommand(eq(SAdd), eq(arguments), any()))
291+
.thenReturn(testResponse);
292+
293+
// exercise
294+
CompletableFuture<Long> response = service.sadd(key, members);
295+
Long payload = response.get();
296+
297+
// verify
298+
assertEquals(testResponse, response);
299+
assertEquals(value, payload);
300+
}
301+
302+
@SneakyThrows
303+
@Test
304+
public void srem_returns_success() {
305+
// setup
306+
String key = "testKey";
307+
String[] members = new String[] {"testMember1", "testMember2"};
308+
String[] arguments = ArrayUtils.addFirst(members, key);
309+
Long value = 2L;
310+
311+
CompletableFuture<Long> testResponse = mock(CompletableFuture.class);
312+
when(testResponse.get()).thenReturn(value);
313+
314+
// match on protobuf request
315+
when(commandManager.<Long>submitNewCommand(eq(SRem), eq(arguments), any()))
316+
.thenReturn(testResponse);
317+
318+
// exercise
319+
CompletableFuture<Long> response = service.srem(key, members);
320+
Long payload = response.get();
321+
322+
// verify
323+
assertEquals(testResponse, response);
324+
assertEquals(value, payload);
325+
}
326+
327+
@SneakyThrows
328+
@Test
329+
public void smembers_returns_success() {
330+
// setup
331+
String key = "testKey";
332+
Set<String> value = Set.of("testMember");
333+
334+
CompletableFuture<Set<String>> testResponse = mock(CompletableFuture.class);
335+
when(testResponse.get()).thenReturn(value);
336+
337+
// match on protobuf request
338+
when(commandManager.<Set<String>>submitNewCommand(eq(SMembers), eq(new String[] {key}), any()))
339+
.thenReturn(testResponse);
340+
341+
// exercise
342+
CompletableFuture<Set<String>> response = service.smembers(key);
343+
Set<String> payload = response.get();
344+
345+
// verify
346+
assertEquals(testResponse, response);
347+
assertEquals(value, payload);
348+
}
349+
350+
@SneakyThrows
351+
@Test
352+
public void scard_returns_success() {
353+
// setup
354+
String key = "testKey";
355+
Long value = 2L;
356+
357+
CompletableFuture<Long> testResponse = mock(CompletableFuture.class);
358+
when(testResponse.get()).thenReturn(value);
359+
360+
// match on protobuf request
361+
when(commandManager.<Long>submitNewCommand(eq(SCard), eq(new String[] {key}), any()))
362+
.thenReturn(testResponse);
363+
364+
// exercise
365+
CompletableFuture<Long> response = service.scard(key);
366+
Long payload = response.get();
367+
368+
// verify
369+
assertEquals(testResponse, response);
370+
assertEquals(value, payload);
371+
}
270372
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import static redis_request.RedisRequestOuterClass.RequestType.GetString;
77
import static redis_request.RedisRequestOuterClass.RequestType.Info;
88
import static redis_request.RedisRequestOuterClass.RequestType.Ping;
9+
import static redis_request.RedisRequestOuterClass.RequestType.SAdd;
10+
import static redis_request.RedisRequestOuterClass.RequestType.SCard;
11+
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
12+
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
913
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
1014

1115
import glide.api.models.commands.InfoOptions;
@@ -57,6 +61,18 @@ public void transaction_builds_protobuf_request() {
5761
Info,
5862
ArgsArray.newBuilder().addArgs(InfoOptions.Section.EVERYTHING.toString()).build()));
5963

64+
transaction.sadd("key", new String[] {"value"});
65+
results.add(Pair.of(SAdd, ArgsArray.newBuilder().addArgs("key").addArgs("value").build()));
66+
67+
transaction.srem("key", new String[] {"value"});
68+
results.add(Pair.of(SRem, ArgsArray.newBuilder().addArgs("key").addArgs("value").build()));
69+
70+
transaction.smembers("key");
71+
results.add(Pair.of(SMembers, ArgsArray.newBuilder().addArgs("key").build()));
72+
73+
transaction.scard("key");
74+
results.add(Pair.of(SCard, ArgsArray.newBuilder().addArgs("key").build()));
75+
6076
var protobufTransaction = transaction.getProtobufTransaction().build();
6177

6278
for (int idx = 0; idx < protobufTransaction.getCommandsCount(); idx++) {

0 commit comments

Comments
 (0)