Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 38d3576

Browse files
mp911dechristophstrobl
authored andcommittedJun 29, 2021
Add reactive support for ZDIFF, ZDIFFSTORE, ZINTER, and ZUNION commands.
See: #2041 & #2042 Original Pull Request: #2097
1 parent 8dc6f01 commit 38d3576

File tree

8 files changed

+1537
-347
lines changed

8 files changed

+1537
-347
lines changed
 

‎src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java

Lines changed: 739 additions & 199 deletions
Large diffs are not rendered by default.

‎src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,34 +1174,6 @@ default Long zRemRangeByScore(byte[] key, double min, double max) {
11741174
@Nullable
11751175
Set<byte[]> zInter(byte[]... sets);
11761176

1177-
/**
1178-
* Intersect sorted {@code sets}.
1179-
*
1180-
* @param aggregate must not be {@literal null}.
1181-
* @param weights must not be {@literal null}.
1182-
* @param sets must not be {@literal null}.
1183-
* @return {@literal null} when used in pipeline / transaction.
1184-
* @since 2.6
1185-
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
1186-
*/
1187-
@Nullable
1188-
default Set<byte[]> zInter(Aggregate aggregate, int[] weights, byte[]... sets) {
1189-
return zInter(aggregate, Weights.of(weights), sets);
1190-
}
1191-
1192-
/**
1193-
* Intersect sorted {@code sets}.
1194-
*
1195-
* @param aggregate must not be {@literal null}.
1196-
* @param weights must not be {@literal null}.
1197-
* @param sets must not be {@literal null}.
1198-
* @return {@literal null} when used in pipeline / transaction.
1199-
* @since 2.6
1200-
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
1201-
*/
1202-
@Nullable
1203-
Set<byte[]> zInter(Aggregate aggregate, Weights weights, byte[]... sets);
1204-
12051177
/**
12061178
* Intersect sorted {@code sets}.
12071179
*
@@ -1293,34 +1265,6 @@ default Long zInterStore(byte[] destKey, Aggregate aggregate, int[] weights, byt
12931265
@Nullable
12941266
Set<byte[]> zUnion(byte[]... sets);
12951267

1296-
/**
1297-
* Union sorted {@code sets}.
1298-
*
1299-
* @param aggregate must not be {@literal null}.
1300-
* @param weights must not be {@literal null}.
1301-
* @param sets must not be {@literal null}.
1302-
* @return {@literal null} when used in pipeline / transaction.
1303-
* @since 2.6
1304-
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
1305-
*/
1306-
@Nullable
1307-
default Set<byte[]> zUnion(Aggregate aggregate, int[] weights, byte[]... sets) {
1308-
return zUnion(aggregate, Weights.of(weights), sets);
1309-
}
1310-
1311-
/**
1312-
* Union sorted {@code sets}.
1313-
*
1314-
* @param aggregate must not be {@literal null}.
1315-
* @param weights must not be {@literal null}.
1316-
* @param sets must not be {@literal null}.
1317-
* @return {@literal null} when used in pipeline / transaction.
1318-
* @since 2.6
1319-
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
1320-
*/
1321-
@Nullable
1322-
Set<byte[]> zUnion(Aggregate aggregate, Weights weights, byte[]... sets);
1323-
13241268
/**
13251269
* Union sorted {@code sets}.
13261270
*

‎src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveClusterZSetCommands.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ class LettuceReactiveClusterZSetCommands extends LettuceReactiveZSetCommands imp
4141
super(connection);
4242
}
4343

44-
/* (non-Javadoc)
44+
/*
45+
* (non-Javadoc)
4546
* @see org.springframework.data.redis.connection.lettuce.LettuceReactiveZSetCommands#zUnionStore(org.reactivestreams.Publisher)
4647
*/
4748
@Override
48-
public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUnionStoreCommand> commands) {
49+
public Flux<NumericResponse<ZAggregateStoreCommand, Long>> zUnionStore(
50+
Publisher<? extends ZAggregateStoreCommand> commands) {
4951

5052
return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> {
5153

@@ -60,11 +62,13 @@ public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUn
6062
}));
6163
}
6264

