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 6e28a7f74..2e6cd2bb2 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -1659,6 +1659,24 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return convertAndReturn(delegate.zUnionStore(destKey, sets), identityConverter); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zLexCount(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long zLexCount(byte[] key, Range range) { + return delegate.zLexCount(key, range); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#zLexCount(java.lang.String, org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long zLexCount(String key, Range range) { + return delegate.zLexCount(serialize(key), range); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisKeyCommands#pExpire(byte[], long) 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 5dbbf56a4..f0ca15ca0 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -55,6 +55,7 @@ import org.springframework.lang.Nullable; * @author Christoph Strobl * @author Mark Paluch * @author Tugdual Grall + * @author Andrey Shlykov * @since 2.0 */ public interface DefaultedRedisConnection extends RedisConnection { @@ -1093,6 +1094,13 @@ public interface DefaultedRedisConnection extends RedisConnection { return zSetCommands().zRangeByScore(key, min, max, offset, count); } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ + @Override + @Deprecated + default Long zLexCount(byte[] key, Range range) { + return zSetCommands().zLexCount(key, range); + } + // HASH COMMANDS /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */ 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 26db60793..98fdd036e 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java @@ -46,6 +46,7 @@ import org.springframework.util.Assert; * * @author Christoph Strobl * @author Mark Paluch + * @author Andrey Shlykov * @since 2.0 */ public interface ReactiveZSetCommands { @@ -1935,4 +1936,79 @@ public interface ReactiveZSetCommands { * @see Redis Documentation: ZREVRANGEBYLEX */ Flux>> zRangeByLex(Publisher commands); + + /** + * {@code ZLEXCOUNT} command parameters. + * + * @author Andrey Shlykov + * @see Redis Documentation: ZLEXCOUNT + */ + class ZLexCountCommand extends KeyCommand { + + private final Range range; + + private ZLexCountCommand(@Nullable ByteBuffer key, Range range) { + super(key); + this.range = range; + } + + /** + * Creates a new {@link ZLexCountCommand} given a {@link Range} of {@link String} to retrieve elements + * count. + * + * @param range must not be {@literal null}. + * @return a new {@link ZLexCountCommand} for {@link Range}. + */ + public static ZLexCountCommand stringsWithin(Range range) { + + Assert.notNull(range, "Range must not be null!"); + + return new ZLexCountCommand(null, range); + } + + /** + * 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 ZLexCountCommand} with {@literal key} applied. + */ + public ZLexCountCommand forKey(ByteBuffer key) { + + Assert.notNull(key, "Key must not be null!"); + + return new ZLexCountCommand(key, range); + } + + /** + * @return + */ + public Range getRange() { + return range; + } + } + + /** + * Count number of elements within sorted set with value within {@link Range}. + * + * @param key must not be {@literal null}. + * @param range must not be {@literal null}. + * @return + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + default Mono zLexCount(ByteBuffer key, Range range) { + return zLexCount(Mono.just(ZLexCountCommand.stringsWithin(range).forKey(key))).next() + .map(NumericResponse::getOutput); + } + + /** + * Count number of elements within sorted set with value within {@link Range}. + * + * @param commands must not be {@literal null}. + * @return + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + Flux> zLexCount(Publisher commands); + } 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 d41bfb331..5aa49421b 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java @@ -1059,4 +1059,16 @@ public interface RedisZSetCommands { @Nullable Set zRevRangeByLex(byte[] key, Range range, Limit limit); + /** + * Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max}. + * + * @param key must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + @Nullable + Long zLexCount(byte[] key, Range range); + } 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 9995f3a1c..5852d3f47 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -1541,6 +1541,19 @@ public interface StringRedisConnection extends RedisConnection { */ Set zRevRangeByLex(String key, Range range, Limit limit); + /** + * Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max}. + * + * @param key must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + * @see RedisZSetCommands#zLexCount(byte[], Range) + */ + @Nullable + Long zLexCount(String key, Range range); + // ------------------------------------------------------------------------- // 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 27c8c6d10..51757c8d1 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 @@ -795,6 +795,26 @@ class JedisClusterZSetCommands implements RedisZSetCommands { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zLexCount(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long zLexCount(byte[] key, Range range) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(range, "Range must not be null!"); + + byte[] min = JedisConverters.boundaryToBytesForZRangeByLex(range.getMin(), JedisConverters.MINUS_BYTES); + byte[] max = JedisConverters.boundaryToBytesForZRangeByLex(range.getMax(), JedisConverters.PLUS_BYTES); + + try { + return connection.getCluster().zlexcount(key, min, max); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + private DataAccessException convertJedisAccessException(Exception ex) { return connection.convertJedisAccessException(ex); } 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 5f4e65ad8..3143bc7fd 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 @@ -942,6 +942,34 @@ class JedisZSetCommands implements RedisZSetCommands { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zLexCount(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long zLexCount(byte[] key, Range range) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(range, "Range must not be null!"); + + byte[] min = JedisConverters.boundaryToBytesForZRangeByLex(range.getMin(), JedisConverters.MINUS_BYTES); + byte[] max = JedisConverters.boundaryToBytesForZRangeByLex(range.getMax(), JedisConverters.PLUS_BYTES); + + try { + if (isPipelined()) { + pipeline(connection.newJedisResult(connection.getRequiredPipeline().zlexcount(key, min, max))); + return null; + } + if (isQueueing()) { + transaction(connection.newJedisResult(connection.getRequiredTransaction().zlexcount(key, min, max))); + return null; + } + return connection.getJedis().zlexcount(key, min, max); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + private boolean isPipelined() { return connection.isPipelined(); } 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 36c694e40..ea493be63 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 @@ -45,6 +45,7 @@ import org.springframework.util.ObjectUtils; * @author Christoph Strobl * @author Mark Paluch * @author Michele Mancioppi + * @author Andrey Shlykov * @since 2.0 */ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { @@ -484,6 +485,24 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { })); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.ReactiveZSetCommands#zLexCount(org.reactivestreams.Publisher) + */ + @Override + public Flux> zLexCount(Publisher commands) { + + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null!"); + Assert.notNull(command.getRange(), "Range must not be null!"); + + Mono result = cmd.zlexcount(command.getKey(), RangeConverter.toRange(command.getRange())); + + return result.map(value -> new NumericResponse<>(command, value)); + })); + } + private static ZStoreArgs zStoreArgs(@Nullable Aggregate aggregate, @Nullable List weights) { ZStoreArgs args = new ZStoreArgs(); 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 1ec643d27..8355dbe10 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 @@ -927,6 +927,31 @@ class LettuceZSetCommands implements RedisZSetCommands { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zLexCount(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long zLexCount(byte[] key, Range range) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(range, "Range must not be null!"); + + try { + if (isPipelined()) { + pipeline(connection.newLettuceResult(getAsyncConnection().zlexcount(key, LettuceConverters.toRange(range)))); + return null; + } + if (isQueueing()) { + transaction(connection.newLettuceResult(getAsyncConnection().zlexcount(key, LettuceConverters.toRange(range)))); + return null; + } + return getConnection().zlexcount(key, LettuceConverters.toRange(range, true)); + } catch (Exception ex) { + throw convertLettuceAccessException(ex); + } + } + private boolean isPipelined() { return connection.isPipelined(); } 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 b5dae9205..c0474ccf8 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java @@ -33,6 +33,7 @@ import org.springframework.lang.Nullable; * @author Christoph Strobl * @author Mark Paluch * @author Wongoo (望哥) + * @author Andrey Shlykov */ public interface BoundZSetOperations extends BoundKeyOperations { @@ -414,6 +415,17 @@ public interface BoundZSetOperations extends BoundKeyOperations { @Nullable Set reverseRangeByLex(Range range, Limit limit); + /** + * Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max}. + * + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + @Nullable + Long lexCount(Range range); + /** * @return never {@literal null}. */ diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java index b48b48aa1..b453716ef 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java @@ -33,6 +33,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; * @author Christoph Strobl * @author Mark Paluch * @author Wongoo (望哥) + * @author Andrey Shlykov */ class DefaultBoundZSetOperations extends DefaultBoundKeyOperations implements BoundZSetOperations { @@ -329,6 +330,15 @@ class DefaultBoundZSetOperations extends DefaultBoundKeyOperations impl return ops.unionAndStore(getKey(), otherKeys, destKey, aggregate, weights); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.BoundZSetOperations#lexCount(org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long lexCount(Range range) { + return ops.lexCount(getKey(), range); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.BoundKeyOperations#getType() 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 f5f42d540..03d46e9b8 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java @@ -43,6 +43,7 @@ import org.springframework.util.Assert; * * @author Mark Paluch * @author Christoph Strobl + * @author Andrey Shlykov * @since 2.0 */ class DefaultReactiveZSetOperations implements ReactiveZSetOperations { @@ -548,6 +549,20 @@ class DefaultReactiveZSetOperations implements ReactiveZSetOperations connection.keyCommands().del(rawKey(key))).map(l -> l != 0); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.ReactiveZSetOperations#lexCount(java.lang.Object, org.springframework.data.domain.Range) + */ + @Override + public Mono lexCount(K key, Range range) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(range, "Range must not be null!"); + + + return createMono(connection -> connection.zLexCount(rawKey(key), range)); + } + private Mono createMono(Function> function) { Assert.notNull(function, "Function must not be null!"); 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 c6bc37a6e..31512bc1f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java @@ -34,6 +34,7 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Weights; * @author David Liu * @author Mark Paluch * @author Wongoo (望哥) + * @author Andrey Shlykov */ class DefaultZSetOperations extends AbstractOperations implements ZSetOperations { @@ -466,4 +467,11 @@ class DefaultZSetOperations extends AbstractOperations implements ZS return execute(connection -> connection.zRangeByScore(rawKey, min, max, offset, count), true); } + + @Override + public Long lexCount(K key, Range range) { + + byte[] rawKey = rawKey(key); + return execute(connection -> connection.zLexCount(rawKey, range), true); + } } 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 65babf9bf..19152b86c 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -32,6 +32,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; * * @author Mark Paluch * @author Christoph Strobl + * @author Andrey Shlykov * @see Redis Documentation: Sorted Set Commands * @since 2.0 */ @@ -461,4 +462,15 @@ public interface ReactiveZSetOperations { * @param key must not be {@literal null}. */ Mono delete(K key); + + /** + * Count number of elements within sorted set with a value between + * {@link Range#getLowerBound()} and {@link Range#getUpperBound()}. + * + * @param key must not be {@literal null}. + * @param range must not be {@literal null} + * @return + * @see Redis Documentation: ZLEXCOUNT + */ + Mono lexCount(K key, Range range); } 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 269b49b5e..0cda46d92 100644 --- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java @@ -33,6 +33,7 @@ import org.springframework.lang.Nullable; * @author Mark Paluch * @author Rosty Kerei * @author Wongoo (望哥) + * @author Andrey Shlykov */ public interface ZSetOperations { @@ -524,6 +525,18 @@ public interface ZSetOperations { @Nullable Set reverseRangeByLex(K key, Range range, Limit limit); + /** + * Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max}. + * + * @param key must not be {@literal null}. + * @param range must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + @Nullable + Long lexCount(K key, Range range); + /** * @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 33b7205aa..3382fe6ea 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 @@ -37,6 +37,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; * @author Costin Leau * @author Christoph Strobl * @author Mark Paluch + * @author Andrey Shlykov */ public class DefaultRedisZSet extends AbstractRedisCollection implements RedisZSet { @@ -392,6 +393,15 @@ public class DefaultRedisZSet extends AbstractRedisCollection implements R return boundZSetOps.reverseRank(o); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.support.collections.RedisZSet#lexCount(org.springframework.data.redis.connection.RedisZSetCommands.Range) + */ + @Override + public Long lexCount(Range range) { + return boundZSetOps.lexCount(range); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.support.collections.RedisZSet#score(java.lang.Object) 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 0e54e4376..da873af6a 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 @@ -36,6 +36,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple; * @author Costin Leau * @author Mark Paluch * @author Christoph Strobl + * @author Andrey Shlykov */ public interface RedisZSet extends RedisCollection, Set { @@ -167,6 +168,16 @@ public interface RedisZSet extends RedisCollection, Set { */ Double getDefaultScore(); + /** + * Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max}. + * + * @param range must not be {@literal null}. + * @return + * @since 2.4 + * @see Redis Documentation: ZLEXCOUNT + */ + Long lexCount(Range range); + /** * Returns the first (lowest) element currently in this 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 5d594c133..6cc1d521b 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -2454,6 +2454,43 @@ public abstract class AbstractConnectionIntegrationTests { assertThat((Set) results.get(13)).contains("c", "b").doesNotContain("a", "d", "e", "f", "g"); } + @Test // DATAREDIS-729 + @IfProfileValue(name = "redisVersion", value = "2.9.0+") + @WithRedisDriver({ RedisDriver.JEDIS, RedisDriver.LETTUCE }) + public void zLexCountTest() { + + actual.add(connection.zAdd("myzset", 0, "a")); + actual.add(connection.zAdd("myzset", 0, "b")); + actual.add(connection.zAdd("myzset", 0, "c")); + actual.add(connection.zAdd("myzset", 0, "d")); + actual.add(connection.zAdd("myzset", 0, "e")); + actual.add(connection.zAdd("myzset", 0, "f")); + actual.add(connection.zAdd("myzset", 0, "g")); + + actual.add(connection.zLexCount("myzset", Range.unbounded())); + actual.add(connection.zLexCount("myzset", Range.range().lt("c"))); + actual.add(connection.zLexCount("myzset", Range.range().lte("c"))); + actual.add(connection.zLexCount("myzset", Range.range().gte("aaa").lt("g"))); + actual.add(connection.zLexCount("myzset", Range.range().gte("e"))); + + List results = getResults(); + + Long count = (Long) results.get(7); + assertThat(count).isEqualTo(7); + + count = (Long) results.get(8); + assertThat(count).isEqualTo(2); + + count = (Long) results.get(9); + assertThat(count).isEqualTo(3); + + count = (Long) results.get(10); + assertThat(count).isEqualTo(5); + + count = (Long) results.get(11); + assertThat(count).isEqualTo(3); + } + @Test(expected = IllegalArgumentException.class) // DATAREDIS-316, DATAREDIS-692 @WithRedisDriver({ RedisDriver.JEDIS, RedisDriver.LETTUCE }) public void setWithExpirationAndNullOpionShouldThrowException() { 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 6a71276c8..2c290b04a 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java @@ -48,6 +48,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; * * @author Mark Paluch * @author Christoph Strobl + * @author Andrey Shlykov */ @RunWith(Parameterized.class) @SuppressWarnings("unchecked") @@ -693,4 +694,32 @@ public class DefaultReactiveZSetOperationsIntegrationTests { .expectNext(0L) // .verifyComplete(); } + + @Test // DATAREDIS-729 + public void lexCount() { + + assumeTrue(serializer instanceof StringRedisSerializer); + + K key = keyFactory.instance(); + + zSetOperations.add(key, (V) "a", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "b", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "c", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "d", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "e", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "f", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, (V) "g", 0).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.lexCount(key, Range.unbounded()).as(StepVerifier::create) + .expectNext(7L) + .verifyComplete(); + + zSetOperations.lexCount(key, Range.leftOpen("b", "f")).as(StepVerifier::create) + .expectNext(4L) + .verifyComplete(); + + zSetOperations.lexCount(key, Range.rightOpen("b", "f")).as(StepVerifier::create) + .expectNext(4L) + .verifyComplete(); + } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsTests.java index d40b848c3..a30ebd1a3 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsTests.java @@ -53,6 +53,7 @@ import org.springframework.test.annotation.IfProfileValue; * @author Christoph Strobl * @author Mark Paluch * @author Wongoo (望哥) + * @author Andrey Shlykov * @param Key type * @param Value type */ @@ -459,4 +460,40 @@ public class DefaultZSetOperationsTests { assertThat(zSetOps.score(key1, value1)).isCloseTo(6.0, offset(0.1)); } + + @Test // DATAREDIS-729 + public void testLexCountUnbounded() { + + assumeThat(valueFactory).isOfAnyClassIn(DoubleObjectFactory.class, DoubleAsStringObjectFactory.class, + LongAsStringObjectFactory.class, LongObjectFactory.class); + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + V value3 = valueFactory.instance(); + + zSetOps.add(key, value1, 0); + zSetOps.add(key, value2, 0); + zSetOps.add(key, value3, 0); + + assertThat(zSetOps.lexCount(key, RedisZSetCommands.Range.unbounded())).isEqualTo(3); + } + + @Test // DATAREDIS-729 + public void testLexCountBounded() { + + assumeThat(valueFactory).isOfAnyClassIn(DoubleObjectFactory.class, DoubleAsStringObjectFactory.class, + LongAsStringObjectFactory.class, LongObjectFactory.class); + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + V value3 = valueFactory.instance(); + + zSetOps.add(key, value1, 0); + zSetOps.add(key, value2, 0); + zSetOps.add(key, value3, 0); + + assertThat(zSetOps.lexCount(key, RedisZSetCommands.Range.range().gt(value1))).isEqualTo(2); + } } diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTest.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTest.java index ee881675b..88facd89b 100644 --- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTest.java +++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTest.java @@ -53,6 +53,7 @@ import org.springframework.test.annotation.IfProfileValue; * @author Jennifer Hickey * @author Thomas Darimont * @author Mark Paluch + * @author Andrey Shlykov */ public abstract class AbstractRedisZSetTest extends AbstractRedisCollectionTests { @@ -660,4 +661,38 @@ public abstract class AbstractRedisZSetTest extends AbstractRedisCollectionTe cursor.close(); } + + @Test // DATAREDIS-729 + public void testLexCountUnbounded() { + + 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, 1); + zSet.add(t3, 1); + + assertThat(zSet.lexCount(RedisZSetCommands.Range.unbounded())).isEqualTo(Long.valueOf(3)); + } + + @Test // DATAREDIS-729 + public void testLexCountBounded() { + + 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, 1); + zSet.add(t3, 1); + + assertThat(zSet.lexCount(RedisZSetCommands.Range.range().gt(t1))).isEqualTo(Long.valueOf(2)); + } }