Skip to content

Commit 15c9aaf

Browse files
committed
Add support for SMISMEMBER.
Allows querying with a single command whether multiple elements are members of a set. Also, refine DefaultRedisSet.containsAll(…) implementation. Closes #2037
1 parent a0f90a8 commit 15c9aaf

25 files changed

+425
-2
lines changed

src/main/asciidoc/new-features.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele
77
== New in Spring Data Redis 2.6
88

99
* Support for `SubscriptionListener` when using `MessageListener` for subscription confirmation callbacks. `ReactiveRedisMessageListenerContainer` and `ReactiveRedisOperations` provide `receiveLater(…)` and `listenToLater(…)` methods to await until Redis acknowledges the subscription.
10-
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`).
10+
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`, `SMISMEMBER`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`).
1111

1212
[[new-in-2.5.0]]
1313
== New in Spring Data Redis 2.5

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

+18
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,15 @@ public Boolean sIsMember(byte[] key, byte[] value) {
11641164
return convertAndReturn(delegate.sIsMember(key, value), Converters.identityConverter());
11651165
}
11661166

1167+
/*
1168+
* (non-Javadoc)
1169+
* @see org.springframework.data.redis.connection.RedisSetCommands#sIsMember(byte[], byte[]...)
1170+
*/
1171+
@Override
1172+
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
1173+
return convertAndReturn(delegate.sMIsMember(key, values), Converters.identityConverter());
1174+
}
1175+
11671176
/*
11681177
* (non-Javadoc)
11691178
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])
@@ -2766,6 +2775,15 @@ public Boolean sIsMember(String key, String value) {
27662775
return sIsMember(serialize(key), serialize(value));
27672776
}
27682777

2778+
/*
2779+
* (non-Javadoc)
2780+
* @see org.springframework.data.redis.connection.StringRedisConnection#sMIsMember(java.lang.String, java.lang.String...)
2781+
*/
2782+
@Override
2783+
public List<Boolean> sMIsMember(String key, String... values) {
2784+
return sMIsMember(serialize(key), serializeMulti(values));
2785+
}
2786+
27692787
/*
27702788
* (non-Javadoc)
27712789
* @see org.springframework.data.redis.connection.StringRedisConnection#sMembers(java.lang.String)

src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java

+7
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,13 @@ default Boolean sIsMember(byte[] key, byte[] value) {
835835
return setCommands().sIsMember(key, value);
836836
}
837837

838+
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
839+
@Override
840+
@Deprecated
841+
default List<Boolean> sMIsMember(byte[] key, byte[]... value) {
842+
return setCommands().sMIsMember(key, value);
843+
}
844+
838845
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
839846
@Override
840847
@Deprecated

src/main/java/org/springframework/data/redis/connection/ReactiveSetCommands.java

+82-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
3333
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
3434
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand;
35+
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
3536
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
3637
import org.springframework.data.redis.core.ScanOptions;
3738
import org.springframework.lang.Nullable;
@@ -546,14 +547,94 @@ default Mono<Boolean> sIsMember(ByteBuffer key, ByteBuffer value) {
546547
}
547548

548549
/**
549-
* Check if set at {@link SIsMemberCommand#getKey()} contains {@link SIsMemberCommand#getKey()}.
550+
* Check if set at {@link SIsMemberCommand#getKey()} contains {@link SIsMemberCommand#getValue()}.
550551
*
551552
* @param commands must not be {@literal null}.
552553
* @return
553554
* @see <a href="https://redis.io/commands/sismember">Redis Documentation: SISMEMBER</a>
554555
*/
555556
Flux<BooleanResponse<SIsMemberCommand>> sIsMember(Publisher<SIsMemberCommand> commands);
556557

558+
/**
559+
* {@code SMISMEMBER} command parameters.
560+
*
561+
* @author Mark Paluch
562+
* @since 2.6
563+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
564+
*/
565+
class SMIsMemberCommand extends KeyCommand {
566+
567+
private final List<ByteBuffer> values;
568+
569+
private SMIsMemberCommand(@Nullable ByteBuffer key, List<ByteBuffer> values) {
570+
571+
super(key);
572+
573+
this.values = values;
574+
}
575+
576+
/**
577+
* Creates a new {@link SMIsMemberCommand} given one or more {@literal values}.
578+
*
579+
* @param value must not be {@literal null}.
580+
* @return a new {@link SMIsMemberCommand} for a {@literal value}.
581+
*/
582+
public static SMIsMemberCommand values(List<ByteBuffer> values) {
583+
584+
Assert.notNull(values, "Values must not be null!");
585+
Assert.notEmpty(values, "Values must not be empty!");
586+
587+
return new SMIsMemberCommand(null, values);
588+
}
589+
590+
/**
591+
* Applies the {@literal set} key. Constructs a new command instance with all previously configured properties.
592+
*
593+
* @param set must not be {@literal null}.
594+
* @return a new {@link SMIsMemberCommand} with {@literal set} applied.
595+
*/
596+
public SMIsMemberCommand of(ByteBuffer set) {
597+
598+
Assert.notNull(set, "Set key must not be null!");
599+
600+
return new SMIsMemberCommand(set, values);
601+
}
602+
603+
/**
604+
* @return
605+
*/
606+
public List<ByteBuffer> getValues() {
607+
return values;
608+
}
609+
}
610+
611+
/**
612+
* Check if set at {@code key} contains one or more {@code values}.
613+
*
614+
* @param key must not be {@literal null}.
615+
* @param values must not be {@literal null}.
616+
* @return {@literal null} when used in pipeline / transaction.
617+
* @since 2.6
618+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
619+
*/
620+
default Mono<List<Boolean>> sMIsMember(ByteBuffer key, List<ByteBuffer> values) {
621+
622+
Assert.notNull(key, "Key must not be null!");
623+
Assert.notNull(values, "Value must not be null!");
624+
625+
return sMIsMember(Mono.just(SMIsMemberCommand.values(values).of(key))).next().map(MultiValueResponse::getOutput);
626+
}
627+
628+
/**
629+
* Check if set at {@link SMIsMemberCommand#getKey()} contains {@link SMIsMemberCommand#getValues()}.
630+
*
631+
* @param commands must not be {@literal null}.
632+
* @return
633+
* @since 2.6
634+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
635+
*/
636+
Flux<MultiValueResponse<SMIsMemberCommand, Boolean>> sMIsMember(Publisher<SMIsMemberCommand> commands);
637+
557638
/**
558639
* {@code SINTER} command parameters.
559640
*

src/main/java/org/springframework/data/redis/connection/RedisSetCommands.java

+12
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ public interface RedisSetCommands {
108108
@Nullable
109109
Boolean sIsMember(byte[] key, byte[] value);
110110

111+
/**
112+
* Check if set at {@code key} contains one or more {@code values}.
113+
*
114+
* @param key must not be {@literal null}.
115+
* @param values must not be {@literal null}.
116+
* @return {@literal null} when used in pipeline / transaction.
117+
* @since 2.6
118+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
119+
*/
120+
@Nullable
121+
List<Boolean> sMIsMember(byte[] key, byte[]... values);
122+
111123
/**
112124
* Diff all sets for given {@code keys}.
113125
*

src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java

+13
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,19 @@ default Long lPos(String key, String element) {
10431043
*/
10441044
Boolean sIsMember(String key, String value);
10451045

1046+
/**
1047+
* Check if set at {@code key} contains one or more {@code values}.
1048+
*
1049+
* @param key must not be {@literal null}.
1050+
* @param values must not be {@literal null}.
1051+
* @return {@literal null} when used in pipeline / transaction.
1052+
* @since 2.6
1053+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
1054+
* @see RedisSetCommands#sMIsMember(byte[], byte[]...)
1055+
*/
1056+
@Nullable
1057+
List<Boolean> sMIsMember(String key, String... values);
1058+
10461059
/**
10471060
* Returns the members intersecting all given sets at {@code keys}.
10481061
*

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java

+18
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,24 @@ public Boolean sIsMember(byte[] key, byte[] value) {
177177
}
178178
}
179179

180+
/*
181+
* (non-Javadoc)
182+
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
183+
*/
184+
@Override
185+
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
186+
187+
Assert.notNull(key, "Key must not be null!");
188+
Assert.notNull(values, "Value must not be null!");
189+
Assert.noNullElements(values, "Values must not contain null elements!");
190+
191+
try {
192+
return connection.getCluster().smismember(key, values);
193+
} catch (Exception ex) {
194+
throw convertJedisAccessException(ex);
195+
}
196+
}
197+
180198
/*
181199
* (non-Javadoc)
182200
* @see org.springframework.data.redis.connection.RedisSetCommands#sInter(byte[][])

src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java

+14
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,20 @@ public Boolean sIsMember(byte[] key, byte[] value) {
136136
return connection.invoke().just(BinaryJedis::sismember, MultiKeyPipelineBase::sismember, key, value);
137137
}
138138

139+
/*
140+
* (non-Javadoc)
141+
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
142+
*/
143+
@Override
144+
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
145+
146+
Assert.notNull(key, "Key must not be null!");
147+
Assert.notNull(values, "Values must not be null!");
148+
Assert.noNullElements(values, "Values must not contain null elements!");
149+
150+
return connection.invoke().just(BinaryJedis::smismember, MultiKeyPipelineBase::smismember, key, values);
151+
}
152+
139153
/*
140154
* (non-Javadoc)
141155
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveSetCommands.java

+19
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
import java.nio.ByteBuffer;
2323

2424
import org.reactivestreams.Publisher;
25+
2526
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
2627
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
2728
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
2829
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
2930
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand;
31+
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
3032
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
3133
import org.springframework.data.redis.connection.ReactiveSetCommands;
3234
import org.springframework.util.Assert;
@@ -163,6 +165,23 @@ public Flux<BooleanResponse<SIsMemberCommand>> sIsMember(Publisher<SIsMemberComm
163165
}));
164166
}
165167

168+
/*
169+
* (non-Javadoc)
170+
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sMIsMember(org.reactivestreams.Publisher)
171+
*/
172+
@Override
173+
public Flux<MultiValueResponse<SMIsMemberCommand, Boolean>> sMIsMember(Publisher<SMIsMemberCommand> commands) {
174+
175+
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
176+
177+
Assert.notNull(command.getKey(), "Key must not be null!");
178+
Assert.notNull(command.getValues(), "Values must not be null!");
179+
180+
return cmd.smismember(command.getKey(), command.getValues().toArray(new ByteBuffer[0])).collectList()
181+
.map(value -> new MultiValueResponse<>(command, value));
182+
}));
183+
}
184+
166185
/*
167186
* (non-Javadoc)
168187
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInter(org.reactivestreams.Publisher)

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceSetCommands.java

+14
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ public Boolean sIsMember(byte[] key, byte[] value) {
137137
return connection.invoke().just(RedisSetAsyncCommands::sismember, key, value);
138138
}
139139

140+
/*
141+
* (non-Javadoc)
142+
* @see org.springframework.data.redis.connection.RedisSetCommands#sMIsMember(byte[], byte[]...)
143+
*/
144+
@Override
145+
public List<Boolean> sMIsMember(byte[] key, byte[]... values) {
146+
147+
Assert.notNull(key, "Key must not be null!");
148+
Assert.notNull(values, "Values must not be null!");
149+
Assert.noNullElements(values, "Values must not contain null elements!");
150+
151+
return connection.invoke().just(RedisSetAsyncCommands::smismember, key, values);
152+
}
153+
140154
/*
141155
* (non-Javadoc)
142156
* @see org.springframework.data.redis.connection.RedisSetCommands#sMembers(byte[])

src/main/java/org/springframework/data/redis/core/BoundSetOperations.java

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.Collection;
1919
import java.util.List;
20+
import java.util.Map;
2021
import java.util.Set;
2122

2223
import org.springframework.lang.Nullable;
@@ -88,6 +89,18 @@ public interface BoundSetOperations<K, V> extends BoundKeyOperations<K> {
8889
@Nullable
8990
Boolean isMember(Object o);
9091

92+
/**
93+
* Check if set at at the bound key contains one or more {@code values}.
94+
*
95+
* @param key must not be {@literal null}.
96+
* @param objects
97+
* @return {@literal null} when used in pipeline / transaction.
98+
* @since 2.6
99+
* @see <a href="https://redis.io/commands/smismember">Redis Documentation: SMISMEMBER</a>
100+
*/
101+
@Nullable
102+
Map<Object, Boolean> isMember(Object... objects);
103+
91104
/**
92105
* Returns the members intersecting all given sets at the bound key and {@code key}.
93106
*

src/main/java/org/springframework/data/redis/core/DefaultBoundSetOperations.java

+10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Collection;
2020
import java.util.List;
21+
import java.util.Map;
2122
import java.util.Set;
2223

2324
import org.springframework.data.redis.connection.DataType;
@@ -143,6 +144,15 @@ public Boolean isMember(Object o) {
143144
return ops.isMember(getKey(), o);
144145
}
145146

147+
/*
148+
* (non-Javadoc)
149+
* @see org.springframework.data.redis.core.BoundSetOperations#isMember(java.lang.Object...)
150+
*/
151+
@Override
152+
public Map<Object, Boolean> isMember(Object... objects) {
153+
return ops.isMember(getKey(), objects);
154+
}
155+
146156
/*
147157
* (non-Javadoc)
148158
* @see org.springframework.data.redis.core.BoundSetOperations#members()

src/main/java/org/springframework/data/redis/core/DefaultReactiveSetOperations.java

+30
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.util.ArrayList;
2323
import java.util.Collection;
2424
import java.util.Collections;
25+
import java.util.LinkedHashMap;
2526
import java.util.List;
27+
import java.util.Map;
2628
import java.util.function.Function;
2729

2830
import org.reactivestreams.Publisher;
@@ -151,6 +153,34 @@ public Mono<Boolean> isMember(K key, Object o) {
151153
return createMono(connection -> connection.sIsMember(rawKey(key), rawValue((V) o)));
152154
}
153155

156+
/*
157+
* (non-Javadoc)
158+
* @see org.springframework.data.redis.core.ReactiveSetOperations#isMember(java.lang.Object, java.lang.Object...)
159+
*/
160+
@Override
161+
public Mono<Map<Object, Boolean>> isMember(K key, Object... objects) {
162+
163+
Assert.notNull(key, "Key must not be null!");
164+
165+
return createMono(connection -> {
166+
167+
return Flux.fromArray((V[]) objects) //
168+
.map(this::rawValue) //
169+
.collectList() //
170+
.flatMap(rawValues -> connection.sMIsMember(rawKey(key), rawValues)) //
171+
.map(result -> {
172+
173+
Map<Object, Boolean> isMember = new LinkedHashMap<>(result.size());
174+
175+
for (int i = 0; i < objects.length; i++) {
176+
isMember.put(objects[i], result.get(i));
177+
}
178+
179+
return isMember;
180+
});
181+
});
182+
}
183+
154184
/*
155185
* (non-Javadoc)
156186
* @see org.springframework.data.redis.core.ReactiveSetOperations#intersect(java.lang.Object, java.lang.Object)

0 commit comments

Comments
 (0)