63-
/* (non-Javadoc)
65+
/*
66+
* (non-Javadoc)
6467
* @see org.springframework.data.redis.connection.lettuce.LettuceReactiveZSetCommands#zInterStore(org.reactivestreams.Publisher)
6568
*/
6669
@Override
67-
public Flux<NumericResponse<ZInterStoreCommand, Long>> zInterStore(Publisher<ZInterStoreCommand> commands) {
70+
public Flux<NumericResponse<ZAggregateStoreCommand, Long>> zInterStore(
71+
Publisher<? extends ZAggregateStoreCommand> commands) {
6872
return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> {
6973

7074
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty.");

‎src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java

Lines changed: 167 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,18 @@ public Flux<CommandResponse<ZRangeCommand, Flux<Tuple>>> zRange(Publisher<ZRange
200200
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
201201
if (command.isWithScores()) {
202202

203-
result = cmd.zrangeWithScores(command.getKey(), start, stop)
204-
.map(this::toTuple);
203+
result = cmd.zrangeWithScores(command.getKey(), start, stop).map(this::toTuple);
205204
} else {
206205

207-
result = cmd.zrange(command.getKey(), start, stop)
208-
.map(value -> toTuple(value, Double.NaN));
206+
result = cmd.zrange(command.getKey(), start, stop).map(value -> toTuple(value, Double.NaN));
209207
}
210208
} else {
211209
if (command.isWithScores()) {
212210

213-
result = cmd.zrevrangeWithScores(command.getKey(), start, stop)
214-
.map(this::toTuple);
211+
result = cmd.zrevrangeWithScores(command.getKey(), start, stop).map(this::toTuple);
215212
} else {
216213

217-
result = cmd.zrevrange(command.getKey(), start, stop)
218-
.map(value -> toTuple(value, Double.NaN));
214+
result = cmd.zrevrange(command.getKey(), start, stop).map(value -> toTuple(value, Double.NaN));
219215
}
220216
}
221217

@@ -247,8 +243,7 @@ public Flux<CommandResponse<ZRangeByScoreCommand, Flux<Tuple>>> zRangeByScore(
247243
if (command.isWithScores()) {
248244

249245
if (!isLimited) {
250-
result = cmd.zrangebyscoreWithScores(command.getKey(), range)
251-
.map(this::toTuple);
246+
result = cmd.zrangebyscoreWithScores(command.getKey(), range).map(this::toTuple);
252247
} else {
253248
result = cmd
254249
.zrangebyscoreWithScores(command.getKey(), range, LettuceConverters.toLimit(command.getLimit().get()))
@@ -257,8 +252,7 @@ public Flux<CommandResponse<ZRangeByScoreCommand, Flux<Tuple>>> zRangeByScore(
257252
} else {
258253

259254
if (!isLimited) {
260-
result = cmd.zrangebyscore(command.getKey(), range)
261-
.map(value -> toTuple(value, Double.NaN));
255+
result = cmd.zrangebyscore(command.getKey(), range).map(value -> toTuple(value, Double.NaN));
262256
} else {
263257

264258
result = cmd.zrangebyscore(command.getKey(), range, LettuceConverters.toLimit(command.getLimit().get()))
@@ -272,20 +266,16 @@ public Flux<CommandResponse<ZRangeByScoreCommand, Flux<Tuple>>> zRangeByScore(
272266
if (command.isWithScores()) {
273267

274268
if (!isLimited) {
275-
result = cmd.zrevrangebyscoreWithScores(command.getKey(), range)
276-
.map(this::toTuple);
269+
result = cmd.zrevrangebyscoreWithScores(command.getKey(), range).map(this::toTuple);
277270
} else {
278271

279-
result = cmd
280-
.zrevrangebyscoreWithScores(command.getKey(), range,
281-
LettuceConverters.toLimit(command.getLimit().get()))
282-
.map(this::toTuple);
272+
result = cmd.zrevrangebyscoreWithScores(command.getKey(), range,
273+
LettuceConverters.toLimit(command.getLimit().get())).map(this::toTuple);
283274
}
284275
} else {
285276

286277
if (!isLimited) {
287-
result = cmd.zrevrangebyscore(command.getKey(), range)
288-
.map(value -> toTuple(value, Double.NaN));
278+
result = cmd.zrevrangebyscore(command.getKey(), range).map(value -> toTuple(value, Double.NaN));
289279
} else {
290280

291281
result = cmd.zrevrangebyscore(command.getKey(), range, LettuceConverters.toLimit(command.getLimit().get()))
@@ -520,26 +510,99 @@ public Flux<NumericResponse<ZRemRangeByLexCommand, Long>> zRemRangeByLex(Publish
520510

521511
/*
522512
* (non-Javadoc)
523-
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zUnionStore(org.reactivestreams.Publisher)
513+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zDiff(Publisher)
514+
*/
515+
@Override
516+
public Flux<CommandResponse<ZDiffCommand, Flux<ByteBuffer>>> zDiff(Publisher<? extends ZDiffCommand> commands) {
517+
518+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
519+
520+
Assert.notEmpty(command.getKeys(), "Keys must not be null or empty!");
521+
522+
ByteBuffer[] sourceKeys = command.getKeys().toArray(new ByteBuffer[0]);
523+
return new CommandResponse<>(command, cmd.zdiff(sourceKeys));
524+
}));
525+
}
526+
527+
/*
528+
* (non-Javadoc)
529+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zDiffWithScores(Publisher)
530+
*/
531+
@Override
532+
public Flux<CommandResponse<ZDiffCommand, Flux<Tuple>>> zDiffWithScores(Publisher<? extends ZDiffCommand> commands) {
533+
534+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
535+
536+
Assert.notEmpty(command.getKeys(), "Keys must not be null or empty!");
537+
538+
ByteBuffer[] sourceKeys = command.getKeys().toArray(new ByteBuffer[0]);
539+
return new CommandResponse<>(command, cmd.zdiffWithScores(sourceKeys).map(this::toTuple));
540+
}));
541+
}
542+
543+
/*
544+
* (non-Javadoc)
545+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zDiffStore(Publisher)
524546
*/
525547
@Override
526-
public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUnionStoreCommand> commands) {
548+
public Flux<NumericResponse<ZDiffStoreCommand, Long>> zDiffStore(Publisher<ZDiffStoreCommand> commands) {
527549

528550
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
529551

530552
Assert.notNull(command.getKey(), "Destination key must not be null!");
531553
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
532554

555+
ByteBuffer[] sourceKeys = command.getSourceKeys().toArray(new ByteBuffer[0]);
556+
return cmd.zdiffstore(command.getKey(), sourceKeys).map(value -> new NumericResponse<>(command, value));
557+
}));
558+
}
559+
560+
/*
561+
* (non-Javadoc)
562+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zInter(Publisher)
563+
*/
564+
@Override
565+
public Flux<CommandResponse<ZAggregateCommand, Flux<ByteBuffer>>> zInter(
566+
Publisher<? extends ZAggregateCommand> commands) {
567+
568+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
569+
570+
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
571+
533572
ZStoreArgs args = null;
534573
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
535574
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null,
536575
command.getWeights());
537576
}
538577

539-
ByteBuffer[] sourceKeys = command.getSourceKeys().stream().toArray(ByteBuffer[]::new);
540-
Mono<Long> result = args != null ? cmd.zunionstore(command.getKey(), args, sourceKeys)
541-
: cmd.zunionstore(command.getKey(), sourceKeys);
542-
return result.map(value -> new NumericResponse<>(command, value));
578+
ByteBuffer[] sourceKeys = command.getSourceKeys().toArray(new ByteBuffer[0]);
579+
Flux<ByteBuffer> result = args != null ? cmd.zinter(args, sourceKeys) : cmd.zinter(sourceKeys);
580+
return new CommandResponse<>(command, result);
581+
}));
582+
}
583+
584+
/*
585+
* (non-Javadoc)
586+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zInterWithScores(Publisher)
587+
*/
588+
@Override
589+
public Flux<CommandResponse<ZAggregateCommand, Flux<Tuple>>> zInterWithScores(
590+
Publisher<? extends ZAggregateCommand> commands) {
591+
592+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
593+
594+
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
595+
596+
ZStoreArgs args = null;
597+
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
598+
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null,
599+
command.getWeights());
600+
}
601+
602+
ByteBuffer[] sourceKeys = command.getSourceKeys().toArray(new ByteBuffer[0]);
603+
Flux<ScoredValue<ByteBuffer>> result = args != null ? cmd.zinterWithScores(args, sourceKeys)
604+
: cmd.zinterWithScores(sourceKeys);
605+
return new CommandResponse<>(command, result.map(this::toTuple));
543606
}));
544607
}
545608

