diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index eab00c043..61653e755 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele == New in Spring Data Redis 2.6 * Support for `SubscriptionListener` when using `MessageListener` for subscription confirmation callbacks. `ReactiveRedisMessageListenerContainer` and `ReactiveRedisOperations` provide `receiveLater(…)` and `listenToLater(…)` methods to await until Redis acknowledges the subscription. -* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`). +* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`, `ZMSCORE`). [[new-in-2.5.0]] == New in Spring Data Redis 2.5 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 a6348abaf..aeb5ba896 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -1722,6 +1722,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return convertAndReturn(delegate.zScore(key, value), Converters.identityConverter()); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zMScore(byte[], byte[][]) + */ + @Override + public List zMScore(byte[] key, byte[]... values) { + return convertAndReturn(delegate.zMScore(key, values), Converters.identityConverter()); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisZSetCommands#zUnionStore(byte[], org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, org.springframework.data.redis.connection.RedisZSetCommands.Weights, byte[][]) @@ -3052,6 +3061,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return zScore(serialize(key), serialize(value)); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#zMScore(java.lang.String, java.lang.String[]) + */ + @Override + public List zMScore(String key, String... values) { + return zMScore(serialize(key), serializeMulti(values)); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.StringRedisConnection#zUnionStore(java.lang.String, org.springframework.data.redis.connection.RedisZSetCommands.Aggregate, int[], java.lang.String[]) 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 fad7b59af..e4df1a5ab 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -1103,6 +1103,13 @@ public interface DefaultedRedisConnection extends RedisConnection { return zSetCommands().zScore(key, value); } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ + @Override + @Deprecated + default List zMScore(byte[] key, byte[]... values) { + return zSetCommands().zMScore(key, values); + } + /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */ @Override @Deprecated 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 ed307120c..393846310 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java @@ -31,6 +31,7 @@ import org.springframework.data.domain.Sort.Direction; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand; +import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; import org.springframework.data.redis.connection.RedisZSetCommands.Limit; @@ -292,7 +293,7 @@ public interface ReactiveZSetCommands { * Creates a new {@link ZRemCommand} given a {@link Tuple}. * * @param value must not be {@literal null}. - * @return a new {@link ZAddCommand} for {@link Tuple}. + * @return a new {@link ZRemCommand} for {@link Tuple}. */ public static ZRemCommand values(ByteBuffer value) { @@ -305,7 +306,7 @@ public interface ReactiveZSetCommands { * Creates a new {@link ZRemCommand} given a {@link Collection} of {@link Tuple}. * * @param values must not be {@literal null}. - * @return a new {@link ZAddCommand} for {@link Tuple}. + * @return a new {@link ZRemCommand} for {@link Tuple}. */ public static ZRemCommand values(Collection values) { @@ -1283,7 +1284,7 @@ public interface ReactiveZSetCommands { * Creates a new {@link ZScoreCommand} given a {@link ByteBuffer member}. * * @param member must not be {@literal null}. - * @return a new {@link ZScoreCommand} for {@link Range}. + * @return a new {@link ZScoreCommand} for {@link ByteBuffer member}. */ public static ZScoreCommand scoreOf(ByteBuffer member) { @@ -1339,6 +1340,99 @@ public interface ReactiveZSetCommands { */ Flux> zScore(Publisher commands); + /** + * {@code ZMSCORE} command parameters. + * + * @author Mark Paluch + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + class ZMScoreCommand extends KeyCommand { + + private final Collection values; + + private ZMScoreCommand(@Nullable ByteBuffer key, Collection values) { + + super(key); + + this.values = values; + } + + /** + * Creates a new {@link ZMScoreCommand} given a {@link ByteBuffer member}. + * + * @param member must not be {@literal null}. + * @return a new {@link ZMScoreCommand} for {@link ByteBuffer}. + */ + public static ZMScoreCommand scoreOf(ByteBuffer member) { + + Assert.notNull(member, "Member must not be null!"); + + return new ZMScoreCommand(null, Collections.singletonList(member)); + } + + /** + * Creates a new {@link ZMScoreCommand} given a {@link List members}. + * + * @param member must not be {@literal null}. + * @return a new {@link ZMScoreCommand} for {@link List} of members. + */ + public static ZMScoreCommand scoreOf(Collection members) { + + Assert.notNull(members, "Members must not be null!"); + + return new ZMScoreCommand(null, members); + } + + /** + * 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 ZMScoreCommand} with {@literal key} applied. + */ + public ZMScoreCommand forKey(ByteBuffer key) { + + Assert.notNull(key, "Key must not be null!"); + + return new ZMScoreCommand(key, values); + } + + /** + * @return + */ + public Collection getValues() { + return values; + } + } + + /** + * Get the scores of elements with {@literal values} from sorted set with key {@literal key}. + * + * @param key must not be {@literal null}. + * @param values must not be {@literal null}. + * @return + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + default Mono> zMScore(ByteBuffer key, Collection values) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(values, "Values must not be null!"); + + return zMScore(Mono.just(ZMScoreCommand.scoreOf(values).forKey(key))).next().map(MultiValueResponse::getOutput); + } + + /** + * Get the scores of elements with {@link ZMScoreCommand#getValues()} from sorted set with key + * {@link ZMScoreCommand#getKey()} + * + * @param commands must not be {@literal null}. + * @return + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + Flux> zMScore(Publisher commands); + /** * {@code ZREMRANGEBYRANK} command parameters. * @@ -1787,7 +1881,7 @@ public interface ReactiveZSetCommands { * Creates a new {@link ZInterStoreCommand} given a {@link List} of keys. * * @param keys must not be {@literal null}. - * @return a new {@link ZInterStoreCommand} for {@link Range}. + * @return a new {@link ZInterStoreCommand} for {@link List} of keys. */ public static ZInterStoreCommand sets(List keys) { 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 fa8f5ef74..08eaf9739 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java @@ -993,6 +993,18 @@ public interface RedisZSetCommands { @Nullable Double zScore(byte[] key, byte[] value); + /** + * Get the scores of elements with {@code values} from sorted set with key {@code key}. + * + * @param key must not be {@literal null}. + * @param values the values. + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + @Nullable + List zMScore(byte[] key, byte[]... values); + /** * Remove elements in range between {@code start} and {@code end} from sorted set with {@code key}. * 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 d828fbe69..df3eee406 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -1455,6 +1455,18 @@ public interface StringRedisConnection extends RedisConnection { */ Double zScore(String key, String value); + /** + * Get the scores of elements with {@code values} from sorted set with key {@code key}. + * + * @param key must not be {@literal null}. + * @param values the values. + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: ZMSCORE + * @see RedisZSetCommands#zMScore(byte[], byte[][]) + * @since 2.6 + */ + List zMScore(String key, String... values); + /** * Remove elements in range between {@code start} and {@code end} from sorted set with {@code key}. * 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 53df37898..15480d38a 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 @@ -18,6 +18,7 @@ package org.springframework.data.redis.connection.jedis; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ZParams; +import java.util.List; import java.util.Set; import org.springframework.dao.DataAccessException; @@ -637,6 +638,23 @@ class JedisClusterZSetCommands implements RedisZSetCommands { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zMScore(byte[], byte[][]) + */ + @Override + public List zMScore(byte[] key, byte[][] values) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(values, "Values must not be null!"); + + try { + return connection.getCluster().zmscore(key, values); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisZSetCommands#zRemRange(byte[], long, long) 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 2f6b4f67e..f4a401cbb 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 @@ -21,14 +21,13 @@ import redis.clients.jedis.MultiKeyPipelineBase; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import redis.clients.jedis.ZParams; -import redis.clients.jedis.params.ZAddParams; import java.nio.charset.StandardCharsets; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import org.springframework.data.redis.connection.RedisZSetCommands; -import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs.Flag; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.KeyBoundCursor; import org.springframework.data.redis.core.ScanIteration; @@ -323,6 +322,19 @@ class JedisZSetCommands implements RedisZSetCommands { return connection.invoke().just(BinaryJedis::zscore, MultiKeyPipelineBase::zscore, key, value); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zScore(byte[], byte[][]) + */ + @Override + public List zMScore(byte[] key, byte[][] values) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(values, "Value must not be null!"); + + return connection.invoke().just(BinaryJedis::zmscore, MultiKeyPipelineBase::zmscore, key, values); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisZSetCommands#zRemRange(byte[], long, long) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index edac71ad2..2ca7c62bc 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -1209,6 +1209,9 @@ public class LettuceConnection extends AbstractRedisConnection { COMMAND_OUTPUT_TYPE_MAPPING.put(ZINCRBY, DoubleOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(ZSCORE, DoubleOutput.class); + // DOUBLE LIST + COMMAND_OUTPUT_TYPE_MAPPING.put(ZMSCORE, DoubleListOutput.class); + // MAP COMMAND_OUTPUT_TYPE_MAPPING.put(HGETALL, MapOutput.class); @@ -1281,6 +1284,7 @@ public class LettuceConnection extends AbstractRedisConnection { COMMAND_OUTPUT_TYPE_MAPPING.put(HSET, BooleanOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(HSETNX, BooleanOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(MOVE, BooleanOutput.class); + COMMAND_OUTPUT_TYPE_MAPPING.put(COPY, BooleanOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(MSETNX, BooleanOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(PERSIST, BooleanOutput.class); COMMAND_OUTPUT_TYPE_MAPPING.put(PEXPIRE, BooleanOutput.class); 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 a46f061d0..af24ccf5a 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 @@ -27,11 +27,13 @@ import java.nio.ByteBuffer; import java.util.List; import org.reactivestreams.Publisher; + import org.springframework.data.domain.Sort.Direction; import org.springframework.data.redis.connection.DefaultTuple; import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyScanCommand; +import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.ReactiveZSetCommands; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; @@ -381,6 +383,23 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands { })); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.ReactiveZSetCommands#zMScore(org.reactivestreams.Publisher) + */ + @Override + public Flux> zMScore(Publisher commands) { + + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { + + Assert.notNull(command.getKey(), "Key must not be null!"); + Assert.notNull(command.getValues(), "Values must not be null!"); + + return cmd.zmscore(command.getKey(), command.getValues().toArray(new ByteBuffer[0])) + .map(value -> new MultiValueResponse<>(command, value)); + })); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRemRangeByRank(org.reactivestreams.Publisher) 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 7376860d8..40b9178c9 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 @@ -302,6 +302,19 @@ class LettuceZSetCommands implements RedisZSetCommands { return connection.invoke().just(RedisSortedSetAsyncCommands::zscore, key, value); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisZSetCommands#zMScore(byte[], byte[][]) + */ + @Override + public List zMScore(byte[] key, byte[][] values) { + + Assert.notNull(key, "Key must not be null!"); + Assert.notNull(values, "Value must not be null!"); + + return connection.invoke().just(RedisSortedSetAsyncCommands::zmscore, key, values); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisZSetCommands#zRemRange(byte[], long, long) 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 8ff3df2ee..213aa53cd 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.core; import java.util.Collection; +import java.util.List; import java.util.Set; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; @@ -264,6 +265,17 @@ public interface BoundZSetOperations extends BoundKeyOperations { @Nullable Double score(Object o); + /** + * Get the scores of elements with {@code values} from sorted set with key the bound key. + * + * @param o the values. + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + @Nullable + List score(Object... o); + /** * Remove elements in range between {@code start} and {@code end} from sorted set with the bound key. * 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 f9a4a42be..ede279583 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java @@ -17,6 +17,7 @@ package org.springframework.data.redis.core; import java.util.Collection; +import java.util.List; import java.util.Set; import org.springframework.data.redis.connection.DataType; @@ -249,6 +250,15 @@ class DefaultBoundZSetOperations extends DefaultBoundKeyOperations impl return ops.score(getKey(), o); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.BoundZSetOperations#score(java.lang.Object[]) + */ + @Override + public List score(Object... o) { + return ops.score(getKey(), o); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.BoundZSetOperations#remove(java.lang.Object[]) 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 0995bc96d..b7f03020f 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java @@ -370,6 +370,22 @@ class DefaultReactiveZSetOperations implements ReactiveZSetOperations connection.zScore(rawKey(key), rawValue((V) o))); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.ReactiveZSetOperations#score(java.lang.Object, java.lang.Object) + */ + @Override + @SuppressWarnings("unchecked") + public Mono> score(K key, Object... o) { + + Assert.notNull(key, "Key must not be null!"); + + return createMono(connection -> Flux.fromArray((V[]) o) // + .map(this::rawValue) // + .collectList() // + .flatMap(values -> connection.zMScore(rawKey(key), values))); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.ReactiveZSetOperations#removeRange(java.lang.Object, org.springframework.data.domain.Range) 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 79ae492d8..28e3ee82d 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java @@ -17,6 +17,7 @@ package org.springframework.data.redis.core; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Set; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; @@ -437,6 +438,18 @@ class DefaultZSetOperations extends AbstractOperations implements ZS return execute(connection -> connection.zScore(rawKey, rawValue), true); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.ZSetOperations#score(java.lang.Object, java.lang.Object[]) + */ + @Override + public List score(K key, Object... o) { + + byte[] rawKey = rawKey(key); + byte[][] rawValues = rawValues(o); + return execute(connection -> connection.zMScore(rawKey, rawValues), true); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.ZSetOperations#count(java.lang.Object, double, double) 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 1914da966..a476ffe1c 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java @@ -19,6 +19,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Collection; +import java.util.List; import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; @@ -300,6 +301,17 @@ public interface ReactiveZSetOperations { */ Mono score(K key, Object o); + /** + * Get the scores of elements with {@code values} from sorted set with key {@code key}. + * + * @param key must not be {@literal null}. + * @param o the values. + * @return + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + Mono> score(K key, Object... o); + /** * Remove elements in range between {@code start} and {@code end} from sorted set with {@code key}. * 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 822b8a47a..0c209e41e 100644 --- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java @@ -16,6 +16,7 @@ package org.springframework.data.redis.core; import java.util.Collection; +import java.util.List; import java.util.Set; import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate; @@ -370,6 +371,18 @@ public interface ZSetOperations { @Nullable Double score(K key, Object o); + /** + * Get the scores of elements with {@code values} from sorted set with key {@code key}. + * + * @param key must not be {@literal null}. + * @param o the values. + * @return {@literal null} when used in pipeline / transaction. + * @see Redis Documentation: ZMSCORE + * @since 2.6 + */ + @Nullable + List score(K key, Object... o); + /** * Remove elements in range between {@code start} and {@code end} from sorted set with {@code key}. * 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 cacafc14f..541e90c58 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -2054,6 +2054,16 @@ public abstract class AbstractConnectionIntegrationTests { verifyResults(Arrays.asList(new Object[] { true, true, true, 3d })); } + @Test + @EnabledOnCommand("ZMSCORE") + void testZMScore() { + actual.add(connection.zAdd("myset", 2, "Bob")); + actual.add(connection.zAdd("myset", 1, "James")); + actual.add(connection.zAdd("myset", 3, "Joe")); + actual.add(connection.zMScore("myset", "James", "Joe")); + verifyResults(Arrays.asList(new Object[] { true, true, true, Arrays.asList(1d, 3d) })); + } + @Test void testZUnionStore() { actual.add(connection.zAdd("myset", 2, "Bob")); diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java index 90547fb6b..08a0bd848 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java @@ -670,6 +670,9 @@ public interface ClusterConnectionTests { // DATAREDIS-315 void zScoreShouldRetrieveScoreForValue(); + // GH-2038 + void zMScoreShouldRetrieveScoreForValues(); + // DATAREDIS-315 void zUnionStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java index e7f270765..e8ca76ce9 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java @@ -1330,6 +1330,12 @@ public class DefaultStringRedisConnectionPipelineTests extends DefaultStringRedi super.testZScore(); } + @Test + public void testZMScore() { + doReturn(Collections.singletonList(Arrays.asList(1d, 3d))).when(nativeConnection).closePipeline(); + super.testZMScore(); + } + @Test public void testZUnionStoreAggWeightsBytes() { doReturn(Collections.singletonList(5L)).when(nativeConnection).closePipeline(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java index 2691e66c4..0b5a5d651 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java @@ -1433,6 +1433,13 @@ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRe super.testZScore(); } + @Test + public void testZMScore() { + doReturn(Collections.singletonList(Collections.singletonList(Arrays.asList(1d, 3d)))).when(nativeConnection) + .closePipeline(); + super.testZMScore(); + } + @Test public void testZUnionStoreAggWeightsBytes() { doReturn(Collections.singletonList(Collections.singletonList(5L))).when(nativeConnection).closePipeline(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index fe09f3610..b4ddc021e 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -1624,6 +1624,13 @@ public class DefaultStringRedisConnectionTests { verifyResults(Collections.singletonList(3d)); } + @Test + public void testZMScore() { + doReturn(Arrays.asList(1d, 3d)).when(nativeConnection).zMScore(fooBytes, barBytes, bar2Bytes); + actual.add(connection.zMScore(foo, bar, bar2)); + verifyResults(Collections.singletonList(Arrays.asList(1d, 3d))); + } + @Test public void testZUnionStoreAggWeightsBytes() { doReturn(5L).when(nativeConnection).zUnionStore(eq(fooBytes), eq(Aggregate.MAX), any(Weights.class), eq(fooBytes)); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java index c846f0bb2..5c8e8519b 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java @@ -1316,6 +1316,12 @@ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConne super.testZScore(); } + @Test + public void testZMScore() { + doReturn(Collections.singletonList(Arrays.asList(1d, 3d))).when(nativeConnection).exec(); + super.testZMScore(); + } + @Test public void testZUnionStoreAggWeightsBytes() { doReturn(Collections.singletonList(5L)).when(nativeConnection).exec(); diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java index 019f0f491..7f787b794 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java @@ -833,6 +833,10 @@ class RedisConnectionUnitTests { return delegate.zScore(key, value); } + public List zMScore(byte[] key, byte[][] values) { + return delegate.zMScore(key, values); + } + public Long zRemRange(byte[] key, long begin, long end) { return delegate.zRemRange(key, begin, end); } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 1ad156d01..f4d625eb3 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -2304,6 +2304,16 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.zScore(KEY_1_BYTES, VALUE_2_BYTES)).isEqualTo(20D); } + @Test // GH-2038 + @EnabledOnCommand("ZMSCORE") + public void zMScoreShouldRetrieveScoreForValues() { + + nativeConnection.zadd(KEY_1_BYTES, 10D, VALUE_1_BYTES); + nativeConnection.zadd(KEY_1_BYTES, 20D, VALUE_2_BYTES); + + assertThat(clusterConnection.zMScore(KEY_1_BYTES, VALUE_1_BYTES, VALUE_2_BYTES)).containsSequence(10D, 20D); + } + @Test // DATAREDIS-315 public void zUnionStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots() { assertThatExceptionOfType(DataAccessException.class) diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index e628e234e..103b835a6 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -2345,6 +2345,16 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.zScore(KEY_1_BYTES, VALUE_2_BYTES)).isEqualTo(20D); } + @Test // GH-2038 + @EnabledOnCommand("ZMSCORE") + public void zMScoreShouldRetrieveScoreForValues() { + + nativeConnection.zadd(KEY_1, 10D, VALUE_1); + nativeConnection.zadd(KEY_1, 20D, VALUE_2); + + assertThat(clusterConnection.zMScore(KEY_1_BYTES, VALUE_1_BYTES, VALUE_2_BYTES)).containsExactly(10D, 20D); + } + @Test // DATAREDIS-315 public void zUnionStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots() { assertThatExceptionOfType(DataAccessException.class) diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java index 0e4cb2de4..3ef98d713 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommandsIntegrationTests.java @@ -27,6 +27,7 @@ import java.util.Arrays; import org.springframework.data.domain.Range; import org.springframework.data.redis.connection.DefaultTuple; import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; /** @@ -402,6 +403,17 @@ public class LettuceReactiveZSetCommandsIntegrationTests extends LettuceReactive assertThat(connection.zSetCommands().zScore(KEY_1_BBUFFER, VALUE_2_BBUFFER).block()).isEqualTo(2D); } + @ParameterizedRedisTest // GH-2038 + @EnabledOnCommand("ZMSCORE") + void zMScoreShouldReturnScoreCorrectly() { + + nativeCommands.zadd(KEY_1, 1D, VALUE_1); + nativeCommands.zadd(KEY_1, 2D, VALUE_2); + + connection.zSetCommands().zMScore(KEY_1_BBUFFER, Arrays.asList(VALUE_1_BBUFFER, VALUE_2_BBUFFER)) + .as(StepVerifier::create).expectNext(Arrays.asList(1D, 2D)).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-525 void zRemRangeByRankShouldRemoveValuesCorrectly() { 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 2950a54d0..34ffea7f7 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveZSetOperationsIntegrationTests.java @@ -38,6 +38,7 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Weights; import org.springframework.data.redis.core.ReactiveOperationsTestParams.Fixture; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.parametrized.MethodSource; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; @@ -460,6 +461,21 @@ public class DefaultReactiveZSetOperationsIntegrationTests { zSetOperations.score(key, value2).as(StepVerifier::create).expectNext(10d).verifyComplete(); } + @ParameterizedRedisTest // GH-2038 + @EnabledOnCommand("ZMSCORE") + void scores() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + + zSetOperations.add(key, value1, 42.1).as(StepVerifier::create).expectNext(true).verifyComplete(); + zSetOperations.add(key, value2, 10).as(StepVerifier::create).expectNext(true).verifyComplete(); + + zSetOperations.score(key, value1, value2, valueFactory.instance()).as(StepVerifier::create) + .expectNext(Arrays.asList(42.1d, 10d, null)).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-602 void removeRange() { diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java index aa1860e00..5aa157eaa 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java @@ -339,6 +339,21 @@ public class DefaultZSetOperationsIntegrationTests { assertThat(zSetOps.range(key, 0, -1)).containsOnly(value2); } + @ParameterizedRedisTest + void testScore() { + + K key = keyFactory.instance(); + V value1 = valueFactory.instance(); + V value2 = valueFactory.instance(); + V value3 = valueFactory.instance(); + + zSetOps.add(key, value1, 1.9); + zSetOps.add(key, value2, 3.7); + zSetOps.add(key, value3, 5.8); + + assertThat(zSetOps.score(key, value1, value2, valueFactory.instance())).containsExactly(1.9d, 3.7d, null); + } + @ParameterizedRedisTest void zCardRetrievesDataCorrectly() {