diff --git a/src/main/asciidoc/appendix/appendix-command-reference.adoc b/src/main/asciidoc/appendix/appendix-command-reference.adoc index 256a080ef..fca3e5e82 100644 --- a/src/main/asciidoc/appendix/appendix-command-reference.adoc +++ b/src/main/asciidoc/appendix/appendix-command-reference.adoc @@ -181,6 +181,7 @@ |ZRANGEBYLEX |- |ZREVRANGEBYLEX |- |ZRANGEBYSCORE |X +|ZRANGESTORE |X |ZRANK |X |ZREM |X |ZREMRANGEBYLEX |- diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index bcd7d19e6..1c9354153 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -2711,6 +2711,12 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return convertAndReturn(delegate.zRevRangeByLex(key, range, limit), Converters.identityConverter()); } + @Override + public Set zRevRangeByLex(String key, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return convertAndReturn(delegate.zRevRangeByLex(serialize(key), serialize(range), limit), byteSetToStringSet); + } + @Override public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, @@ -2721,16 +2727,21 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco @Override public Long zRangeStoreByLex(String dstKey, String srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + org.springframework.data.domain.Range range, org.springframework.data.redis.connection.Limit limit) { return convertAndReturn(delegate.zRangeStoreByLex(serialize(dstKey), serialize(srcKey), serialize(range), limit), Converters.identityConverter()); } @Override - public Long zRangeStoreByScore(String dstKey, String srcKey, double min, double max, - org.springframework.data.redis.connection.Limit limit) { - return convertAndReturn(delegate.zRangeStoreByScore(serialize(dstKey), serialize(srcKey), min, max, limit), + public Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return convertAndReturn(delegate.zRangeStoreRevByLex(dstKey, srcKey, range, limit), Converters.identityConverter()); + } + + @Override + public Long zRangeStoreRevByLex(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return convertAndReturn(delegate.zRangeStoreRevByLex(serialize(dstKey), serialize(srcKey), serialize(range), limit), Converters.identityConverter()); } @@ -2743,9 +2754,24 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco } @Override - public Set zRevRangeByLex(String key, org.springframework.data.domain.Range range, + public Long zRangeStoreByScore(String dstKey, String srcKey, org.springframework.data.domain.Range range, org.springframework.data.redis.connection.Limit limit) { - return convertAndReturn(delegate.zRevRangeByLex(serialize(key), serialize(range), limit), byteSetToStringSet); + return convertAndReturn(delegate.zRangeStoreByScore(serialize(dstKey), serialize(srcKey), range, limit), + Converters.identityConverter()); + } + + @Override + public Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return convertAndReturn(delegate.zRangeStoreRevByScore(dstKey, srcKey, range, limit), + Converters.identityConverter()); + } + + @Override + public Long zRangeStoreRevByScore(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return convertAndReturn(delegate.zRangeStoreRevByScore(serialize(dstKey), serialize(srcKey), range, limit), + Converters.identityConverter()); } @Override diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index f70a78560..6c16b0fc2 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -1846,6 +1846,14 @@ public interface DefaultedRedisConnection extends RedisCommands, RedisCommandsPr return zSetCommands().zRangeStoreByLex(dstKey, srcKey, range, limit); } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ + @Override + @Deprecated + default Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zSetCommands().zRangeStoreRevByLex(dstKey, srcKey, range, limit); + } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ @Override @Deprecated @@ -1855,4 +1863,12 @@ public interface DefaultedRedisConnection extends RedisCommands, RedisCommandsPr return zSetCommands().zRangeStoreByScore(dstKey, srcKey, range, limit); } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ + @Override + @Deprecated + default Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zSetCommands().zRangeStoreRevByScore(dstKey, srcKey, range, limit); + } + } diff --git a/src/main/java/org/springframework/data/redis/connection/Limit.java b/src/main/java/org/springframework/data/redis/connection/Limit.java index 9918cb306..39d3d7bf9 100644 --- a/src/main/java/org/springframework/data/redis/connection/Limit.java +++ b/src/main/java/org/springframework/data/redis/connection/Limit.java @@ -52,6 +52,10 @@ public class Limit { return this.equals(UNLIMITED); } + public boolean isLimited() { + return !isUnlimited(); + } + /** * @return new {@link Limit} indicating no limit; * @since 1.3 diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java index 8b2f00445..010191368 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java @@ -902,6 +902,250 @@ public interface ReactiveZSetCommands { */ Flux>> zRange(Publisher commands); + /** + * {@code ZRANGESTORE} command parameters. + * + * @author Mark Paluch + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + class ZRangeStoreCommand extends KeyCommand { + + private final ByteBuffer destKey; + private final RangeMode rangeMode; + private final Range range; + private final Direction direction; + private final Limit limit; + + private ZRangeStoreCommand(@Nullable ByteBuffer srcKey, @Nullable ByteBuffer destKey, RangeMode rangeMode, + Range range, Direction direction, Limit limit) { + + super(srcKey); + this.destKey = destKey; + this.rangeMode = rangeMode; + this.range = range; + this.direction = direction; + this.limit = limit; + } + + /** + * Creates a new {@link ZRangeStoreCommand} given a {@link Range} to obtain elements ordered from the lowest to the + * highest score. + * + * @param range must not be {@literal null}. + * @return a new {@link ZRangeCommand} for {@link Tuple}. + */ + public static ZRangeStoreCommand scoreWithin(Range range) { + + Assert.notNull(range, "Range must not be null"); + + return new ZRangeStoreCommand(null, null, RangeMode.ByScore, range, Direction.ASC, Limit.unlimited()); + } + + /** + * Creates a new {@link ZRangeStoreCommand} given a {@link Range} to obtain elements ordered from the highest to the + * lowest score. + * + * @param range must not be {@literal null}. + * @return a new {@link ZRangeStoreCommand} for {@link Tuple}. + */ + public static ZRangeStoreCommand reverseScoreWithin(Range range) { + + Assert.notNull(range, "Range must not be null"); + + return new ZRangeStoreCommand(null, null, RangeMode.ByScore, range, Direction.DESC, Limit.unlimited()); + } + + /** + * Creates a new {@link ZRangeStoreCommand} given a {@link Range} to obtain elements ordered from the lowest to the + * highest lexicographical value. + * + * @param range must not be {@literal null}. + * @return a new {@link ZRangeCommand} for {@link Tuple}. + */ + public static ZRangeStoreCommand valueWithin(Range range) { + + Assert.notNull(range, "Range must not be null"); + + return new ZRangeStoreCommand(null, null, RangeMode.ByLex, range, Direction.ASC, Limit.unlimited()); + } + + /** + * Creates a new {@link ZRangeStoreCommand} given a {@link Range} to obtain elements ordered from the highest to the + * lowest lexicographical value. + * + * @param range must not be {@literal null}. + * @return a new {@link ZRangeStoreCommand} for {@link Tuple}. + */ + public static ZRangeStoreCommand reverseValueWithin(Range range) { + + Assert.notNull(range, "Range must not be null"); + + return new ZRangeStoreCommand(null, null, RangeMode.ByLex, range, Direction.DESC, Limit.unlimited()); + } + + /** + * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. + * + * @param key must not be {@literal null}. + * @return a new {@link ZRangeStoreCommand} with {@literal key} applied. + */ + public ZRangeStoreCommand from(ByteBuffer key) { + + Assert.notNull(key, "Key must not be null"); + + return new ZRangeStoreCommand(key, destKey, rangeMode, range, direction, limit); + } + + /** + * Applies the {@literal key}. Constructs a new command instance with all previously configured properties. + * + * @param key must not be {@literal null}. + * @return a new {@link ZRangeStoreCommand} with {@literal key} applied. + */ + public ZRangeStoreCommand to(ByteBuffer key) { + + Assert.notNull(key, "Key must not be null"); + + return new ZRangeStoreCommand(getKey(), key, rangeMode, range, direction, limit); + } + + /** + * Applies the {@literal limit}. Constructs a new command instance with all previously configured properties. + * + * @param limit must not be {@literal null}. + * @return a new {@link ZRangeStoreCommand} with {@literal key} applied. + */ + public ZRangeStoreCommand limit(Limit limit) { + + Assert.notNull(limit, "Limit must not be null"); + + return new ZRangeStoreCommand(getKey(), getDestKey(), rangeMode, range, direction, limit); + } + + public ByteBuffer getDestKey() { + return destKey; + } + + public RangeMode getRangeMode() { + return rangeMode; + } + + public Range getRange() { + return range; + } + + public Direction getDirection() { + return direction; + } + + public Limit getLimit() { + return limit; + } + + public enum RangeMode { + ByScore, ByLex, + } + } + + /** + * Add elements from {@code srcKey} by score {@literal range} to {@code destKey}. + * + * @param srcKey must not be {@literal null}. + * @param destKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono zRangeStoreByScore(ByteBuffer srcKey, ByteBuffer destKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(destKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return zRangeStore(Mono.just(ZRangeStoreCommand.scoreWithin(range).from(srcKey).to(destKey).limit(limit))) // + .flatMap(CommandResponse::getOutput).next(); + } + + /** + * Add elements from {@code srcKey} by lexicographical {@literal range} to {@code destKey}. + * + * @param srcKey must not be {@literal null}. + * @param destKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono zRangeStoreByLex(ByteBuffer srcKey, ByteBuffer destKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(destKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return zRangeStore(Mono.just(ZRangeStoreCommand.valueWithin(range).from(srcKey).to(destKey).limit(limit))) // + .flatMap(CommandResponse::getOutput).next(); + } + + /** + * Add elements from {@code srcKey} by reverse score {@literal range} to {@code destKey}. + * + * @param srcKey must not be {@literal null}. + * @param destKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono zRangeStoreRevByScore(ByteBuffer srcKey, ByteBuffer destKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(destKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return zRangeStore(Mono.just(ZRangeStoreCommand.reverseScoreWithin(range).from(srcKey).to(destKey).limit(limit))) // + .flatMap(CommandResponse::getOutput).next(); + } + + /** + * Add elements from {@code srcKey} by reverse lexicographical {@literal range} to {@code destKey}. + * + * @param srcKey must not be {@literal null}. + * @param destKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono zRangeStoreRevByLex(ByteBuffer srcKey, ByteBuffer destKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(destKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return zRangeStore(Mono.just(ZRangeStoreCommand.reverseValueWithin(range).from(srcKey).to(destKey).limit(limit))) // + .flatMap(CommandResponse::getOutput).next(); + } + + /** + * Get set of {@link Tuple}s in {@literal range} from sorted set. + * + * @param commands must not be {@literal null}. + * @return + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + Flux>> zRangeStore(Publisher commands); + /** * {@literal ZRANGEBYSCORE}/{@literal ZREVRANGEBYSCORE}. * diff --git a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java index 70abdb9d1..5eb27ca1e 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java @@ -1379,8 +1379,7 @@ public interface RedisZSetCommands { * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range) { + default Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range) { return zRangeStoreByLex(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); } @@ -1396,43 +1395,38 @@ public interface RedisZSetCommands { * @see Redis Documentation: ZRANGESTORE */ @Nullable - Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit); + Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); /** - * This command is like ZRANGE, but stores the result in the {@literal dstKey} destination key. + * This command is like ZRANGE … REV , but stores the result in the {@literal dstKey} destination key. * * @param dstKey must not be {@literal null}. * @param srcKey must not be {@literal null}. - * @param min minimal inclusive score - * @param max maximal inclusive score + * @param range must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. * @since 3.0 * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, double min, double max) { - return zRangeStoreByScore(dstKey, srcKey, org.springframework.data.domain.Range.closed(min, max)); + default Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range) { + return zRangeStoreRevByLex(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); } /** - * This command is like ZRANGE, but stores the result in the {@literal dstKey} destination key. + * This command is like ZRANGE … REV , but stores the result in the {@literal dstKey} destination key. * * @param dstKey must not be {@literal null}. * @param srcKey must not be {@literal null}. - * @param min minimal inclusive score - * @param max maximal inclusive score + * @param range must not be {@literal null}. * @param limit must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. * @since 3.0 * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, double min, double max, - org.springframework.data.redis.connection.Limit limit) { - return zRangeStoreByScore(dstKey, srcKey, org.springframework.data.domain.Range.closed(min, max), limit); - } + Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); /** * This command is like ZRANGE, but stores the result in the {@literal dstKey} destination key. @@ -1445,10 +1439,8 @@ public interface RedisZSetCommands { * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range) { - return zRangeStoreByScore(dstKey, srcKey, range, - org.springframework.data.redis.connection.Limit.unlimited()); + default Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range) { + return zRangeStoreByScore(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); } /** @@ -1463,8 +1455,38 @@ public interface RedisZSetCommands { * @see Redis Documentation: ZRANGESTORE */ @Nullable - Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit); + Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); + + /** + * This command is like ZRANGE … REV, but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, + org.springframework.data.domain.Range range) { + return zRangeStoreRevByScore(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); + } + + /** + * This command is like ZRANGE … REV, but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); } diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index 41cd699ef..f489f89aa 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -1999,8 +1999,7 @@ public interface StringRedisConnection extends RedisConnection { * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByLex(String dstKey, String srcKey, - org.springframework.data.domain.Range range) { + default Long zRangeStoreByLex(String dstKey, String srcKey, org.springframework.data.domain.Range range) { return zRangeStoreByLex(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); } @@ -2016,25 +2015,52 @@ public interface StringRedisConnection extends RedisConnection { * @see Redis Documentation: ZRANGESTORE */ @Nullable - Long zRangeStoreByLex(String dstKey, String srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit); + Long zRangeStoreByLex(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); + /** + * This command is like ZRANGE … REV , but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long zRangeStoreRevByLex(String dstKey, String srcKey, org.springframework.data.domain.Range range) { + return zRangeStoreRevByLex(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); + } + + /** + * This command is like ZRANGE … REV , but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long zRangeStoreRevByLex(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); /** * This command is like ZRANGE, but stores the result in the {@literal dstKey} destination key. * * @param dstKey must not be {@literal null}. * @param srcKey must not be {@literal null}. - * @param min minimal inclusive score - * @param max maximal inclusive score + * @param range must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. * @since 3.0 * @see Redis Documentation: ZRANGESTORE */ @Nullable - default Long zRangeStoreByScore(String dstKey, String srcKey, double min, double max) { - return zRangeStoreByScore(dstKey, srcKey, min, max, org.springframework.data.redis.connection.Limit.unlimited()); + default Long zRangeStoreByScore(String dstKey, String srcKey, org.springframework.data.domain.Range range) { + return zRangeStoreByScore(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); } /** @@ -2042,16 +2068,46 @@ public interface StringRedisConnection extends RedisConnection { * * @param dstKey must not be {@literal null}. * @param srcKey must not be {@literal null}. - * @param min minimal inclusive score - * @param max maximal inclusive score + * @param range must not be {@literal null}. * @param limit must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. * @since 3.0 * @see Redis Documentation: ZRANGESTORE */ @Nullable - Long zRangeStoreByScore(String dstKey, String srcKey, double min, double max, - org.springframework.data.redis.connection.Limit limit); + Long zRangeStoreByScore(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); + + /** + * This command is like ZRANGE … REV, but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long zRangeStoreRevByScore(String dstKey, String srcKey, + org.springframework.data.domain.Range range) { + return zRangeStoreRevByScore(dstKey, srcKey, range, org.springframework.data.redis.connection.Limit.unlimited()); + } + + /** + * This command is like ZRANGE … REV, but stores the result in the {@literal dstKey} destination key. + * + * @param dstKey must not be {@literal null}. + * @param srcKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long zRangeStoreRevByScore(String dstKey, String srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit); // ------------------------------------------------------------------------- // Methods dealing with Redis Hashes diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java index 02287ff77..6e2799d99 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java @@ -497,19 +497,37 @@ class JedisClusterZSetCommands implements RedisZSetCommands { } @Override - public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByLex(dstKey, srcKey, range, limit, false); + } + + @Override + public Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByLex(dstKey, srcKey, range, limit, true); + } + + private Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit, boolean rev) { + Assert.notNull(dstKey, "Destination key must not be null"); Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYLEX must not be null"); + Assert.notNull(range, "Range must not be null"); Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); byte[] min = JedisConverters.boundaryToBytesForZRangeByLex(range.getLowerBound(), JedisConverters.MINUS_BYTES); byte[] max = JedisConverters.boundaryToBytesForZRangeByLex(range.getUpperBound(), JedisConverters.PLUS_BYTES); - ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYLEX, min, max) - .limit(limit.getOffset(), limit.getCount()); + ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYLEX, min, max); + + if (limit.isLimited()) { + zRangeParams = zRangeParams.limit(limit.getOffset(), limit.getCount()); + } + + if (rev) { + zRangeParams = zRangeParams.rev(); + } try { return connection.getCluster().zrangestore(dstKey, srcKey, zRangeParams); @@ -518,22 +536,42 @@ class JedisClusterZSetCommands implements RedisZSetCommands { } } + @Nullable @Override - public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByScore(dstKey, srcKey, range, limit, false); + } + + @Nullable + @Override + public Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByScore(dstKey, srcKey, range, limit, true); + } + + private Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit, boolean rev) { + Assert.notNull(dstKey, "Destination key must not be null"); Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYSCORE must not be null"); + Assert.notNull(range, "Range for must not be null"); Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); - byte[] min = JedisConverters - .boundaryToBytesForZRange(range.getLowerBound(), JedisConverters.NEGATIVE_INFINITY_BYTES); - byte[] max = JedisConverters - .boundaryToBytesForZRange(range.getUpperBound(), JedisConverters.POSITIVE_INFINITY_BYTES); + byte[] min = JedisConverters.boundaryToBytesForZRange(range.getLowerBound(), + JedisConverters.NEGATIVE_INFINITY_BYTES); + byte[] max = JedisConverters.boundaryToBytesForZRange(range.getUpperBound(), + JedisConverters.POSITIVE_INFINITY_BYTES); - ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYSCORE, min, max) - .limit(limit.getOffset(), limit.getCount()); + ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYSCORE, min, max); + + if (limit.isLimited()) { + zRangeParams = zRangeParams.limit(limit.getOffset(), limit.getCount()); + } + + if (rev) { + zRangeParams = zRangeParams.rev(); + } try { return connection.getCluster().zrangestore(dstKey, srcKey, zRangeParams); diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java index 9c41cb455..b4e724548 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java @@ -680,47 +680,63 @@ class JedisZSetCommands implements RedisZSetCommands { } @Override - public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { - - Assert.notNull(dstKey, "Destination key must not be null"); - Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYLEX must not be null"); - Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); - - byte[] min = JedisConverters - .boundaryToBytesForZRangeByLex(range.getLowerBound(), JedisConverters.MINUS_BYTES); - byte[] max = JedisConverters - .boundaryToBytesForZRangeByLex(range.getUpperBound(), JedisConverters.PLUS_BYTES); - - ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYLEX, min, max) - .limit(limit.getOffset(), limit.getCount()); - - return connection.invoke().just(Jedis::zrangestore, PipelineBinaryCommands::zrangestore, - dstKey, srcKey, zRangeParams); + public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByLex(dstKey, srcKey, range, limit, false); } @Override - public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + public Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByLex(dstKey, srcKey, range, limit, true); + } + + private Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit, boolean rev) { Assert.notNull(dstKey, "Destination key must not be null"); Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYSCORE must not be null"); + Assert.notNull(range, "Range must not be null"); Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); - byte[] min = JedisConverters - .boundaryToBytesForZRange(range.getLowerBound(), JedisConverters.NEGATIVE_INFINITY_BYTES); - byte[] max = JedisConverters - .boundaryToBytesForZRange(range.getUpperBound(), JedisConverters.POSITIVE_INFINITY_BYTES); + byte[] min = JedisConverters.boundaryToBytesForZRangeByLex(range.getLowerBound(), JedisConverters.MINUS_BYTES); + byte[] max = JedisConverters.boundaryToBytesForZRangeByLex(range.getUpperBound(), JedisConverters.PLUS_BYTES); - ZRangeParams zRangeParams = new ZRangeParams(Protocol.Keyword.BYSCORE, min, max) - .limit(limit.getOffset(), limit.getCount()); + ZRangeParams zRangeParams = toZRangeParams(Protocol.Keyword.BYLEX, min, max, limit, rev); - return connection.invoke().just(Jedis::zrangestore, PipelineBinaryCommands::zrangestore, - dstKey, srcKey, zRangeParams); + return connection.invoke().just(Jedis::zrangestore, PipelineBinaryCommands::zrangestore, dstKey, srcKey, + zRangeParams); + } + + @Override + public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByScore(dstKey, srcKey, range, limit, false); + } + + @Override + public Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + return zRangeStoreByScore(dstKey, srcKey, range, limit, true); + } + + private Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit, boolean rev) { + + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); + + byte[] min = JedisConverters.boundaryToBytesForZRange(range.getLowerBound(), + JedisConverters.NEGATIVE_INFINITY_BYTES); + byte[] max = JedisConverters.boundaryToBytesForZRange(range.getUpperBound(), + JedisConverters.POSITIVE_INFINITY_BYTES); + + ZRangeParams zRangeParams = toZRangeParams(Protocol.Keyword.BYSCORE, min, max, limit, rev); + + return connection.invoke().just(Jedis::zrangestore, PipelineBinaryCommands::zrangestore, dstKey, srcKey, + zRangeParams); } private boolean isPipelined() { @@ -735,6 +751,23 @@ class JedisZSetCommands implements RedisZSetCommands { return new ZParams().weights(weights.toArray()).aggregate(ZParams.Aggregate.valueOf(aggregate.name())); } + static ZRangeParams toZRangeParams(Protocol.Keyword by, byte[] min, byte[] max, + org.springframework.data.redis.connection.Limit limit, boolean rev) { + + ZRangeParams zRangeParams; + + if (rev) { + zRangeParams = new ZRangeParams(by, max, min).rev(); + } else { + zRangeParams = new ZRangeParams(by, min, max); + } + + if (limit.isLimited()) { + zRangeParams = zRangeParams.limit(limit.getOffset(), limit.getCount()); + } + return zRangeParams; + } + /** * Workaround for broken Jedis BZPOP signature. * diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java index 069b68f79..834197e3f 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection.lettuce; +import io.lettuce.core.Limit; import io.lettuce.core.Range; import io.lettuce.core.ScanStream; import io.lettuce.core.ScoredValue; @@ -169,8 +170,8 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { Assert.notNull(command.getKey(), "Key must not be null"); - return new CommandResponse<>(command, cmd.zrandmemberWithScores(command.getKey(), command.getCount()) - .map(this::toTuple)); + return new CommandResponse<>(command, + cmd.zrandmemberWithScores(command.getKey(), command.getCount()).map(this::toTuple)); })); } @@ -225,6 +226,43 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { })); } + @Override + @SuppressWarnings("unchecked") + public Flux>> zRangeStore(Publisher commands) { + + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Source key must not be null"); + Assert.notNull(command.getDestKey(), "Destination key must not be null"); + Assert.notNull(command.getRange(), "Range must not be null"); + Assert.notNull(command.getLimit(), "Limit must not be null"); + + Limit limit = LettuceConverters.toLimit(command.getLimit()); + Mono result; + + if (command.getDirection() == Direction.ASC) { + + switch (command.getRangeMode()) { + case ByScore -> result = cmd.zrangestorebyscore(command.getDestKey(), command.getKey(), + (Range) LettuceConverters.toRange(command.getRange()), limit); + case ByLex -> result = cmd.zrangestorebylex(command.getDestKey(), command.getKey(), + RangeConverter.toRange(command.getRange()), limit); + default -> throw new IllegalStateException("Unsupported value: " + command.getRangeMode()); + } + } else { + switch (command.getRangeMode()) { + case ByScore -> result = cmd.zrevrangestorebyscore(command.getDestKey(), command.getKey(), + (Range) LettuceConverters.toRange(command.getRange()), limit); + case ByLex -> result = cmd.zrevrangestorebylex(command.getDestKey(), command.getKey(), + RangeConverter.toRange(command.getRange()), limit); + default -> throw new IllegalStateException("Unsupported value: " + command.getRangeMode()); + } + } + + return Mono.just(new CommandResponse<>(command, result)); + })); + } + @Override public Flux>> zRangeByScore( Publisher commands) { @@ -362,7 +400,7 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { Assert.notNull(command.getKey(), "Key must not be null"); Assert.notNull(command.getTimeout(), "Timeout must not be null"); - if(command.getTimeUnit() == TimeUnit.MILLISECONDS) { + if (command.getTimeUnit() == TimeUnit.MILLISECONDS) { double timeout = TimeoutUtils.toDoubleSeconds(command.getTimeout(), command.getTimeUnit()); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java index 9be0c5b98..23559d8c4 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java @@ -174,14 +174,13 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(limit, "Limit must not be null"); if (limit.isUnlimited()) { - return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrangebyscoreWithScores, key, - LettuceConverters. toRange(range)).toSet(LettuceConverters::toTuple); + return connection.invoke() + .fromMany(RedisSortedSetAsyncCommands::zrangebyscoreWithScores, key, LettuceConverters.toRange(range)) + .toSet(LettuceConverters::toTuple); } - return connection - .invoke().fromMany(RedisSortedSetAsyncCommands::zrangebyscoreWithScores, key, - LettuceConverters. toRange(range), LettuceConverters.toLimit(limit)) - .toSet(LettuceConverters::toTuple); + return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrangebyscoreWithScores, key, + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)).toSet(LettuceConverters::toTuple); } @@ -214,12 +213,11 @@ class LettuceZSetCommands implements RedisZSetCommands { if (limit.isUnlimited()) { return connection.invoke() - .fromMany(RedisSortedSetAsyncCommands::zrevrangebyscore, key, LettuceConverters. toRange(range)) - .toSet(); + .fromMany(RedisSortedSetAsyncCommands::zrevrangebyscore, key, LettuceConverters.toRange(range)).toSet(); } return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrevrangebyscore, key, - LettuceConverters. toRange(range), LettuceConverters.toLimit(limit)).toSet(); + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)).toSet(); } @@ -232,14 +230,13 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(limit, "Limit must not be null"); if (limit.isUnlimited()) { - return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrevrangebyscoreWithScores, key, - LettuceConverters. toRange(range)).toSet(LettuceConverters::toTuple); + return connection.invoke() + .fromMany(RedisSortedSetAsyncCommands::zrevrangebyscoreWithScores, key, LettuceConverters.toRange(range)) + .toSet(LettuceConverters::toTuple); } - return connection.invoke() - .fromMany(RedisSortedSetAsyncCommands::zrevrangebyscoreWithScores, key, - LettuceConverters. toRange(range), LettuceConverters.toLimit(limit)) - .toSet(LettuceConverters::toTuple); + return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrevrangebyscoreWithScores, key, + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)).toSet(LettuceConverters::toTuple); } @@ -248,8 +245,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(key, "Key must not be null"); - return connection.invoke().just(RedisSortedSetAsyncCommands::zcount, key, - LettuceConverters. toRange(range)); + return connection.invoke().just(RedisSortedSetAsyncCommands::zcount, key, LettuceConverters.toRange(range)); } @Override @@ -259,7 +255,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(range, "Range must not be null"); return connection.invoke().just(RedisSortedSetAsyncCommands::zlexcount, key, - LettuceConverters. toRange(range, true)); + LettuceConverters.toRange(range, true)); } @Nullable @@ -288,7 +284,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(key, "Key must not be null"); Assert.notNull(unit, "TimeUnit must not be null"); - if(TimeUnit.MILLISECONDS == unit) { + if (TimeUnit.MILLISECONDS == unit) { return connection.invoke(connection.getAsyncDedicatedConnection()) .from(RedisSortedSetAsyncCommands::bzpopmin, TimeoutUtils.toDoubleSeconds(timeout, unit), key) @@ -326,7 +322,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(key, "Key must not be null"); Assert.notNull(unit, "TimeUnit must not be null"); - if(TimeUnit.MILLISECONDS == unit) { + if (TimeUnit.MILLISECONDS == unit) { return connection.invoke(connection.getAsyncDedicatedConnection()) .from(RedisSortedSetAsyncCommands::bzpopmax, TimeoutUtils.toDoubleSeconds(timeout, unit), key) @@ -379,7 +375,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(range, "Range must not be null for ZREMRANGEBYLEX"); return connection.invoke().just(RedisSortedSetAsyncCommands::zremrangebylex, key, - LettuceConverters. toRange(range, true)); + LettuceConverters.toRange(range, true)); } @Override @@ -389,7 +385,7 @@ class LettuceZSetCommands implements RedisZSetCommands { Assert.notNull(range, "Range for ZREMRANGEBYSCORE must not be null"); return connection.invoke().just(RedisSortedSetAsyncCommands::zremrangebyscore, key, - LettuceConverters. toRange(range)); + LettuceConverters.toRange(range)); } @Override @@ -601,11 +597,11 @@ class LettuceZSetCommands implements RedisZSetCommands { if (limit.isUnlimited()) { return connection.invoke() - .fromMany(RedisSortedSetAsyncCommands::zrangebyscore, key, LettuceConverters. toRange(range)).toSet(); + .fromMany(RedisSortedSetAsyncCommands::zrangebyscore, key, LettuceConverters.toRange(range)).toSet(); } return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrangebyscore, key, - LettuceConverters. toRange(range), LettuceConverters.toLimit(limit)).toSet(); + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)).toSet(); } @Override @@ -618,12 +614,11 @@ class LettuceZSetCommands implements RedisZSetCommands { if (limit.isUnlimited()) { return connection.invoke() - .fromMany(RedisSortedSetAsyncCommands::zrangebylex, key, LettuceConverters. toRange(range, true)) - .toSet(); + .fromMany(RedisSortedSetAsyncCommands::zrangebylex, key, LettuceConverters.toRange(range, true)).toSet(); } return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrangebylex, key, - LettuceConverters. toRange(range, true), LettuceConverters.toLimit(limit)).toSet(); + LettuceConverters.toRange(range, true), LettuceConverters.toLimit(limit)).toSet(); } @Override @@ -631,47 +626,70 @@ class LettuceZSetCommands implements RedisZSetCommands { org.springframework.data.redis.connection.Limit limit) { Assert.notNull(key, "Key must not be null"); - Assert.notNull(range, "Range for ZREVRANGEBYLEX must not be null"); + Assert.notNull(range, "Range for must not be null"); Assert.notNull(limit, "Limit must not be null"); if (limit.isUnlimited()) { return connection.invoke() - .fromMany(RedisSortedSetAsyncCommands::zrevrangebylex, key, LettuceConverters. toRange(range, true)) - .toSet(); + .fromMany(RedisSortedSetAsyncCommands::zrevrangebylex, key, LettuceConverters.toRange(range, true)).toSet(); } return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrevrangebylex, key, - LettuceConverters. toRange(range, true), LettuceConverters.toLimit(limit)).toSet(); + LettuceConverters.toRange(range, true), LettuceConverters.toLimit(limit)).toSet(); } @Override - public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + public Long zRangeStoreByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + Assert.notNull(dstKey, "Destination key must not be null"); Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYLEX must not be null"); + Assert.notNull(range, "Range for must not be null"); Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); - return connection.invoke().just(RedisSortedSetAsyncCommands::zrangestorebylex, dstKey, srcKey, - LettuceConverters.toRange(range, true), LettuceConverters.toLimit(limit)); - + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)); } @Override - public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, - org.springframework.data.domain.Range range, - org.springframework.data.redis.connection.Limit limit) { + public Long zRangeStoreRevByLex(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + Assert.notNull(dstKey, "Destination key must not be null"); Assert.notNull(srcKey, "Source key must not be null"); - Assert.notNull(range, "Range for ZRANGESTORE BYSCORE must not be null"); + Assert.notNull(range, "Range for must not be null"); + Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); + + return connection.invoke().just(RedisSortedSetAsyncCommands::zrevrangestorebylex, dstKey, srcKey, + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)); + } + + @Override + public Long zRangeStoreByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(range, "Range for must not be null"); Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); return connection.invoke().just(RedisSortedSetAsyncCommands::zrangestorebyscore, dstKey, srcKey, LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)); } + @Override + public Long zRangeStoreRevByScore(byte[] dstKey, byte[] srcKey, org.springframework.data.domain.Range range, + org.springframework.data.redis.connection.Limit limit) { + + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(range, "Range for must not be null"); + Assert.notNull(limit, "Limit must not be null. Use Limit.unlimited() instead."); + + return connection.invoke().just(RedisSortedSetAsyncCommands::zrevrangestorebyscore, dstKey, srcKey, + LettuceConverters.toRange(range), LettuceConverters.toLimit(limit)); + } + public RedisClusterCommands getConnection() { return connection.getConnection(); } diff --git a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java index 2187cfef4..cf53bc3ac 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java @@ -932,8 +932,8 @@ public interface BoundZSetOperations extends BoundKeyOperations { /** * Get all elements {@literal n} elements, where {@literal n = } {@link Limit#getCount()}, starting at - * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} having a value - * between {@link org.springframework.data.redis.connection.RedisZSetCommands.Range#getMin()} and + * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} having a value between + * {@link org.springframework.data.redis.connection.RedisZSetCommands.Range#getMin()} and * {@link org.springframework.data.redis.connection.RedisZSetCommands.Range#getMax()}. * * @param range must not be {@literal null}. @@ -945,7 +945,8 @@ public interface BoundZSetOperations extends BoundKeyOperations { */ @Nullable @Deprecated(since = "3.0", forRemoval = true) - default Set reverseRangeByLex(org.springframework.data.redis.connection.RedisZSetCommands.Range range, Limit limit) { + default Set reverseRangeByLex(org.springframework.data.redis.connection.RedisZSetCommands.Range range, + Limit limit) { return reverseRangeByLex(range.toRange(), limit); } @@ -963,6 +964,133 @@ public interface BoundZSetOperations extends BoundKeyOperations { @Nullable Set reverseRangeByLex(Range range, Limit limit); + /** + * Store all elements at {@code dstKey} with lexicographical ordering from {@literal ZSET} at the bound key with a + * value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByLex(Range) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long rangeAndStoreByLex(K dstKey, Range range) { + return rangeAndStoreByLex(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with lexicographical ordering from {@literal ZSET} at the bound key with a value between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByLex(Range, Limit) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long rangeAndStoreByLex(K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse lexicographical ordering from {@literal ZSET} at the bound key + * with a value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByLex(Range) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long reverseRangeAndStoreByLex(K dstKey, Range range) { + return reverseRangeAndStoreByLex(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at the bound key with a value + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByLex(Range, Limit) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long reverseRangeAndStoreByLex(K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with ordering by score from {@literal ZSET} at the bound key with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByScore(double, double) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long rangeAndStoreByScore(K dstKey, Range range) { + return rangeAndStoreByScore(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with ordering by score from {@literal ZSET} at the bound key with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByScore(double, double) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long rangeAndStoreByScore(K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse ordering by score from {@literal ZSET} at the bound key with a + * score between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByScore(double, double) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long reverseRangeAndStoreByScore(K dstKey, Range range) { + return reverseRangeAndStoreByScore(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse ordering by score from {@literal ZSET} at the bound key with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long reverseRangeAndStoreByScore(K dstKey, Range range, Limit limit); + /** * @return never {@literal null}. */ diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java index 5dfd9dfdf..0a2fae3c8 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java @@ -288,6 +288,50 @@ class DefaultReactiveZSetOperations implements ReactiveZSetOperations connection.zRevRangeByScoreWithScores(rawKey(key), range, limit).map(this::readTypedTuple)); } + @Override + public Mono rangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return createMono(connection -> connection.zRangeStoreByLex(rawKey(srcKey), rawKey(dstKey), range, limit)); + } + + @Override + public Mono reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return createMono(connection -> connection.zRangeStoreRevByLex(rawKey(srcKey), rawKey(dstKey), range, limit)); + } + + @Override + public Mono rangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return createMono(connection -> connection.zRangeStoreByScore(rawKey(srcKey), rawKey(dstKey), range, limit)); + } + + @Override + public Mono reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit) { + + Assert.notNull(srcKey, "Source key must not be null"); + Assert.notNull(dstKey, "Destination key must not be null"); + Assert.notNull(range, "Range must not be null"); + Assert.notNull(limit, "Limit must not be null"); + + return createMono(connection -> connection.zRangeStoreRevByScore(rawKey(srcKey), rawKey(dstKey), range, limit)); + } + @Override public Flux> scan(K key, ScanOptions options) { diff --git a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java index f92acfabe..5b258b269 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java @@ -228,19 +228,33 @@ class DefaultZSetOperations extends AbstractOperations implements ZS } @Override - public Long rangeStoreByLex(K dstKey, K srcKey, Range range, Limit limit) { + public Long rangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit) { byte[] rawDstKey = rawKey(dstKey); byte[] rawSrcKey = rawKey(srcKey); return execute(connection -> connection.zRangeStoreByLex(rawDstKey, rawSrcKey, serialize(range), limit)); } @Override - public Long rangeStoreByScore(K dstKey, K srcKey, Range range, Limit limit) { + public Long reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit) { + byte[] rawDstKey = rawKey(dstKey); + byte[] rawSrcKey = rawKey(srcKey); + return execute(connection -> connection.zRangeStoreRevByLex(rawDstKey, rawSrcKey, serialize(range), limit)); + } + + @Override + public Long rangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit) { byte[] rawDstKey = rawKey(dstKey); byte[] rawSrcKey = rawKey(srcKey); return execute(connection -> connection.zRangeStoreByScore(rawDstKey, rawSrcKey, range, limit)); } + @Override + public Long reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit) { + byte[] rawDstKey = rawKey(dstKey); + byte[] rawSrcKey = rawKey(srcKey); + return execute(connection -> connection.zRangeStoreRevByScore(rawDstKey, rawSrcKey, range, limit)); + } + @Override public Set rangeByScore(K key, double min, double max) { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java index f46a19f00..3d6f07901 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -29,6 +29,7 @@ import org.springframework.data.redis.connection.zset.Aggregate; import org.springframework.data.redis.connection.zset.Tuple; import org.springframework.data.redis.connection.zset.Weights; import org.springframework.data.redis.core.ZSetOperations.TypedTuple; +import org.springframework.lang.Nullable; /** * Redis ZSet/sorted set specific operations. @@ -300,6 +301,128 @@ public interface ReactiveZSetOperations { */ Flux> reverseRangeByScoreWithScores(K key, Range range, Limit limit); + /** + * Store all elements at {@code dstKey} with lexicographical ordering from {@literal ZSET} at {@code srcKey} with a + * value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono rangeAndStoreByLex(K srcKey, K dstKey, Range range) { + return rangeAndStoreByLex(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with lexicographical ordering from {@literal ZSET} at {@code srcKey} with a value between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + Mono rangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse lexicographical ordering from {@literal ZSET} at {@code srcKey} + * with a value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range) { + return reverseRangeAndStoreByLex(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at {@code srcKey} with a value + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see #reverseRangeByLex(Object, Range, Limit) + * @see Redis Documentation: ZRANGESTORE + */ + Mono reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with ordering by score from {@literal ZSET} at {@code srcKey} with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Mono rangeAndStoreByScore(K srcKey, K dstKey, Range range) { + return rangeAndStoreByScore(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with ordering by score from {@literal ZSET} at {@code srcKey} with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + Mono rangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse ordering by score from {@literal ZSET} at {@code srcKey} with a + * score between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + default Mono reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range) { + return reverseRangeAndStoreByScore(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse ordering by score from {@literal ZSET} at {@code srcKey} with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements. + * @since 3.0 + * @see Redis Documentation: ZRANGESTORE + */ + Mono reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit); + /** * Use a {@link Flux} to iterate over entries in the sorted set at {@code key}. The resulting {@link Flux} acts as a * cursor and issues {@code ZSCAN} commands itself as long as the subscriber signals demand. diff --git a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java index 4867e06ab..d2710d5d2 100644 --- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java @@ -1101,7 +1101,7 @@ public interface ZSetOperations { * between {@link org.springframework.data.redis.connection.RedisZSetCommands.Range#getMin()} and * {@link org.springframework.data.redis.connection.RedisZSetCommands.Range#getMax()}. * - * @param key must not be {@literal null} + * @param key must not be {@literal null}. * @param range must not be {@literal null}. * @param limit can be {@literal null}. * @return {@literal null} when used in pipeline / transaction. @@ -1121,7 +1121,7 @@ public interface ZSetOperations { * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at {@code key} with a value * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. * - * @param key must not be {@literal null} + * @param key must not be {@literal null}. * @param range must not be {@literal null}. * @param limit can be {@literal null}. * @return {@literal null} when used in pipeline / transaction. @@ -1131,11 +1131,141 @@ public interface ZSetOperations { @Nullable Set reverseRangeByLex(K key, Range range, Limit limit); + /** + * Store all elements at {@code dstKey} with lexicographical ordering from {@literal ZSET} at {@code srcKey} with a + * value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByLex(Object, Range) + * @see Redis Documentation: ZRANGESTORE + */ @Nullable - Long rangeStoreByLex(K dstKey, K srcKey, Range range, Limit limit); + default Long rangeAndStoreByLex(K srcKey, K dstKey, Range range) { + return rangeAndStoreByLex(srcKey, dstKey, range, Limit.unlimited()); + } + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with lexicographical ordering from {@literal ZSET} at {@code srcKey} with a value between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByLex(Object, Range, Limit) + * @see Redis Documentation: ZRANGESTORE + */ @Nullable - Long rangeStoreByScore(K dstKey, K srcKey, Range range, Limit limit); + Long rangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse lexicographical ordering from {@literal ZSET} at {@code srcKey} + * with a value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByLex(Object, Range) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range) { + return reverseRangeAndStoreByLex(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at {@code srcKey} with a value + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByLex(Object, Range, Limit) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long reverseRangeAndStoreByLex(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with ordering by score from {@literal ZSET} at {@code srcKey} with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByScore(Object, double, double) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long rangeAndStoreByScore(K srcKey, K dstKey, Range range) { + return rangeAndStoreByScore(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with ordering by score from {@literal ZSET} at {@code srcKey} with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #rangeByScore(Object, double, double, long, long) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long rangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse ordering by score from {@literal ZSET} at {@code srcKey} with a + * score between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByScore(Object, double, double) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + default Long reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range) { + return reverseRangeAndStoreByScore(srcKey, dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse ordering by score from {@literal ZSET} at {@code srcKey} with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param srcKey must not be {@literal null}. + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return the number of stored elements or {@literal null} when used in pipeline / transaction. + * @since 3.0 + * @see #reverseRangeByScore(Object, double, double, long, long) + * @see Redis Documentation: ZRANGESTORE + */ + @Nullable + Long reverseRangeAndStoreByScore(K srcKey, K dstKey, Range range, Limit limit); /** * @return never {@literal null}. diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java index 7ad7db419..4fab38862 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java @@ -260,6 +260,30 @@ public class DefaultRedisZSet extends AbstractRedisCollection implements R return boundZSetOps.reverseRangeWithScores(start, end); } + @Override + public RedisZSet rangeAndStoreByLex(String dstKey, Range range, Limit limit) { + boundZSetOps.rangeAndStoreByLex(dstKey, range, limit); + return new DefaultRedisZSet<>(getOperations().boundZSetOps(dstKey)); + } + + @Override + public RedisZSet reverseRangeAndStoreByLex(String dstKey, Range range, Limit limit) { + boundZSetOps.reverseRangeAndStoreByLex(dstKey, range, limit); + return new DefaultRedisZSet<>(getOperations().boundZSetOps(dstKey)); + } + + @Override + public RedisZSet rangeAndStoreByScore(String dstKey, Range range, Limit limit) { + boundZSetOps.rangeAndStoreByScore(dstKey, range, limit); + return new DefaultRedisZSet<>(getOperations().boundZSetOps(dstKey)); + } + + @Override + public RedisZSet reverseRangeAndStoreByScore(String dstKey, Range range, Limit limit) { + boundZSetOps.reverseRangeAndStoreByScore(dstKey, range, limit); + return new DefaultRedisZSet<>(getOperations().boundZSetOps(dstKey)); + } + @Override public RedisZSet remove(long start, long end) { boundZSetOps.removeRange(start, end); diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java index b22c2fb85..d038ca942 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java +++ b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java @@ -434,6 +434,117 @@ public interface RedisZSet extends RedisCollection, Set { */ Set> reverseRangeByScoreWithScores(double min, double max); + /** + * Store all elements at {@code dstKey} with lexicographical ordering from {@literal ZSET} at the bound key with a + * value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #rangeByLex(Range) + */ + default RedisZSet rangeAndStoreByLex(String dstKey, Range range) { + return rangeAndStoreByLex(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with lexicographical ordering from {@literal ZSET} at the bound key with a value between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #rangeByLex(Range, Limit) + */ + RedisZSet rangeAndStoreByLex(String dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse lexicographical ordering from {@literal ZSET} at the bound key + * with a value between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #reverseRangeByLex(Range) + */ + default RedisZSet reverseRangeAndStoreByLex(String dstKey, Range range) { + return reverseRangeAndStoreByLex(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse lexicographical ordering from {@literal ZSET} at the bound key with a value + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #reverseRangeByLex(Range, Limit) + */ + RedisZSet reverseRangeAndStoreByLex(String dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with ordering by score from {@literal ZSET} at the bound key with a score + * between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #rangeByScore(double, double) + */ + default RedisZSet rangeAndStoreByScore(String dstKey, Range range) { + return rangeAndStoreByScore(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with ordering by score from {@literal ZSET} at the bound key with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #rangeByScore(double, double) + */ + RedisZSet rangeAndStoreByScore(String dstKey, Range range, Limit limit); + + /** + * Store all elements at {@code dstKey} with reverse ordering by score from {@literal ZSET} at the bound key with a + * score between {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + * @see #reverseRangeByScore(double, double) + */ + default RedisZSet reverseRangeAndStoreByScore(String dstKey, Range range) { + return reverseRangeAndStoreByScore(dstKey, range, Limit.unlimited()); + } + + /** + * Store {@literal n} elements at {@code dstKey}, where {@literal n = } {@link Limit#getCount()}, starting at + * {@link Limit#getOffset()} with reverse ordering by score from {@literal ZSET} at the bound key with a score between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param dstKey must not be {@literal null}. + * @param range must not be {@literal null}. + * @param limit must not be {@literal null}. + * @return a new {@link RedisZSet} pointing at {@code destKey} + * @since 3.0 + */ + RedisZSet reverseRangeAndStoreByScore(String dstKey, Range range, Limit limit); + /** * Remove elements in range between {@code start} and {@code end} from sorted set. * diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 767820515..ffb6de91d 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -4070,14 +4070,33 @@ public abstract class AbstractConnectionIntegrationTests { } @Test //GH-2345 - public void zRangeStoreStoresKeysCreatedByZAddByScore() { - final String dstKey = KEY_2; - final String srcKey = KEY_1; + public void zRangeStoreByScoreStoresKeys() { + String dstKey = KEY_2; + String srcKey = KEY_1; actual.add(connection.zAdd(srcKey, 1, VALUE_1)); actual.add(connection.zAdd(srcKey, 2, VALUE_2)); actual.add(connection.zAdd(srcKey, 3, VALUE_3)); actual.add(connection.zAdd(srcKey, 4, VALUE_4)); - actual.add(connection.zRangeStoreByScore(dstKey, srcKey, 3, 4)); + actual.add(connection.zRangeStoreByScore(dstKey, srcKey, Range.closed(3, 4))); + actual.add(connection.zRange(dstKey, 0, -1)); + List result = getResults(); + assertThat(result.get(0)).isEqualTo(true); + assertThat(result.get(1)).isEqualTo(true); + assertThat(result.get(2)).isEqualTo(true); + assertThat(result.get(3)).isEqualTo(true); + assertThat(result.get(4)).isEqualTo(2L); + assertThat((LinkedHashSet) result.get(5)).containsSequence(VALUE_3, VALUE_4); + } + + @Test // GH-2345 + public void zRangeStoreRevByScoreStoresKeys() { + String dstKey = KEY_2; + String srcKey = KEY_1; + actual.add(connection.zAdd(srcKey, 1, VALUE_1)); + actual.add(connection.zAdd(srcKey, 2, VALUE_2)); + actual.add(connection.zAdd(srcKey, 3, VALUE_3)); + actual.add(connection.zAdd(srcKey, 4, VALUE_4)); + actual.add(connection.zRangeStoreRevByScore(dstKey, srcKey, Range.closed(3, 4))); actual.add(connection.zRange(dstKey, 0, -1)); List result = getResults(); assertThat(result.get(0)).isEqualTo(true); @@ -4089,9 +4108,9 @@ public abstract class AbstractConnectionIntegrationTests { } @Test //GH-2345 - public void zRangeStoreStoresKeysCreatedByZAddByLex() { - final String dstKey = KEY_2; - final String srcKey = KEY_1; + public void zRangeStoreByLexStoresKeys() { + String dstKey = KEY_2; + String srcKey = KEY_1; actual.add(connection.zAdd(srcKey, 0, VALUE_3)); actual.add(connection.zAdd(srcKey, 0, VALUE_1)); actual.add(connection.zAdd(srcKey, 0, VALUE_4)); @@ -4107,6 +4126,25 @@ public abstract class AbstractConnectionIntegrationTests { assertThat((LinkedHashSet) result.get(5)).containsSequence(VALUE_3, VALUE_4); } + @Test // GH-2345 + public void zRangeStoreRevByLexStoresKeys() { + String dstKey = KEY_2; + String srcKey = KEY_1; + actual.add(connection.zAdd(srcKey, 0, VALUE_3)); + actual.add(connection.zAdd(srcKey, 0, VALUE_1)); + actual.add(connection.zAdd(srcKey, 0, VALUE_4)); + actual.add(connection.zAdd(srcKey, 0, VALUE_2)); + actual.add(connection.zRangeStoreRevByLex(dstKey, srcKey, Range.rightUnbounded(Bound.inclusive(VALUE_3)))); + actual.add(connection.zRange(dstKey, 0, -1)); + List result = getResults(); + assertThat(result.get(0)).isEqualTo(true); + assertThat(result.get(1)).isEqualTo(true); + assertThat(result.get(2)).isEqualTo(true); + assertThat(result.get(3)).isEqualTo(true); + assertThat(result.get(4)).isEqualTo(2L); + assertThat((LinkedHashSet) result.get(5)).containsSequence(VALUE_3, VALUE_4); + } + protected void verifyResults(List expected) { assertThat(getResults()).isEqualTo(expected); } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java index 451ff8e76..edb60907e 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java @@ -420,6 +420,96 @@ public class DefaultReactiveZSetOperationsIntegrationTests { .verifyComplete(); } + @ParameterizedRedisTest // GH-2345 + void rangeAndStoreByLex() { + + assumeThat(serializer instanceof StringRedisSerializer).isTrue(); + + K key = keyFactory.instance(); + K destKey = keyFactory.instance(); + V a = (V) "a"; + V b = (V) "b"; + V c = (V) "c"; + + zSetOperations.add(key, a, 1).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, b, 2).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, c, 3).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.rangeAndStoreByLex(key, destKey, Range.closed("a", "b")).as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2345 + void rangeAndStoreByScore() { + + assumeThat(valueFactory instanceof ByteBufferObjectFactory).isFalse(); + + K key = keyFactory.instance(); + K destKey = keyFactory.instance(); + V a = valueFactory.instance(); + V b = valueFactory.instance(); + V c = valueFactory.instance(); + + zSetOperations.add(key, a, 1).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, b, 2).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, c, 3).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.rangeAndStoreByScore(key, destKey, Range.closed(1.0, 2.0)).as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + + zSetOperations.range(destKey, Range.unbounded()).as(StepVerifier::create) // + .expectNext(a) // + .expectNext(b) // + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2345 + void reverseRangeAndStoreByLex() { + + assumeThat(serializer instanceof StringRedisSerializer).isTrue(); + + K key = keyFactory.instance(); + K destKey = keyFactory.instance(); + V a = (V) "a"; + V b = (V) "b"; + V c = (V) "c"; + + zSetOperations.add(key, a, 1).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, b, 2).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, c, 3).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.reverseRangeAndStoreByLex(key, destKey, Range.closed("a", "b")).as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + } + + @ParameterizedRedisTest // GH-2345 + void reverseRangeAndStoreByScore() { + + assumeThat(valueFactory instanceof ByteBufferObjectFactory).isFalse(); + + K key = keyFactory.instance(); + K destKey = keyFactory.instance(); + V a = valueFactory.instance(); + V b = valueFactory.instance(); + V c = valueFactory.instance(); + + zSetOperations.add(key, a, 1).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, b, 2).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, c, 3).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.reverseRangeAndStoreByScore(key, destKey, Range.closed(1.0, 2.0)).as(StepVerifier::create) // + .expectNext(2L) // + .verifyComplete(); + + zSetOperations.range(destKey, Range.unbounded()).as(StepVerifier::create) // + .expectNext(a) // + .expectNext(b) // + .verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-743 void scan() { @@ -594,8 +684,7 @@ public class DefaultReactiveZSetOperationsIntegrationTests { zSetOperations.add(key, value2, 10).as(StepVerifier::create).expectNext(true).verifyComplete(); zSetOperations.removeRangeByScore(key, NINE_TO_ELEVEN_DOUBLE).as(StepVerifier::create).expectNext(1L) - .expectComplete() - .verify(); + .expectComplete().verify(); zSetOperations.range(key, ZERO_TO_FIVE).as(StepVerifier::create) // .expectNext(value1) // .verifyComplete(); @@ -806,8 +895,7 @@ public class DefaultReactiveZSetOperationsIntegrationTests { zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(22d).verifyComplete(); zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM, Weights.of(2, 1)) - .as(StepVerifier::create) - .expectNext(3L).verifyComplete(); + .as(StepVerifier::create).expectNext(3L).verifyComplete(); zSetOperations.score(destKey, shared).as(StepVerifier::create).expectNext(33d).verifyComplete(); } diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java index 295aa27bb..73ddffe1e 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java @@ -572,6 +572,74 @@ public abstract class AbstractRedisZSetTestIntegration extends AbstractRedisC assertThat(tuple2.getScore()).isEqualTo(Double.valueOf(3)); } + @ParameterizedRedisTest // GH-2345 + void testRangeAndStoreByLex() { + + assumeThat(factory).isOfAnyClassIn(DoubleObjectFactory.class, DoubleAsStringObjectFactory.class, + LongAsStringObjectFactory.class, LongObjectFactory.class); + + T t1 = getT(); + T t2 = getT(); + T t3 = getT(); + + zSet.add(t1, 1); + zSet.add(t2, 2); + zSet.add(t3, 3); + RedisZSet tuples = zSet.rangeAndStoreByLex("dest", Range.closed(t2.toString(), t3.toString())); + + assertThat(tuples).hasSize(2).containsSequence(t2, t3); + } + + @ParameterizedRedisTest // GH-2345 + void testRangeAndStoreRevByLex() { + + assumeThat(factory).isOfAnyClassIn(DoubleObjectFactory.class, DoubleAsStringObjectFactory.class, + LongAsStringObjectFactory.class, LongObjectFactory.class); + + T t1 = getT(); + T t2 = getT(); + T t3 = getT(); + + zSet.add(t1, 1); + zSet.add(t2, 2); + zSet.add(t3, 3); + RedisZSet tuples = zSet.reverseRangeAndStoreByLex("dest", Range.closed(t1.toString(), t3.toString()), + Limit.limit().count(2).offset(1)); + + assertThat(tuples).hasSize(2).containsSequence(t1, t2); + } + + @ParameterizedRedisTest // GH-2345 + void testRangeAndStoreByScore() { + + T t1 = getT(); + T t2 = getT(); + T t3 = getT(); + + zSet.add(t1, 1); + zSet.add(t2, 2); + zSet.add(t3, 3); + RedisZSet tuples = zSet.rangeAndStoreByScore("dest", Range.closed(2, 3)); + + assertThat(tuples).hasSize(2).containsSequence(t2, t3); + } + + @ParameterizedRedisTest // GH-2345 + void testRangeAndStoreRevByScore() { + + T t1 = getT(); + T t2 = getT(); + T t3 = getT(); + + zSet.add(t1, 1); + zSet.add(t2, 2); + zSet.add(t3, 3); + RedisZSet tuples = zSet.reverseRangeAndStoreByScore("dest", Range.closed(1, 3), + Limit.limit().count(2).offset(0)); + + assertThat(tuples).hasSize(2).containsSequence(t2, t3); + } + @ParameterizedRedisTest void testRemove() { T t1 = getT();