@@ -548,7 +611,8 @@ public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUn
548611
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zInterStore(org.reactivestreams.Publisher)
549612
*/
550613
@Override
551-
public Flux<NumericResponse<ZInterStoreCommand, Long>> zInterStore(Publisher<ZInterStoreCommand> commands) {
614+
public Flux<NumericResponse<ZAggregateStoreCommand, Long>> zInterStore(
615+
Publisher<? extends ZAggregateStoreCommand> commands) {
552616

553617
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
554618

@@ -561,13 +625,88 @@ public Flux<NumericResponse<ZInterStoreCommand, Long>> zInterStore(Publisher<ZIn
561625
command.getWeights());
562626
}
563627

564-
ByteBuffer[] sourceKeys = command.getSourceKeys().stream().toArray(ByteBuffer[]::new);
628+
ByteBuffer[] sourceKeys = command.getSourceKeys().toArray(new ByteBuffer[0]);
565629
Mono<Long> result = args != null ? cmd.zinterstore(command.getKey(), args, sourceKeys)
566630
: cmd.zinterstore(command.getKey(), sourceKeys);
567631
return result.map(value -> new NumericResponse<>(command, value));
568632
}));
569633
}
570634

635+
/*
636+
* (non-Javadoc)
637+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zUnion(org.reactivestreams.Publisher)
638+
*/
639+
@Override
640+
public Flux<CommandResponse<ZAggregateCommand, Flux<ByteBuffer>>> zUnion(
641+
Publisher<? extends ZAggregateCommand> commands) {
642+
643+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
644+
645+
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
646+
647+
ZStoreArgs args = null;
648+
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
649+
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null,
650+
command.getWeights());
651+
}
652+
653+
ByteBuffer[] sourceKeys = command.getSourceKeys().stream().toArray(ByteBuffer[]::new);
654+
Flux<ByteBuffer> result = args != null ? cmd.zunion(args, sourceKeys) : cmd.zunion(sourceKeys);
655+
return new CommandResponse<>(command, result);
656+
}));
657+
}
658+
659+
/*
660+
* (non-Javadoc)
661+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zUnion(org.reactivestreams.Publisher)
662+
*/
663+
@Override
664+
public Flux<CommandResponse<ZAggregateCommand, Flux<Tuple>>> zUnionWithScores(
665+
Publisher<? extends ZAggregateCommand> commands) {
666+
667+
return connection.execute(cmd -> Flux.from(commands).map(command -> {
668+
669+
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
670+
671+
ZStoreArgs args = null;
672+
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
673+
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null,
674+
command.getWeights());
675+
}
676+
677+
ByteBuffer[] sourceKeys = command.getSourceKeys().stream().toArray(ByteBuffer[]::new);
678+
Flux<ScoredValue<ByteBuffer>> result = args != null ? cmd.zunionWithScores(args, sourceKeys)
679+
: cmd.zunionWithScores(sourceKeys);
680+
return new CommandResponse<>(command, result.map(this::toTuple));
681+
}));
682+
}
683+
684+
/*
685+
* (non-Javadoc)
686+
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zUnionStore(org.reactivestreams.Publisher)
687+
*/
688+
@Override
689+
public Flux<NumericResponse<ZAggregateStoreCommand, Long>> zUnionStore(
690+
Publisher<? extends ZAggregateStoreCommand> commands) {
691+
692+
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
693+
694+
Assert.notNull(command.getKey(), "Destination key must not be null!");
695+
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
696+
697+
ZStoreArgs args = null;
698+
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
699+
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null,
700+
command.getWeights());
701+
}
702+
703+
ByteBuffer[] sourceKeys = command.getSourceKeys().stream().toArray(ByteBuffer[]::new);
704+
Mono<Long> result = args != null ? cmd.zunionstore(command.getKey(), args, sourceKeys)
705+
: cmd.zunionstore(command.getKey(), sourceKeys);
706+
return result.map(value -> new NumericResponse<>(command, value));
707+
}));
708+
}
709+
571710
/*
572711
* (non-Javadoc)
573712
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRangeByLex(org.reactivestreams.Publisher)

‎src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java

Lines changed: 158 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import java.util.function.Function;
2828

2929
import org.reactivestreams.Publisher;
30-
3130
import org.springframework.data.domain.Range;
3231
import org.springframework.data.redis.connection.DefaultTuple;
3332
import org.springframework.data.redis.connection.ReactiveZSetCommands;
@@ -501,26 +500,114 @@ public Mono<Long> removeRangeByScore(K key, Range<Double> range) {
501500
return createMono(connection -> connection.zRemRangeByScore(rawKey(key), range));
502501
}
503502

504-
/*
503+
/*
505504
* (non-Javadoc)
506-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
505+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#difference(K, Collection)
507506
*/
508507
@Override
509-
public Mono<Long> unionAndStore(K key, K otherKey, K destKey) {
508+
public Flux<V> difference(K key, Collection<K> otherKeys) {
510509

511510
Assert.notNull(key, "Key must not be null!");
512-
Assert.notNull(otherKey, "Other key must not be null!");
511+
Assert.notNull(otherKeys, "Other keys must not be null!");
512+
513+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
514+
.map(this::rawKey) //
515+
.collectList() //
516+
.flatMapMany(connection::zDiff).map(this::readValue));
517+
}
518+
519+
/*
520+
* (non-Javadoc)
521+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#differenceWithScores(K, Collection)
522+
*/
523+
@Override
524+
public Flux<TypedTuple<V>> differenceWithScores(K key, Collection<K> otherKeys) {
525+
526+
Assert.notNull(key, "Key must not be null!");
527+
Assert.notNull(otherKeys, "Other keys must not be null!");
528+
529+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
530+
.map(this::rawKey) //
531+
.collectList() //
532+
.flatMapMany(connection::zDiffWithScores).map(this::readTypedTuple));
533+
}
534+
535+
/*
536+
* (non-Javadoc)
537+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#differenceAndStore(K, Collection, K)
538+
*/
539+
@Override
540+
public Mono<Long> differenceAndStore(K key, Collection<K> otherKeys, K destKey) {
541+
542+
Assert.notNull(key, "Key must not be null!");
543+
Assert.notNull(otherKeys, "Other keys must not be null!");
513544
Assert.notNull(destKey, "Destination key must not be null!");
514545

515-
return unionAndStore(key, Collections.singleton(otherKey), destKey);
546+
return createMono(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
547+
.map(this::rawKey) //
548+
.collectList() //
549+
.flatMap(serialized -> connection.zDiffStore(rawKey(destKey), serialized)));
550+
551+
}
552+
553+
/*
554+
* (non-Javadoc)
555+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersect(K, Collection)
556+
*/
557+
@Override
558+
public Flux<V> intersect(K key, Collection<K> otherKeys) {
559+
560+
Assert.notNull(key, "Key must not be null!");
561+
Assert.notNull(otherKeys, "Other keys must not be null!");
562+
563+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
564+
.map(this::rawKey) //
565+
.collectList() //
566+
.flatMapMany(connection::zInter).map(this::readValue));
567+
}
568+
569+
/*
570+
* (non-Javadoc)
571+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectWithScores(K, Collection)
572+
*/
573+
@Override
574+
public Flux<TypedTuple<V>> intersectWithScores(K key, Collection<K> otherKeys) {
575+
576+
Assert.notNull(key, "Key must not be null!");
577+
Assert.notNull(otherKeys, "Other keys must not be null!");
578+
579+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
580+
.map(this::rawKey) //
581+
.collectList() //
582+
.flatMapMany(connection::zInterWithScores).map(this::readTypedTuple));
583+
}
584+
585+
/*
586+
* (non-Javadoc)
587+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectWithScores(K, Collection, Aggregate, Weights)
588+
*/
589+
@Override
590+
public Flux<TypedTuple<V>> intersectWithScores(K key, Collection<K> otherKeys, Aggregate aggregate, Weights weights) {
591+
592+
// TODO: Inconsistent method signatures Aggregate/Weights vs Weights/Aggregate in Connection API
593+
594+
Assert.notNull(key, "Key must not be null!");
595+
Assert.notNull(otherKeys, "Other keys must not be null!");
596+
Assert.notNull(aggregate, "Aggregate must not be null!");
597+
Assert.notNull(weights, "Weights must not be null!");
598+
599+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
600+
.map(this::rawKey) //
601+
.collectList() //
602+
.flatMapMany(sets -> connection.zInterWithScores(sets, weights, aggregate)).map(this::readTypedTuple));
516603
}
517604

