Skip to content

Commit b09f752

Browse files
Kiminnionobc
authored andcommitted
Add support for Redis SINTERCARD command.
This commit adds support for Redis 7.0+ SINTERCARD command in Spring Data Redis. The `sInterCard` API has been added to SetCommands and ClusterSetCommands for both Jedis and Lettuce. The high-level `intersectSize` API has been added to SetOperations implementations as well. Original Pull Request: #3210 Resolves: #2906 Signed-off-by: Kiminni <imk0980@gmail.com>
1 parent b8e52f8 commit b09f752

19 files changed

+417
-0
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
* @author Dennis Neufeld
8383
* @author Shyngys Sapraliyev
8484
* @author Jeonggyu Choi
85+
* @author Mingi Lee
8586
*/
8687
@NullUnmarked
8788
@SuppressWarnings({ "ConstantConditions", "deprecation" })
@@ -832,6 +833,11 @@ public Long sInterStore(byte[] destKey, byte[]... keys) {
832833
return convertAndReturn(delegate.sInterStore(destKey, keys), Converters.identityConverter());
833834
}
834835

836+
@Override
837+
public Long sInterCard(byte[]... keys) {
838+
return convertAndReturn(delegate.sInterCard(keys), Converters.identityConverter());
839+
}
840+
835841
@Override
836842
public Boolean sIsMember(byte[] key, byte[] value) {
837843
return convertAndReturn(delegate.sIsMember(key, value), Converters.identityConverter());
@@ -1824,6 +1830,11 @@ public Long sInterStore(String destKey, String... keys) {
18241830
return sInterStore(serialize(destKey), serializeMulti(keys));
18251831
}
18261832

1833+
@Override
1834+
public Long sInterCard(String... keys) {
1835+
return sInterCard(serializeMulti(keys));
1836+
}
1837+
18271838
@Override
18281839
public Boolean sIsMember(String key, String value) {
18291840
return sIsMember(serialize(key), serialize(value));

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
* @author Dennis Neufeld
6868
* @author Shyngys Sapraliyev
6969
* @author Tihomir Mateev
70+
* @author Mingi Lee
7071
* @since 2.0
7172
*/
7273
@Deprecated
@@ -890,6 +891,13 @@ default Long sInterStore(byte[] destKey, byte[]... keys) {
890891
return setCommands().sInterStore(destKey, keys);
891892
}
892893

894+
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
895+
@Override
896+
@Deprecated
897+
default Long sInterCard(byte[]... keys) {
898+
return setCommands().sInterCard(keys);
899+
}
900+
893901
/** @deprecated in favor of {@link RedisConnection#setCommands()}}. */
894902
@Override
895903
@Deprecated

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
*
4444
* @author Christoph Strobl
4545
* @author Mark Paluch
46+
* @author Mingi Lee
4647
* @since 2.0
4748
*/
4849
public interface ReactiveSetCommands {
@@ -775,6 +776,73 @@ default Mono<Long> sInterStore(ByteBuffer destinationKey, Collection<ByteBuffer>
775776
*/
776777
Flux<NumericResponse<SInterStoreCommand, Long>> sInterStore(Publisher<SInterStoreCommand> commands);
777778

779+
/**
780+
* {@code SINTERCARD} command parameters.
781+
*
782+
* @author Mingi Lee
783+
* @since 4.0
784+
* @see <a href="https://redis.io/commands/sintercard">Redis Documentation: SINTERCARD</a>
785+
*/
786+
class SInterCardCommand implements Command {
787+
788+
private final List<ByteBuffer> keys;
789+
790+
private SInterCardCommand(List<ByteBuffer> keys) {
791+
this.keys = keys;
792+
}
793+
794+
/**
795+
* Creates a new {@link SInterCardCommand} given a {@link Collection} of keys.
796+
*
797+
* @param keys must not be {@literal null}.
798+
* @return a new {@link SInterCardCommand} for a {@link Collection} of keys.
799+
*/
800+
public static SInterCardCommand keys(Collection<ByteBuffer> keys) {
801+
802+
Assert.notNull(keys, "Keys must not be null");
803+
804+
return new SInterCardCommand(new ArrayList<>(keys));
805+
}
806+
807+
@Override
808+
public @Nullable ByteBuffer getKey() {
809+
return null;
810+
}
811+
812+
/**
813+
* @return never {@literal null}.
814+
*/
815+
public List<ByteBuffer> getKeys() {
816+
return keys;
817+
}
818+
}
819+
820+
/**
821+
* Returns the cardinality of the set which would result from the intersection of all given sets at {@literal keys}.
822+
*
823+
* @param keys must not be {@literal null}.
824+
* @return
825+
* @see <a href="https://redis.io/commands/sintercard">Redis Documentation: SINTERCARD</a>
826+
* @since 4.0
827+
*/
828+
default Mono<Long> sInterCard(Collection<ByteBuffer> keys) {
829+
830+
Assert.notNull(keys, "Keys must not be null");
831+
832+
return sInterCard(Mono.just(SInterCardCommand.keys(keys))).next().map(NumericResponse::getOutput);
833+
}
834+
835+
/**
836+
* Returns the cardinality of the set which would result from the intersection of all given sets at
837+
* {@link SInterCardCommand#getKeys()}.
838+
*
839+
* @param commands must not be {@literal null}.
840+
* @return
841+
* @see <a href="https://redis.io/commands/sintercard">Redis Documentation: SINTERCARD</a>
842+
* @since 4.0
843+
*/
844+
Flux<NumericResponse<SInterCardCommand, Long>> sInterCard(Publisher<SInterCardCommand> commands);
845+
778846
/**
779847
* {@code SUNION} command parameters.
780848
*

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* @author Costin Leau
3030
* @author Christoph Strobl
3131
* @author Mark Paluch
32+
* @author Mingi Lee
3233
* @see RedisCommands
3334
*/
3435
@NullUnmarked
@@ -153,6 +154,16 @@ public interface RedisSetCommands {
153154
*/
154155
Long sInterStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... keys);
155156

157+
/**
158+
* Returns the cardinality of the set which would result from the intersection of all the given sets.
159+
*
160+
* @param keys must not be {@literal null}.
161+
* @return {@literal null} when used in pipeline / transaction.
162+
* @see <a href="https://redis.io/commands/sintercard">Redis Documentation: SINTERCARD</a>
163+
* @since 4.0
164+
*/
165+
Long sInterCard(byte @NonNull [] @NonNull... keys);
166+
156167
/**
157168
* Union all sets at given {@code keys}.
158169
*

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
* @author ihaohong
7575
* @author Shyngys Sapraliyev
7676
* @author Jeonggyu Choi
77+
* @author Mingi Lee
7778
* @see RedisCallback
7879
* @see RedisSerializer
7980
* @see StringRedisTemplate
@@ -1169,6 +1170,17 @@ String bLMove(@NonNull String sourceKey, @NonNull String destinationKey, @NonNul
11691170
*/
11701171
Long sInterStore(@NonNull String destKey, @NonNull String @NonNull... keys);
11711172

1173+
/**
1174+
* Returns the cardinality of the set which would result from the intersection of all the given sets.
1175+
*
1176+
* @param keys must not be {@literal null}.
1177+
* @return
1178+
* @see <a href="https://redis.io/commands/sintercard">Redis Documentation: SINTERCARD</a>
1179+
* @see RedisSetCommands#sInterCard(byte[]...)
1180+
* @since 4.0
1181+
*/
1182+
Long sInterCard(@NonNull String @NonNull... keys);
1183+
11721184
/**
11731185
* Union all sets at given {@code keys}.
11741186
*

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
/**
4141
* @author Christoph Strobl
4242
* @author Mark Paluch
43+
* @author Mingi Lee
4344
* @since 2.0
4445
*/
4546
class JedisClusterSetCommands implements RedisSetCommands {
@@ -230,6 +231,25 @@ public Long sInterStore(byte[] destKey, byte[]... keys) {
230231
return sAdd(destKey, result.toArray(new byte[result.size()][]));
231232
}
232233

234+
@Override
235+
public Long sInterCard(byte[]... keys) {
236+
237+
Assert.notNull(keys, "Keys must not be null");
238+
Assert.noNullElements(keys, "Keys must not contain null elements");
239+
240+
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
241+
try {
242+
return connection.getCluster().sintercard(keys);
243+
} catch (Exception ex) {
244+
throw convertJedisAccessException(ex);
245+
}
246+
}
247+
248+
// For multi-slot clusters, calculate intersection cardinality by performing intersection
249+
Set<byte[]> result = sInter(keys);
250+
return (long) result.size();
251+
}
252+
233253
@Override
234254
public Set<byte[]> sUnion(byte[]... keys) {
235255

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
/**
3939
* @author Christoph Strobl
4040
* @author Mark Paluch
41+
* @author Mingi Lee
4142
* @since 2.0
4243
*/
4344
@NullUnmarked
@@ -105,6 +106,15 @@ public Long sInterStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... k
105106
return connection.invoke().just(Jedis::sinterstore, PipelineBinaryCommands::sinterstore, destKey, keys);
106107
}
107108

109+
@Override
110+
public Long sInterCard(byte @NonNull [] @NonNull... keys) {
111+
112+
Assert.notNull(keys, "Keys must not be null");
113+
Assert.noNullElements(keys, "Keys must not contain null elements");
114+
115+
return connection.invoke().just(Jedis::sintercard, PipelineBinaryCommands::sintercard, keys);
116+
}
117+
108118
@Override
109119
public Boolean sIsMember(byte @NonNull [] key, byte @NonNull [] value) {
110120

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
/**
3232
* @author Christoph Strobl
3333
* @author Mark Paluch
34+
* @author Mingi Lee
3435
* @since 2.0
3536
*/
3637
class LettuceClusterSetCommands extends LettuceSetCommands {
@@ -118,6 +119,21 @@ public Long sInterStore(byte[] destKey, byte[]... keys) {
118119
return sAdd(destKey, result.toArray(new byte[result.size()][]));
119120
}
120121

122+
@Override
123+
public Long sInterCard(byte[]... keys) {
124+
125+
Assert.notNull(keys, "Keys must not be null");
126+
Assert.noNullElements(keys, "Keys must not contain null elements");
127+
128+
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
129+
return super.sInterCard(keys);
130+
}
131+
132+
// For multi-slot clusters, calculate intersection cardinality by performing intersection
133+
Set<byte[]> result = sInter(keys);
134+
return (long) result.size();
135+
}
136+
121137
@Override
122138
public Set<byte[]> sUnion(byte[]... keys) {
123139

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
/**
3737
* @author Christoph Strobl
3838
* @author Mark Paluch
39+
* @author Mingi Lee
3940
* @since 2.0
4041
*/
4142
class LettuceReactiveSetCommands implements ReactiveSetCommands {
@@ -175,6 +176,18 @@ public Flux<NumericResponse<SInterStoreCommand, Long>> sInterStore(Publisher<SIn
175176
}));
176177
}
177178

179+
@Override
180+
public Flux<NumericResponse<SInterCardCommand, Long>> sInterCard(Publisher<SInterCardCommand> commands) {
181+
182+
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
183+
184+
Assert.notNull(command.getKeys(), "Keys must not be null");
185+
186+
return cmd.sintercard(command.getKeys().toArray(new ByteBuffer[0]))
187+
.map(value -> new NumericResponse<>(command, value));
188+
}));
189+
}
190+
178191
@Override
179192
public Flux<CommandResponse<SUnionCommand, Flux<ByteBuffer>>> sUnion(Publisher<SUnionCommand> commands) {
180193

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
/**
4040
* @author Christoph Strobl
4141
* @author Mark Paluch
42+
* @author Mingi Lee
4243
* @since 2.0
4344
*/
4445
@NullUnmarked
@@ -106,6 +107,15 @@ public Long sInterStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... k
106107
return connection.invoke().just(RedisSetAsyncCommands::sinterstore, destKey, keys);
107108
}
108109

110+
@Override
111+
public Long sInterCard(byte @NonNull [] @NonNull... keys) {
112+
113+
Assert.notNull(keys, "Keys must not be null");
114+
Assert.noNullElements(keys, "Keys must not contain null elements");
115+
116+
return connection.invoke().just(RedisSetAsyncCommands::sintercard, keys);
117+
}
118+
109119
@Override
110120
public Boolean sIsMember(byte @NonNull [] key, byte @NonNull [] value) {
111121

0 commit comments

Comments
 (0)