518605
/*
519606
* (non-Javadoc)
520-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
607+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
521608
*/
522609
@Override
523-
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey) {
610+
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
524611

525612
Assert.notNull(key, "Key must not be null!");
526613
Assert.notNull(otherKeys, "Other keys must not be null!");
@@ -529,15 +616,15 @@ public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey) {
529616
return createMono(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
530617
.map(this::rawKey) //
531618
.collectList() //
532-
.flatMap(serialized -> connection.zUnionStore(rawKey(destKey), serialized)));
619+
.flatMap(serialized -> connection.zInterStore(rawKey(destKey), serialized)));
533620
}
534621

535622
/*
536623
* (non-Javadoc)
537-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object, org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights)
624+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object, org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights)
538625
*/
539626
@Override
540-
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
627+
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
541628

542629
Assert.notNull(key, "Key must not be null!");
543630
Assert.notNull(otherKeys, "Other keys must not be null!");
@@ -548,29 +635,79 @@ public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggre
548635
return createMono(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
549636
.map(this::rawKey) //
550637
.collectList() //
551-
.flatMap(serialized -> connection.zUnionStore(rawKey(destKey), serialized, weights, aggregate)));
638+
.flatMap(serialized -> connection.zInterStore(rawKey(destKey), serialized, weights, aggregate)));
639+
}
640+
641+
/*
642+
* (non-Javadoc)
643+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#union(K, Collection)
644+
*/
645+
@Override
646+
public Flux<V> union(K key, Collection<K> otherKeys) {
647+
648+
Assert.notNull(key, "Key must not be null!");
649+
Assert.notNull(otherKeys, "Other keys must not be null!");
650+
651+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
652+
.map(this::rawKey) //
653+
.collectList() //
654+
.flatMapMany(connection::zUnion).map(this::readValue));
655+
}
656+
657+
/*
658+
* (non-Javadoc)
659+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionWithScores(K, Collection)
660+
*/
661+
@Override
662+
public Flux<TypedTuple<V>> unionWithScores(K key, Collection<K> otherKeys) {
663+
664+
Assert.notNull(key, "Key must not be null!");
665+
Assert.notNull(otherKeys, "Other keys must not be null!");
666+
667+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
668+
.map(this::rawKey) //
669+
.collectList() //
670+
.flatMapMany(connection::zUnionWithScores).map(this::readTypedTuple));
671+
}
672+
673+
/*
674+
* (non-Javadoc)
675+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionWithScores(K, Collection, Aggregate, Weights)
676+
*/
677+
@Override
678+
public Flux<TypedTuple<V>> unionWithScores(K key, Collection<K> otherKeys, Aggregate aggregate, Weights weights) {
679+
680+
Assert.notNull(key, "Key must not be null!");
681+
Assert.notNull(otherKeys, "Other keys must not be null!");
682+
Assert.notNull(aggregate, "Aggregate must not be null!");
683+
Assert.notNull(weights, "Weights must not be null!");
684+
685+
return createFlux(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
686+
.map(this::rawKey) //
687+
.collectList() //
688+
.flatMapMany(sets -> connection.zUnionWithScores(sets, weights, aggregate)).map(this::readTypedTuple));
552689
}
553690

554691
/*
555692
* (non-Javadoc)
556-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
693+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.lang.Object, java.lang.Object)
557694
*/
558695
@Override
559-
public Mono<Long> intersectAndStore(K key, K otherKey, K destKey) {
696+
public Mono<Long> unionAndStore(K key, K otherKey, K destKey) {
560697

561698
Assert.notNull(key, "Key must not be null!");
562699
Assert.notNull(otherKey, "Other key must not be null!");
563700
Assert.notNull(destKey, "Destination key must not be null!");
564701

565-
return intersectAndStore(key, Collections.singleton(otherKey), destKey);
702+
return unionAndStore(key, Collections.singleton(otherKey), destKey);
566703
}
567704

568705
/*
569706
* (non-Javadoc)
570-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
707+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object)
571708
*/
572709
@Override
573-
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
710+
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey) {
574711

575712
Assert.notNull(key, "Key must not be null!");
576713
Assert.notNull(otherKeys, "Other keys must not be null!");
@@ -579,15 +716,15 @@ public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey) {
579716
return createMono(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
580717
.map(this::rawKey) //
581718
.collectList() //
582-
.flatMap(serialized -> connection.zInterStore(rawKey(destKey), serialized)));
719+
.flatMap(serialized -> connection.zUnionStore(rawKey(destKey), serialized)));
583720
}
584721

585722
/*
586723
* (non-Javadoc)
587-
* @see org.springframework.data.redis.core.ReactiveZSetOperations#intersectAndStore(java.lang.Object, java.util.Collection, java.lang.Object, org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights)
724+
* @see org.springframework.data.redis.core.ReactiveZSetOperations#unionAndStore(java.lang.Object, java.util.Collection, java.lang.Object, org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights)
588725
*/
589726
@Override
590-
public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
727+
public Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights) {
591728

592729
Assert.notNull(key, "Key must not be null!");
593730
Assert.notNull(otherKeys, "Other keys must not be null!");
@@ -598,7 +735,7 @@ public Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey, A
598735
return createMono(connection -> Flux.fromIterable(getKeys(key, otherKeys)) //
599736
.map(this::rawKey) //
600737
.collectList() //
601-
.flatMap(serialized -> connection.zInterStore(rawKey(destKey), serialized, weights, aggregate)));
738+
.flatMap(serialized -> connection.zUnionStore(rawKey(destKey), serialized, weights, aggregate)));
602739
}
603740

604741
/*

‎src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java

Lines changed: 245 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.time.Duration;
2222
import java.util.Collection;
23+
import java.util.Collections;
2324
import java.util.List;
2425

2526
import org.springframework.data.domain.Range;
@@ -413,55 +414,153 @@ default Flux<TypedTuple<V>> scan(K key) {
413414
Mono<Long> removeRangeByScore(K key, Range<Double> range);
414415

415416
/**
416-
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
417+
* Diff sorted {@code sets}.
417418
*
418419
* @param key must not be {@literal null}.
419420
* @param otherKey must not be {@literal null}.
420-
* @param destKey must not be {@literal null}.
421421
* @return
422-
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
422+
* @since 2.6
423+
* @see <a href="https://redis.io/commands/zdiff">Redis Documentation: ZDIFF</a>
423424
*/
424-
Mono<Long> unionAndStore(K key, K otherKey, K destKey);
425+
default Flux<V> difference(K key, K otherKey) {
426+
return difference(key, Collections.singleton(otherKey));
427+
}
425428

426429
/**
427-
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
430+
* Diff sorted {@code sets}.
431+
*
432+
* @param key must not be {@literal null}.
433+
* @param otherKeys must not be {@literal null}.
434+
* @return
435+
* @since 2.6
436+
* @see <a href="https://redis.io/commands/zdiff">Redis Documentation: ZDIFF</a>
437+
*/
438+
Flux<V> difference(K key, Collection<K> otherKeys);
439+
440+
/**
441+
* Diff sorted {@code sets}.
442+
*
443+
* @param key must not be {@literal null}.
444+
* @param otherKey must not be {@literal null}.
445+
* @return
446+
* @since 2.6
447+
* @see <a href="https://redis.io/commands/zdiff">Redis Documentation: ZDIFF</a>
448+
*/
449+
default Flux<TypedTuple<V>> differenceWithScores(K key, K otherKey) {
450+
return differenceWithScores(key, Collections.singleton(otherKey));
451+
}
452+
453+
/**
454+
* Diff sorted {@code sets}.
455+
*
456+
* @param key must not be {@literal null}.
457+
* @param otherKeys must not be {@literal null}.
458+
* @return
459+
* @since 2.6
460+
* @see <a href="https://redis.io/commands/zdiff">Redis Documentation: ZDIFF</a>
461+
*/
462+
Flux<TypedTuple<V>> differenceWithScores(K key, Collection<K> otherKeys);
463+
464+
/**
465+
* Diff sorted {@code sets} and store result in destination {@code destKey}.
428466
*
429467
* @param key must not be {@literal null}.
430468
* @param otherKeys must not be {@literal null}.
431469
* @param destKey must not be {@literal null}.
432470
* @return
433-
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
471+
* @since 2.6
472+
* @see <a href="https://redis.io/commands/zdiffstore">Redis Documentation: ZDIFFSTORE</a>
434473
*/
435-
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey);
474+
default Mono<Long> differenceAndStore(K key, K otherKey, K destKey) {
475+
return differenceAndStore(key, Collections.singleton(otherKey), destKey);
476+
}
436477

437478
/**
438-
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
479+
* Diff sorted {@code sets} and store result in destination {@code destKey}.
439480
*
440481
* @param key must not be {@literal null}.
441482
* @param otherKeys must not be {@literal null}.
442483
* @param destKey must not be {@literal null}.
484+
* @return
485+
* @since 2.6
486+
* @see <a href="https://redis.io/commands/zdiffstore">Redis Documentation: ZDIFFSTORE</a>
487+
*/
488+
Mono<Long> differenceAndStore(K key, Collection<K> otherKeys, K destKey);
489+
490+
/**
491+
* Intersect sorted {@code sets}.
492+
*
493+
* @param key must not be {@literal null}.
494+
* @param otherKey must not be {@literal null}.
495+
* @return
496+
* @since 2.6
497+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
498+
*/
499+
default Flux<V> intersect(K key, K otherKey) {
500+
return intersect(key, Collections.singleton(otherKey));
501+
}
502+
503+
/**
504+
* Intersect sorted {@code sets}.
505+
*
506+
* @param key must not be {@literal null}.
507+
* @param otherKeys must not be {@literal null}.
508+
* @return
509+
* @since 2.6
510+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
511+
*/
512+
Flux<V> intersect(K key, Collection<K> otherKeys);
513+
514+
/**
515+
* Intersect sorted {@code sets}.
516+
*
517+
* @param key must not be {@literal null}.
518+
* @param otherKey must not be {@literal null}.
519+
* @return
520+
* @since 2.6
521+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
522+
*/
523+
default Flux<TypedTuple<V>> intersectWithScores(K key, K otherKey) {
524+
return intersectWithScores(key, Collections.singleton(otherKey));
525+
}
526+
527+
/**
528+
* Intersect sorted {@code sets}.
529+
*
530+
* @param key must not be {@literal null}.
531+
* @param otherKeys must not be {@literal null}.
532+
* @return
533+
* @since 2.6
534+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
535+
*/
536+
Flux<TypedTuple<V>> intersectWithScores(K key, Collection<K> otherKeys);
537+
538+
/**
539+
* Intersect sorted sets at {@code key} and {@code otherKeys} .
540+
*
541+
* @param key must not be {@literal null}.
542+
* @param otherKeys must not be {@literal null}.
443543
* @param aggregate must not be {@literal null}.
444544
* @return
445-
* @since 2.1
446-
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
545+
* @since 2.6
546+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
447547
*/
448-
default Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate) {
449-
return unionAndStore(key, otherKeys, destKey, aggregate, Weights.fromSetCount(1 + otherKeys.size()));
548+
default Flux<TypedTuple<V>> intersectWithScores(K key, Collection<K> otherKeys, Aggregate aggregate) {
549+
return intersectWithScores(key, otherKeys, aggregate, Weights.fromSetCount(1 + otherKeys.size()));
450550
}
451551

452552
/**
453-
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
553+
* Intersect sorted {@code sets}.
454554
*
455555
* @param key must not be {@literal null}.
456556
* @param otherKeys must not be {@literal null}.
457-
* @param destKey must not be {@literal null}.
458557
* @param aggregate must not be {@literal null}.
459558
* @param weights must not be {@literal null}.
460559
* @return
461-
* @since 2.1
462-
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
560+
* @since 2.6
561+
* @see <a href="https://redis.io/commands/zinter">Redis Documentation: ZINTER</a>
463562
*/
464-
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights);
563+
Flux<TypedTuple<V>> intersectWithScores(K key, Collection<K> otherKeys, Aggregate aggregate, Weights weights);
465564

466565
/**
467566
* Intersect sorted sets at {@code key} and {@code otherKey} and store result in destination {@code destKey}.
@@ -472,7 +571,9 @@ default Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggr
472571
* @return
473572
* @see <a href="https://redis.io/commands/zinterstore">Redis Documentation: ZINTERSTORE</a>
474573
*/
475-
Mono<Long> intersectAndStore(K key, K otherKey, K destKey);
574+
default Mono<Long> intersectAndStore(K key, K otherKey, K destKey) {
575+
return intersectAndStore(key, Collections.singleton(otherKey), destKey);
576+
}
476577

477578
/**
478579
* Intersect sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
@@ -514,6 +615,132 @@ default Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey,
514615
*/
515616
Mono<Long> intersectAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights);
516617

618+
/**
619+
* Union sorted {@code sets}.
620+
*
621+
* @param key must not be {@literal null}.
622+
* @param otherKey must not be {@literal null}.
623+
* @return
624+
* @since 2.6
625+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
626+
*/
627+
default Flux<V> union(K key, K otherKey) {
628+
return union(key, Collections.singleton(otherKey));
629+
}
630+
631+
/**
632+
* Union sorted {@code sets}.
633+
*
634+
* @param key must not be {@literal null}.
635+
* @param otherKeys must not be {@literal null}.
636+
* @return
637+
* @since 2.6
638+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
639+
*/
640+
Flux<V> union(K key, Collection<K> otherKeys);
641+
642+
/**
643+
* Union sorted {@code sets}.
644+
*
645+
* @param key must not be {@literal null}.
646+
* @param otherKey must not be {@literal null}.
647+
* @return
648+
* @since 2.6
649+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
650+
*/
651+
default Flux<TypedTuple<V>> unionWithScores(K key, K otherKey) {
652+
return unionWithScores(key, Collections.singleton(otherKey));
653+
}
654+
655+
/**
656+
* Union sorted {@code sets}.
657+
*
658+
* @param key must not be {@literal null}.
659+
* @param otherKeys must not be {@literal null}.
660+
* @return
661+
* @since 2.6
662+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
663+
*/
664+
Flux<TypedTuple<V>> unionWithScores(K key, Collection<K> otherKeys);
665+
666+
/**
667+
* Union sorted sets at {@code key} and {@code otherKeys} .
668+
*
669+
* @param key must not be {@literal null}.
670+
* @param otherKeys must not be {@literal null}.
671+
* @param aggregate must not be {@literal null}.
672+
* @return
673+
* @since 2.6
674+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
675+
*/
676+
default Flux<TypedTuple<V>> unionWithScores(K key, Collection<K> otherKeys, Aggregate aggregate) {
677+
return unionWithScores(key, otherKeys, aggregate, Weights.fromSetCount(1 + otherKeys.size()));
678+
}
679+
680+
/**
681+
* Union sorted {@code sets}.
682+
*
683+
* @param key must not be {@literal null}.
684+
* @param otherKeys must not be {@literal null}.
685+
* @param aggregate must not be {@literal null}.
686+
* @param weights must not be {@literal null}.
687+
* @return
688+
* @since 2.6
689+
* @see <a href="https://redis.io/commands/zunion">Redis Documentation: ZUNION</a>
690+
*/
691+
Flux<TypedTuple<V>> unionWithScores(K key, Collection<K> otherKeys, Aggregate aggregate, Weights weights);
692+
693+
/**
694+
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
695+
*
696+
* @param key must not be {@literal null}.
697+
* @param otherKey must not be {@literal null}.
698+
* @param destKey must not be {@literal null}.
699+
* @return
700+
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
701+
*/
702+
Mono<Long> unionAndStore(K key, K otherKey, K destKey);
703+
704+
/**
705+
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
706+
*
707+
* @param key must not be {@literal null}.
708+
* @param otherKeys must not be {@literal null}.
709+
* @param destKey must not be {@literal null}.
710+
* @return
711+
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
712+
*/
713+
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey);
714+
715+
/**
716+
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
717+
*
718+
* @param key must not be {@literal null}.
719+
* @param otherKeys must not be {@literal null}.
720+
* @param destKey must not be {@literal null}.
721+
* @param aggregate must not be {@literal null}.
722+
* @return
723+
* @since 2.1
724+
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
725+
*/
726+
default Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate) {
727+
return unionAndStore(key, otherKeys, destKey, aggregate, Weights.fromSetCount(1 + otherKeys.size()));
728+
}
729+
730+
/**
731+
* Union sorted sets at {@code key} and {@code otherKeys} and store result in destination {@code destKey}.
732+
*
733+
* @param key must not be {@literal null}.
734+
* @param otherKeys must not be {@literal null}.
735+
* @param destKey must not be {@literal null}.
736+
* @param aggregate must not be {@literal null}.
737+
* @param weights must not be {@literal null}.
738+
* @return
739+
* @since 2.1
740+
* @see <a href="https://redis.io/commands/zunionstore">Redis Documentation: ZUNIONSTORE</a>
741+
*/
742+
Mono<Long> unionAndStore(K key, Collection<K> otherKeys, K destKey, Aggregate aggregate, Weights weights);
743+
517744
/**
518745
* Get all elements with lexicographical ordering from {@literal ZSET} at {@code key} with a value between
519746
* {@link Range#getLowerBound()} and {@link Range#getUpperBound()}.

‎src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,50 @@ void zRemRangeByScoreShouldRemoveValuesCorrectlyWithExcludingMaxRange() {
555555
.isEqualTo(1L);
556556
}
557557

558-
@ParameterizedRedisTest // DATAREDIS-525
559-
void zUnionStoreShouldWorkCorrectly() {
558+
@ParameterizedRedisTest // GH-2041
559+
void zDiffShouldWorkCorrectly() {
560+
561+
assumeThat(connectionProvider).isInstanceOf(StandaloneConnectionProvider.class);
562+
563+
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
564+
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
565+
nativeCommands.zadd(KEY_1, 3D, VALUE_3);
566+
nativeCommands.zadd(KEY_2, 1D, VALUE_1);
567+
nativeCommands.zadd(KEY_2, 2D, VALUE_2);
568+
569+
connection.zSetCommands().zDiff(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER)) //
570+
.collectList() //
571+
.as(StepVerifier::create) //
572+
.assertNext(actual -> {
573+
assertThat(actual).containsOnly(VALUE_3_BBUFFER);
574+
}).verifyComplete();
575+
576+
connection.zSetCommands().zDiffWithScores(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER)) //
577+
.collectList() //
578+
.as(StepVerifier::create) //
579+
.assertNext(actual -> {
580+
assertThat(actual).containsOnly(new DefaultTuple(VALUE_3_BYTES, 3D));
581+
}).verifyComplete();
582+
}
583+
584+
@ParameterizedRedisTest // GH-2041
585+
void zDiffStoreShouldWorkCorrectly() {
586+
587+
assumeThat(connectionProvider).isInstanceOf(StandaloneConnectionProvider.class);
588+
589+
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
590+
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
591+
nativeCommands.zadd(KEY_1, 3D, VALUE_3);
592+
nativeCommands.zadd(KEY_2, 1D, VALUE_1);
593+
nativeCommands.zadd(KEY_2, 2D, VALUE_2);
594+
595+
connection.zSetCommands().zDiffStore(KEY_3_BBUFFER, Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER)) //
596+
.as(StepVerifier::create) //
597+
.expectNext(1L).verifyComplete();
598+
}
599+
600+
@ParameterizedRedisTest // GH-2042
601+
void zInterShouldWorkCorrectly() {
560602

561603
assumeThat(connectionProvider).isInstanceOf(StandaloneConnectionProvider.class);
562604

@@ -566,9 +608,19 @@ void zUnionStoreShouldWorkCorrectly() {
566608
nativeCommands.zadd(KEY_2, 2D, VALUE_2);
567609
nativeCommands.zadd(KEY_2, 3D, VALUE_3);
568610

569-
assertThat(connection.zSetCommands()
570-
.zUnionStore(KEY_3_BBUFFER, Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), Arrays.asList(2D, 3D)).block())
571-
.isEqualTo(3L);
611+
connection.zSetCommands().zInter(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER)) //
612+
.collectList() //
613+
.as(StepVerifier::create) //
614+
.assertNext(actual -> {
615+
assertThat(actual).contains(VALUE_1_BBUFFER, VALUE_2_BBUFFER);
616+
}).verifyComplete();
617+
618+
connection.zSetCommands().zInterWithScores(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), Arrays.asList(2D, 3D)) //
619+
.collectList() //
620+
.as(StepVerifier::create) //
621+
.assertNext(actual -> {
622+
assertThat(actual).contains(new DefaultTuple(VALUE_1_BYTES, 5D), new DefaultTuple(VALUE_2_BYTES, 10D));
623+
}).verifyComplete();
572624
}
573625

574626
@ParameterizedRedisTest // DATAREDIS-525
@@ -587,6 +639,49 @@ void zInterStoreShouldWorkCorrectly() {
587639
.isEqualTo(2L);
588640
}
589641

642+
@ParameterizedRedisTest // GH-2042
643+
void zUnionShouldWorkCorrectly() {
644+
645+
assumeThat(connectionProvider).isInstanceOf(StandaloneConnectionProvider.class);
646+
647+
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
648+
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
649+
nativeCommands.zadd(KEY_2, 1D, VALUE_1);
650+
nativeCommands.zadd(KEY_2, 2D, VALUE_2);
651+
nativeCommands.zadd(KEY_2, 3D, VALUE_3);
652+
653+
connection.zSetCommands().zUnion(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER)) //
654+
.collectList() //
655+
.as(StepVerifier::create) //
656+
.assertNext(actual -> {
657+
assertThat(actual).contains(VALUE_1_BBUFFER, VALUE_2_BBUFFER, VALUE_3_BBUFFER);
658+
}).verifyComplete();
659+
660+
connection.zSetCommands().zUnionWithScores(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), Arrays.asList(2D, 3D)) //
661+
.collectList() //
662+
.as(StepVerifier::create) //
663+
.assertNext(actual -> {
664+
assertThat(actual).contains(new DefaultTuple(VALUE_1_BYTES, 5D), new DefaultTuple(VALUE_2_BYTES, 10D),
665+
new DefaultTuple(VALUE_3_BYTES, 9D));
666+
}).verifyComplete();
667+
}
668+
669+
@ParameterizedRedisTest // DATAREDIS-525
670+
void zUnionStoreShouldWorkCorrectly() {
671+
672+
assumeThat(connectionProvider).isInstanceOf(StandaloneConnectionProvider.class);
673+
674+
nativeCommands.zadd(KEY_1, 1D, VALUE_1);
675+
nativeCommands.zadd(KEY_1, 2D, VALUE_2);
676+
nativeCommands.zadd(KEY_2, 1D, VALUE_1);
677+
nativeCommands.zadd(KEY_2, 2D, VALUE_2);
678+
nativeCommands.zadd(KEY_2, 3D, VALUE_3);
679+
680+
assertThat(connection.zSetCommands()
681+
.zUnionStore(KEY_3_BBUFFER, Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), Arrays.asList(2D, 3D)).block())
682+
.isEqualTo(3L);
683+
}
684+
590685
@ParameterizedRedisTest // DATAREDIS-525
591686
void zRangeByLex() {
592687

‎src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java

Lines changed: 120 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -559,12 +559,11 @@ void removeRangeByScore() {
559559
.verifyComplete();
560560
}
561561

562-
@ParameterizedRedisTest // DATAREDIS-602
563-
void unionAndStore() {
562+
@ParameterizedRedisTest // GH-2041
563+
void difference() {
564564

565565
K key = keyFactory.instance();
566566
K otherKey = keyFactory.instance();
567-
K destKey = keyFactory.instance();
568567

569568
V onlyInKey = valueFactory.instance();
570569
V shared = valueFactory.instance();
@@ -576,12 +575,14 @@ void unionAndStore() {
576575
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
577576
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
578577

579-
zSetOperations.unionAndStore(key, otherKey, destKey).as(StepVerifier::create).expectNext(3L).verifyComplete();
580-
zSetOperations.range(destKey, Range.closed(0L, 100L)).as(StepVerifier::create).expectNextCount(3).verifyComplete();
578+
zSetOperations.difference(key, otherKey).as(StepVerifier::create).expectNext(onlyInKey).verifyComplete();
579+
580+
zSetOperations.differenceWithScores(key, otherKey).as(StepVerifier::create)
581+
.expectNext(new DefaultTypedTuple<>(onlyInKey, 10D)).verifyComplete();
581582
}
582583

583-
@ParameterizedRedisTest // DATAREDIS-746
584-
void unionAndStoreWithAggregation() {
584+
@ParameterizedRedisTest // GH-2041
585+
void differenceAndStore() {
585586

586587
K key = keyFactory.instance();
587588
K otherKey = keyFactory.instance();
@@ -597,14 +598,37 @@ void unionAndStoreWithAggregation() {
597598
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
598599
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
599600

600-
zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM).as(StepVerifier::create)
601-
.expectNext(3L).verifyComplete();
602-
zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(22d).verifyComplete();
601+
zSetOperations.differenceAndStore(key, otherKey, destKey).as(StepVerifier::create).expectNext(1L).verifyComplete();
603602

604-
zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM, Weights.of(2, 1))
605-
.as(StepVerifier::create)
606-
.expectNext(3L).verifyComplete();
607-
zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(33d).verifyComplete();
603+
zSetOperations.range(destKey, ZERO_TO_FIVE).as(StepVerifier::create) //
604+
.expectNextCount(1) //
605+
.verifyComplete();
606+
}
607+
608+
@ParameterizedRedisTest // GH-2042
609+
@EnabledOnCommand("ZINTER")
610+
void intersect() {
611+
612+
K key = keyFactory.instance();
613+
K otherKey = keyFactory.instance();
614+
615+
V onlyInKey = valueFactory.instance();
616+
V shared = valueFactory.instance();
617+
V onlyInOtherKey = valueFactory.instance();
618+
619+
zSetOperations.add(key, onlyInKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
620+
zSetOperations.add(key, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
621+
622+
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
623+
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
624+
625+
zSetOperations.intersect(key, otherKey).as(StepVerifier::create).expectNext(shared).verifyComplete();
626+
627+
zSetOperations.intersectWithScores(key, otherKey).as(StepVerifier::create)
628+
.expectNext(new DefaultTypedTuple<>(shared, 22D)).verifyComplete();
629+
630+
zSetOperations.intersectWithScores(key, Collections.singleton(otherKey), Aggregate.SUM, Weights.of(1, 2))
631+
.as(StepVerifier::create).expectNext(new DefaultTypedTuple<>(shared, 33D)).verifyComplete();
608632
}
609633

610634
@ParameterizedRedisTest // DATAREDIS-602
@@ -650,8 +674,7 @@ void intersectAndStoreWithAggregation() {
650674
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
651675

652676
zSetOperations.intersectAndStore(key, Collections.singletonList(otherKey), destKey, Aggregate.SUM)
653-
.as(StepVerifier::create)
654-
.expectNext(1L).expectComplete().verify();
677+
.as(StepVerifier::create).expectNext(1L).expectComplete().verify();
655678

656679
zSetOperations.score(destKey, shared).as(StepVerifier::create) //
657680
.expectNext(22d) //
@@ -665,6 +688,87 @@ void intersectAndStoreWithAggregation() {
665688
.verifyComplete();
666689
}
667690

691+
@ParameterizedRedisTest // GH-2042
692+
@EnabledOnCommand("ZUNION")
693+
void union() {
694+
695+
K key = keyFactory.instance();
696+
K otherKey = keyFactory.instance();
697+
698+
V onlyInKey = valueFactory.instance();
699+
V shared = valueFactory.instance();
700+
V onlyInOtherKey = valueFactory.instance();
701+
702+
zSetOperations.add(key, onlyInKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
703+
zSetOperations.add(key, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
704+
705+
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
706+
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
707+
708+
zSetOperations.union(key, otherKey).as(StepVerifier::create).expectNextCount(3).verifyComplete();
709+
710+
zSetOperations.unionWithScores(key, otherKey).collectList().as(StepVerifier::create).assertNext(actual -> {
711+
assertThat(actual).containsOnly(new DefaultTypedTuple<>(onlyInKey, 10D), new DefaultTypedTuple<>(shared, 22D),
712+
new DefaultTypedTuple<>(onlyInOtherKey, 10D));
713+
714+
}).verifyComplete();
715+
716+
zSetOperations.unionWithScores(key, Collections.singleton(otherKey), Aggregate.SUM, Weights.of(1, 2)).collectList()
717+
.as(StepVerifier::create).assertNext(actual -> {
718+
assertThat(actual).containsOnly(new DefaultTypedTuple<>(onlyInKey, 10D), new DefaultTypedTuple<>(shared, 33D),
719+
new DefaultTypedTuple<>(onlyInOtherKey, 20D));
720+
721+
}).verifyComplete();
722+
}
723+
724+
@ParameterizedRedisTest // DATAREDIS-602
725+
void unionAndStore() {
726+
727+
K key = keyFactory.instance();
728+
K otherKey = keyFactory.instance();
729+
K destKey = keyFactory.instance();
730+
731+
V onlyInKey = valueFactory.instance();
732+
V shared = valueFactory.instance();
733+
V onlyInOtherKey = valueFactory.instance();
734+
735+
zSetOperations.add(key, onlyInKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
736+
zSetOperations.add(key, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
737+
738+
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
739+
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
740+
741+
zSetOperations.unionAndStore(key, otherKey, destKey).as(StepVerifier::create).expectNext(3L).verifyComplete();
742+
zSetOperations.range(destKey, Range.closed(0L, 100L)).as(StepVerifier::create).expectNextCount(3).verifyComplete();
743+
}
744+
745+
@ParameterizedRedisTest // DATAREDIS-746
746+
void unionAndStoreWithAggregation() {
747+
748+
K key = keyFactory.instance();
749+
K otherKey = keyFactory.instance();
750+
K destKey = keyFactory.instance();
751+
752+
V onlyInKey = valueFactory.instance();
753+
V shared = valueFactory.instance();
754+
V onlyInOtherKey = valueFactory.instance();
755+
756+
zSetOperations.add(key, onlyInKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
757+
zSetOperations.add(key, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
758+
759+
zSetOperations.add(otherKey, onlyInOtherKey, 10).as(StepVerifier::create).expectNext(true).verifyComplete();
760+
zSetOperations.add(otherKey, shared, 11).as(StepVerifier::create).expectNext(true).verifyComplete();
761+
762+
zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM).as(StepVerifier::create)
763+
.expectNext(3L).verifyComplete();
764+
zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(22d).verifyComplete();
765+
766+
zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM, Weights.of(2, 1))
767+
.as(StepVerifier::create)
768+
.expectNext(3L).verifyComplete();
769+
zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(33d).verifyComplete();
770+
}
771+
668772
@ParameterizedRedisTest // DATAREDIS-602
669773
void rangeByLex() {
670774

0 commit comments

Comments
 (0)
Please sign in to comment.