diff --git a/pom.xml b/pom.xml
index 8657b1992a..563737e979 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-redis
- 2.6.0-SNAPSHOT
+ 2.6.0-2049-SNAPSHOT
Spring Data Redis
diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc
index eab00c043a..b117424c9b 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`, `HRANDFIELD`, `ZRANDMEMBER`).
[[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 a6348abafd..f6f081bbd7 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -23,6 +23,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
@@ -83,6 +84,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
private Converter stringToBytes = new SerializingConverter();
private SetConverter tupleToStringTuple = new SetConverter<>(new TupleConverter());
private SetConverter stringTupleToTuple = new SetConverter<>(new StringTupleConverter());
+ private ListConverter tupleListToStringTuple = new ListConverter<>(new TupleConverter());
private ListConverter byteListToStringList = new ListConverter<>(bytesToString);
private MapConverter byteMapToStringMap = new MapConverter<>(bytesToString);
private MapConverter stringMapToByteMap = new MapConverter<>(stringToBytes);
@@ -104,6 +106,26 @@ public StringRecord convert(ByteRecord source) {
@SuppressWarnings("rawtypes") private Queue txConverters = new LinkedList<>();
private boolean deserializePipelineAndTxResults = false;
+ private Entry convertEntry(Entry source) {
+ return new Entry() {
+
+ @Override
+ public String getKey() {
+ return bytesToString.convert(source.getKey());
+ }
+
+ @Override
+ public String getValue() {
+ return bytesToString.convert(source.getValue());
+ }
+
+ @Override
+ public String setValue(String value) {
+ throw new UnsupportedOperationException("Cannot set value for entry");
+ }
+ };
+ }
+
private class DeserializingConverter implements Converter {
public String convert(byte[] source) {
return serializer.deserialize(source);
@@ -277,7 +299,6 @@ public Long decrBy(byte[] key, long value) {
return convertAndReturn(delegate.decrBy(key, value), Converters.identityConverter());
}
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -1530,7 +1551,8 @@ public Set zRangeByScore(byte[] key, double min, double max) {
*/
@Override
public Set zRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count) {
- return convertAndReturn(delegate.zRangeByScoreWithScores(key, min, max, offset, count), Converters.identityConverter());
+ return convertAndReturn(delegate.zRangeByScoreWithScores(key, min, max, offset, count),
+ Converters.identityConverter());
}
/*
@@ -1602,7 +1624,8 @@ public Set zRevRangeByScore(byte[] key, Range range, Limit limit) {
*/
@Override
public Set zRevRangeByScoreWithScores(byte[] key, double min, double max, long offset, long count) {
- return convertAndReturn(delegate.zRevRangeByScoreWithScores(key, min, max, offset, count), Converters.identityConverter());
+ return convertAndReturn(delegate.zRevRangeByScoreWithScores(key, min, max, offset, count),
+ Converters.identityConverter());
}
/*
@@ -1853,7 +1876,8 @@ public T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... k
*/
@Override
public T evalSha(String scriptSha1, ReturnType returnType, int numKeys, byte[]... keysAndArgs) {
- return convertAndReturn(delegate.evalSha(scriptSha1, returnType, numKeys, keysAndArgs), Converters.identityConverter());
+ return convertAndReturn(delegate.evalSha(scriptSha1, returnType, numKeys, keysAndArgs),
+ Converters.identityConverter());
}
/*
@@ -1862,7 +1886,8 @@ public T evalSha(String scriptSha1, ReturnType returnType, int numKeys, byte
*/
@Override
public T evalSha(byte[] scriptSha1, ReturnType returnType, int numKeys, byte[]... keysAndArgs) {
- return convertAndReturn(delegate.evalSha(scriptSha1, returnType, numKeys, keysAndArgs), Converters.identityConverter());
+ return convertAndReturn(delegate.evalSha(scriptSha1, returnType, numKeys, keysAndArgs),
+ Converters.identityConverter());
}
//
@@ -1949,6 +1974,7 @@ public String bRPopLPush(int timeout, String srcKey, String dstKey) {
public Boolean copy(String sourceKey, String targetKey, boolean replace) {
return copy(serialize(sourceKey), serialize(targetKey), replace);
}
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#decr(java.lang.String)
@@ -1976,7 +2002,6 @@ public Long del(String... keys) {
return del(serializeMulti(keys));
}
-
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[])
@@ -2112,6 +2137,88 @@ public Double hIncrBy(String key, String field, double delta) {
return hIncrBy(serialize(key), serialize(field), delta);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[])
+ */
+ @Nullable
+ @Override
+ public byte[] hRandField(byte[] key) {
+ return convertAndReturn(delegate.hRandField(key), Converters.identityConverter());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[])
+ */
+ @Nullable
+ @Override
+ public Entry hRandFieldWithValues(byte[] key) {
+ return convertAndReturn(delegate.hRandFieldWithValues(key), Converters.identityConverter());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List hRandField(byte[] key, long count) {
+ return convertAndReturn(delegate.hRandField(key, count), Converters.identityConverter());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List> hRandFieldWithValues(byte[] key, long count) {
+ return convertAndReturn(delegate.hRandFieldWithValues(key, count), Converters.identityConverter());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#hRandField(java.lang.String)
+ */
+ @Nullable
+ @Override
+ public String hRandField(String key) {
+ return convertAndReturn(delegate.hRandField(serialize(key)), bytesToString);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#hRandFieldWithValues(java.lang.String)
+ */
+ @Nullable
+ @Override
+ public Entry hRandFieldWithValues(String key) {
+ return convertAndReturn(delegate.hRandFieldWithValues(serialize(key)),
+ (Converter, Entry>) this::convertEntry);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#hRandField(java.lang.String, long)
+ */
+ @Nullable
+ @Override
+ public List hRandField(String key, long count) {
+ return convertAndReturn(delegate.hRandField(serialize(key), count), byteListToStringList);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#hRandFieldWithValues(java.lang.String, long)
+ */
+ @Nullable
+ @Override
+ public List> hRandFieldWithValues(String key, long count) {
+ return convertAndReturn(delegate.hRandFieldWithValues(serialize(key), count),
+ new ListConverter<>(this::convertEntry));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#hKeys(java.lang.String)
@@ -2879,6 +2986,78 @@ public Long zInterStore(String destKey, String... sets) {
return zInterStore(serialize(destKey), serializeMulti(sets));
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[])
+ */
+ @Override
+ public byte[] zRandMember(byte[] key) {
+ return delegate.zRandMember(key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[], long)
+ */
+ @Override
+ public List zRandMember(byte[] key, long count) {
+ return delegate.zRandMember(key, count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[])
+ */
+ @Override
+ public Tuple zRandMemberWithScore(byte[] key) {
+ return delegate.zRandMemberWithScore(key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[], long)
+ */
+ @Override
+ public List zRandMemberWithScore(byte[] key, long count) {
+ return delegate.zRandMemberWithScore(key, count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#zRandMember(java.lang.String)
+ */
+ @Override
+ public String zRandMember(String key) {
+ return convertAndReturn(delegate.zRandMember(serialize(key)), bytesToString);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#zRandMember(java.lang.String, long)
+ */
+ @Override
+ public List zRandMember(String key, long count) {
+ return convertAndReturn(delegate.zRandMember(serialize(key), count), byteListToStringList);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#zRandMemberWithScore(java.lang.String)
+ */
+ @Override
+ public StringTuple zRandMemberWithScore(String key) {
+ return convertAndReturn(delegate.zRandMemberWithScore(serialize(key)), new TupleConverter());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.StringRedisConnection#zRandMemberWithScores(java.lang.String, long)
+ */
+ @Override
+ public List zRandMemberWithScores(String key, long count) {
+ return convertAndReturn(delegate.zRandMemberWithScore(serialize(key), count), tupleListToStringTuple);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#zRange(java.lang.String, long, long)
@@ -3600,24 +3779,7 @@ public String getClientName() {
@Override
public Cursor> hScan(String key, ScanOptions options) {
- return new ConvertingCursor<>(this.delegate.hScan(this.serialize(key), options),
- source -> new Entry() {
-
- @Override
- public String getKey() {
- return bytesToString.convert(source.getKey());
- }
-
- @Override
- public String getValue() {
- return bytesToString.convert(source.getValue());
- }
-
- @Override
- public String setValue(String value) {
- throw new UnsupportedOperationException("Cannot set value for entry in cursor");
- }
- });
+ return new ConvertingCursor<>(this.delegate.hScan(this.serialize(key), options), this::convertEntry);
}
/*
@@ -3860,7 +4022,8 @@ public RecordId xAdd(StringRecord record, XAddOptions options) {
*/
@Override
public List xClaimJustId(String key, String group, String consumer, XClaimOptions options) {
- return convertAndReturn(delegate.xClaimJustId(serialize(key), group, consumer, options), Converters.identityConverter());
+ return convertAndReturn(delegate.xClaimJustId(serialize(key), group, consumer, options),
+ Converters.identityConverter());
}
/*
@@ -3897,7 +4060,8 @@ public String xGroupCreate(String key, ReadOffset readOffset, String group) {
*/
@Override
public String xGroupCreate(String key, ReadOffset readOffset, String group, boolean mkStream) {
- return convertAndReturn(delegate.xGroupCreate(serialize(key), group, readOffset, mkStream), Converters.identityConverter());
+ return convertAndReturn(delegate.xGroupCreate(serialize(key), group, readOffset, mkStream),
+ Converters.identityConverter());
}
/*
@@ -3970,7 +4134,8 @@ public PendingMessagesSummary xPending(String key, String groupName) {
@Override
public PendingMessages xPending(String key, String groupName, String consumer,
org.springframework.data.domain.Range range, Long count) {
- return convertAndReturn(delegate.xPending(serialize(key), groupName, consumer, range, count), Converters.identityConverter());
+ return convertAndReturn(delegate.xPending(serialize(key), groupName, consumer, range, count),
+ Converters.identityConverter());
}
/*
@@ -4267,7 +4432,8 @@ private T convertAndReturn(@Nullable Object value, Converter converter) {
}
return value == null ? null
- : ObjectUtils.nullSafeEquals(converter, Converters.identityConverter()) ? (T) value : (T) converter.convert(value);
+ : ObjectUtils.nullSafeEquals(converter, Converters.identityConverter()) ? (T) value
+ : (T) converter.convert(value);
}
private void addResultConverter(Converter, ?> converter) {
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 fad7b59af8..959487a8d2 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -977,6 +977,34 @@ default Long zInterStore(byte[] destKey, byte[]... sets) {
return zSetCommands().zInterStore(destKey, sets);
}
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default byte[] zRandMember(byte[] key) {
+ return zSetCommands().zRandMember(key);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default List zRandMember(byte[] key, long count) {
+ return zSetCommands().zRandMember(key, count);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default Tuple zRandMemberWithScore(byte[] key) {
+ return zSetCommands().zRandMemberWithScore(key);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default List zRandMemberWithScore(byte[] key, long count) {
+ return zSetCommands().zRandMemberWithScore(key, count);
+ }
+
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
@Override
@Deprecated
@@ -1203,6 +1231,34 @@ default Long hIncrBy(byte[] key, byte[] field, long delta) {
return hashCommands().hIncrBy(key, field, delta);
}
+ /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
+ @Override
+ @Deprecated
+ default byte[] hRandField(byte[] key) {
+ return hashCommands().hRandField(key);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
+ @Override
+ @Deprecated
+ default Entry hRandFieldWithValues(byte[] key) {
+ return hashCommands().hRandFieldWithValues(key);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
+ @Override
+ @Deprecated
+ default List hRandField(byte[] key, long count) {
+ return hashCommands().hRandField(key, count);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
+ @Override
+ @Deprecated
+ default List> hRandFieldWithValues(byte[] key, long count) {
+ return hashCommands().hRandFieldWithValues(key, count);
+ }
+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
@Override
@Deprecated
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
index 5e53d0d629..7656340178 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java
@@ -512,6 +512,145 @@ default Mono hLen(ByteBuffer key) {
*/
Flux> hLen(Publisher commands);
+ /**
+ * {@literal HRANDFIELD} {@link Command}.
+ *
+ * @author Mark Paluch
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ class HRandFieldCommand extends KeyCommand {
+
+ private long count;
+
+ private HRandFieldCommand(@Nullable ByteBuffer key, long count) {
+
+ super(key);
+
+ this.count = count;
+ }
+
+ /**
+ * Applies the hash {@literal key}. Constructs a new command instance with all previously configured properties.
+ *
+ * @param key must not be {@literal null}.
+ * @return a new {@link HRandFieldCommand} with {@literal key} applied.
+ */
+ public static HRandFieldCommand key(ByteBuffer key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return new HRandFieldCommand(key, 1);
+ }
+
+ /**
+ * Applies the {@literal count}. Constructs a new command instance with all previously configured properties. If the
+ * provided {@code count} argument is positive, return a list of distinct fields, capped either at {@code count} or
+ * the hash size. If {@code count} is negative, the behavior changes and the command is allowed to return the same
+ * field multiple times. In this case, the number of returned fields is the absolute value of the specified count.
+ *
+ * @param count
+ * @return a new {@link HRandFieldCommand} with {@literal key} applied.
+ */
+ public HRandFieldCommand count(long count) {
+ return new HRandFieldCommand(getKey(), count);
+ }
+
+ public long getCount() {
+ return count;
+ }
+ }
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ default Mono hRandField(ByteBuffer key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return hRandField(Mono.just(HRandFieldCommand.key(key).count(1))).flatMap(CommandResponse::getOutput).next();
+ }
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ default Mono> hRandFieldWithValues(ByteBuffer key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return hRandFieldWithValues(Mono.just(HRandFieldCommand.key(key).count(1))).flatMap(CommandResponse::getOutput)
+ .next();
+ }
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ default Flux hRandField(ByteBuffer key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return hRandField(Mono.just(HRandFieldCommand.key(key).count(count))).flatMap(CommandResponse::getOutput);
+ }
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ default Flux> hRandFieldWithValues(ByteBuffer key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return hRandFieldWithValues(Mono.just(HRandFieldCommand.key(key).count(count))).flatMap(CommandResponse::getOutput);
+ }
+
+ /**
+ * Get random fields of hash at {@literal key}.
+ *
+ * @param commands must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Flux>> hRandField(Publisher commands);
+
+ /**
+ * Get random fields along their values of hash at {@literal key}.
+ *
+ * @param commands must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Flux>>> hRandFieldWithValues(
+ Publisher commands);
+
/**
* Get key set (fields) of hash at {@literal key}.
*
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 ed307120c2..499e039719 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java
@@ -479,6 +479,142 @@ default Mono zIncrBy(ByteBuffer key, Number increment, ByteBuffer value)
*/
Flux> zIncrBy(Publisher commands);
+ /**
+ * {@code ZRANDMEMBER} command parameters.
+ *
+ * @author Mark Paluch
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ class ZRandMemberCommand extends KeyCommand {
+
+ private final long count;
+
+ private ZRandMemberCommand(@Nullable ByteBuffer key, long count) {
+
+ super(key);
+ this.count = count;
+ }
+
+ /**
+ * Creates a new {@link ZRandMemberCommand} given the number of values to retrieve.
+ *
+ * @param nrValuesToRetrieve
+ * @return a new {@link ZRandMemberCommand} for a number of values to retrieve.
+ */
+ public static ZRandMemberCommand valueCount(long nrValuesToRetrieve) {
+ return new ZRandMemberCommand(null, nrValuesToRetrieve);
+ }
+
+ /**
+ * Creates a new {@link ZRandMemberCommand} to retrieve one random member.
+ *
+ * @return a new {@link ZRandMemberCommand} to retrieve one random member.
+ */
+ public static ZRandMemberCommand singleValue() {
+ return new ZRandMemberCommand(null, 1);
+ }
+
+ /**
+ * 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 ZRandMemberCommand} with {@literal key} applied.
+ */
+ public ZRandMemberCommand from(ByteBuffer key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return new ZRandMemberCommand(key, count);
+ }
+
+ /**
+ * @return
+ */
+ public long getCount() {
+ return count;
+ }
+ }
+
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ default Mono zRandMember(ByteBuffer key) {
+ return zRandMember(Mono.just(ZRandMemberCommand.singleValue().from(key))).flatMap(CommandResponse::getOutput)
+ .next();
+ }
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ default Flux zRandMember(ByteBuffer key, long count) {
+ return zRandMember(Mono.just(ZRandMemberCommand.valueCount(count).from(key))).flatMap(CommandResponse::getOutput);
+ }
+
+ /**
+ * Get random elements from sorted set at {@code key}.
+ *
+ * @param commands must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux>> zRandMember(Publisher commands);
+
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ default Mono zRandMemberWithScore(ByteBuffer key) {
+ return zRandMemberWithScore(Mono.just(ZRandMemberCommand.singleValue().from(key)))
+ .flatMap(CommandResponse::getOutput).next();
+ }
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ default Flux zRandMemberWithScore(ByteBuffer key, long count) {
+ return zRandMemberWithScore(Mono.just(ZRandMemberCommand.valueCount(count).from(key)))
+ .flatMap(CommandResponse::getOutput);
+ }
+
+ /**
+ * Get random elements from sorted set at {@code key}.
+ *
+ * @param commands must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux>> zRandMemberWithScore(Publisher commands);
+
/**
* {@code ZRANK}/{@literal ZREVRANK} command parameters.
*
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
index f55151622a..bf083b9785 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java
@@ -173,6 +173,58 @@ public interface RedisHashCommands {
@Nullable
Map hGetAll(byte[] key);
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ byte[] hRandField(byte[] key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map.Entry hRandFieldWithValues(byte[] key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List hRandField(byte[] key, long count);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List> hRandFieldWithValues(byte[] key, long count);
+
/**
* Use a {@link Cursor} to iterate over entries in hash at {@code key}.
*
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 fa8f5ef741..2e9c58d37d 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
@@ -633,6 +633,58 @@ default Long zAdd(byte[] key, Set tuples) {
@Nullable
Double zIncrBy(byte[] key, double increment, byte[] value);
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return can be {@literal null}.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ byte[] zRandMember(byte[] key);
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List zRandMember(byte[] key, long count);
+
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return can be {@literal null}.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ Tuple zRandMemberWithScore(byte[] key);
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List zRandMemberWithScore(byte[] key, long count);
+
/**
* Determine the index of element with {@code value} in a sorted set.
*
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 d828fbe692..3c711aa120 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -1229,6 +1229,58 @@ default Long lPos(String key, String element) {
*/
Double zIncrBy(String key, double increment, String value);
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return can be {@literal null}.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ String zRandMember(String key);
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List zRandMember(String key, long count);
+
+ /**
+ * Get random element from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return can be {@literal null}.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ StringTuple zRandMemberWithScore(String key);
+
+ /**
+ * Get {@code count} random elements from sorted set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count if the provided {@code count} argument is positive, return a list of distinct fields, capped either at
+ * {@code count} or the set size. If {@code count} is negative, the behavior changes and the command is
+ * allowed to return the same value multiple times. In this case, the number of returned values is the
+ * absolute value of the specified count.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List zRandMemberWithScores(String key, long count);
+
/**
* Determine the index of element with {@code value} in a sorted set.
*
@@ -1742,6 +1794,58 @@ default Set zRevRangeByLex(String key, Range range) {
*/
Double hIncrBy(String key, String field, double delta);
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ String hRandField(String key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map.Entry hRandFieldWithValues(String key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List hRandField(String key, long count);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List> hRandFieldWithValues(String key, long count);
+
/**
* Determine if given hash {@code field} exists.
*
diff --git a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java
index ab679f81a6..80fc7dbe1f 100644
--- a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java
+++ b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java
@@ -461,6 +461,20 @@ public static Object parse(Object source, String sourcePath, Map
+ * @param
+ * @return
+ * @since 2.6
+ */
+ public static Map.Entry entryOf(K key, V value) {
+ return new SimpleEntry<>(key, value);
+ }
+
/**
* @author Christoph Strobl
* @since 1.8
@@ -639,4 +653,30 @@ private SlotRange parseSlotRange(String[] args) {
}
}
+
+ private static class SimpleEntry implements Map.Entry {
+
+ private final K key;
+ private final V value;
+
+ public SimpleEntry(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
index 8149dad35f..43ffee1cd2 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
@@ -30,6 +30,7 @@
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -167,6 +168,74 @@ public Double hIncrBy(byte[] key, byte[] field, double delta) {
}
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[])
+ */
+ @Nullable
+ @Override
+ public byte[] hRandField(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ return connection.getCluster().hrandfield(key);
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[])
+ */
+ @Nullable
+ @Override
+ public Entry hRandFieldWithValues(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ Map map = connection.getCluster().hrandfieldWithValues(key, 1);
+ return map.isEmpty() ? null : map.entrySet().iterator().next();
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List hRandField(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ return connection.getCluster().hrandfield(key, count);
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List> hRandFieldWithValues(byte[] key, long count) {
+
+ try {
+ Map map = connection.getCluster().hrandfieldWithValues(key, count);
+ return Streamable.of(() -> map.entrySet().iterator()).toList();
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisHashCommands#hExists(byte[], byte[])
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 53df378987..913d7e0b2c 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,7 +18,10 @@
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ZParams;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
@@ -60,7 +63,8 @@ public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
Assert.notNull(value, "Value must not be null!");
try {
- return JedisConverters.toBoolean(connection.getCluster().zadd(key, score, value, JedisConverters.toZAddParams(args)));
+ return JedisConverters
+ .toBoolean(connection.getCluster().zadd(key, score, value, JedisConverters.toZAddParams(args)));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -119,6 +123,74 @@ public Double zIncrBy(byte[] key, double increment, byte[] value) {
}
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[])
+ */
+ @Override
+ public byte[] zRandMember(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ return connection.getCluster().zrandmember(key);
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[], long)
+ */
+ @Override
+ public List zRandMember(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ return new ArrayList<>(connection.getCluster().zrandmember(key, count));
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[])
+ */
+ @Override
+ public Tuple zRandMemberWithScore(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ Set tuples = connection.getCluster().zrandmemberWithScores(key, 1);
+
+ return tuples.isEmpty() ? null : JedisConverters.toTuple(tuples.iterator().next());
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[], long)
+ */
+ @Override
+ public List zRandMemberWithScore(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ try {
+ Set tuples = connection.getCluster().zrandmemberWithScores(key, count);
+
+ return tuples.stream().map(JedisConverters::toTuple).collect(Collectors.toList());
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zRank(byte[], byte[])
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
index 57729efbc5..9adb2486db 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java
@@ -20,12 +20,14 @@
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.data.redis.connection.RedisHashCommands;
+import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.KeyBoundCursor;
import org.springframework.data.redis.core.ScanIteration;
@@ -127,6 +129,67 @@ public Map hGetAll(byte[] key) {
return connection.invoke().just(BinaryJedis::hgetAll, MultiKeyPipelineBase::hgetAll, key);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[])
+ */
+ @Nullable
+ @Override
+ public byte[] hRandField(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(BinaryJedis::hrandfield, MultiKeyPipelineBase::hrandfield, key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[])
+ */
+ @Nullable
+ @Override
+ public Entry hRandFieldWithValues(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke()
+ .from(BinaryJedis::hrandfieldWithValues, MultiKeyPipelineBase::hrandfieldWithValues, key, 1L)
+ .get(it -> it.isEmpty() ? null : it.entrySet().iterator().next());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List hRandField(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(BinaryJedis::hrandfield, MultiKeyPipelineBase::hrandfield, key, count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List> hRandFieldWithValues(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke()
+ .from(BinaryJedis::hrandfieldWithValues, MultiKeyPipelineBase::hrandfieldWithValues, key, count).get(it -> {
+
+ List> entries = new ArrayList<>(it.size());
+ it.forEach((k, v) -> entries.add(Converters.entryOf(k, v)));
+
+ return entries;
+ });
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisHashCommands#hIncrBy(byte[], byte[], long)
@@ -280,4 +343,5 @@ private boolean isPipelined() {
private boolean isQueueing() {
return connection.isQueueing();
}
+
}
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 2f6b4f67e7..1a1def243e 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.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;
@@ -60,7 +59,8 @@ public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
- return connection.invoke().from(BinaryJedis::zadd, MultiKeyPipelineBase::zadd, key, score, value, JedisConverters.toZAddParams(args))
+ return connection.invoke()
+ .from(BinaryJedis::zadd, MultiKeyPipelineBase::zadd, key, score, value, JedisConverters.toZAddParams(args))
.get(JedisConverters::toBoolean);
}
@@ -105,6 +105,65 @@ public Double zIncrBy(byte[] key, double increment, byte[] value) {
return connection.invoke().just(BinaryJedis::zincrby, MultiKeyPipelineBase::zincrby, key, increment, value);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[])
+ */
+ @Override
+ public byte[] zRandMember(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(BinaryJedis::zrandmember, MultiKeyPipelineBase::zrandmember, key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[], long)
+ */
+ @Override
+ public List zRandMember(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().fromMany(BinaryJedis::zrandmember, MultiKeyPipelineBase::zrandmember, key, count)
+ .toList();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[])
+ */
+ @Override
+ public Tuple zRandMemberWithScore(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke()
+ .from(BinaryJedis::zrandmemberWithScores, MultiKeyPipelineBase::zrandmemberWithScores, key, 1L).get(it -> {
+
+ if (it.isEmpty()) {
+ return null;
+ }
+
+ return JedisConverters.toTuple(it.iterator().next());
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[], long)
+ */
+ @Override
+ public List zRandMemberWithScore(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke()
+ .fromMany(BinaryJedis::zrandmemberWithScores, MultiKeyPipelineBase::zrandmemberWithScores, key, count)
+ .toList(JedisConverters::toTuple);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zRank(byte[], byte[])
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
index dd210fad74..aa8172a0dd 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
+import io.lettuce.core.KeyValue;
import io.lettuce.core.MapScanCursor;
import io.lettuce.core.ScanArgs;
import io.lettuce.core.api.async.RedisHashAsyncCommands;
@@ -25,6 +26,7 @@
import java.util.Set;
import org.springframework.data.redis.connection.RedisHashCommands;
+import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.KeyBoundCursor;
import org.springframework.data.redis.core.ScanIteration;
@@ -124,6 +126,60 @@ public Map hGetAll(byte[] key) {
return connection.invoke().just(RedisHashAsyncCommands::hgetall, key);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[])
+ */
+ @Nullable
+ @Override
+ public byte[] hRandField(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(RedisHashAsyncCommands::hrandfield, key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[])
+ */
+ @Nullable
+ @Override
+ public Entry hRandFieldWithValues(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().from(RedisHashAsyncCommands::hrandfieldWithvalues, key)
+ .get(LettuceHashCommands::toEntry);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List hRandField(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(RedisHashAsyncCommands::hrandfield, key, count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[], long)
+ */
+ @Nullable
+ @Override
+ public List> hRandFieldWithValues(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().fromMany(RedisHashAsyncCommands::hrandfieldWithvalues, key, count)
+ .toList(LettuceHashCommands::toEntry);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisHashCommands#hIncrBy(byte[], byte[], long)
@@ -275,4 +331,9 @@ public Long hStrLen(byte[] key, byte[] field) {
return connection.invoke().just(RedisHashAsyncCommands::hstrlen, key, field);
}
+ @Nullable
+ private static Entry toEntry(KeyValue value) {
+ return value.hasValue() ? Converters.entryOf(value.getKey(), value.getValue()) : null;
+ }
+
}
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
index 7c7a58f602..b1103de1e2 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveHashCommands.java
@@ -36,6 +36,7 @@
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.convert.Converters;
import org.springframework.util.Assert;
/**
@@ -164,6 +165,39 @@ public Flux> hLen(Publisher comman
}));
}
+ @Override
+ public Flux>> hRandField(Publisher commands) {
+
+ return connection.execute(cmd -> Flux.from(commands).map(command -> {
+
+ Assert.notNull(command.getKey(), "Command.getKey() must not be null!");
+
+ return new CommandResponse<>(command, cmd.hrandfield(command.getKey(), command.getCount()));
+ }));
+ }
+
+ @Override
+ public Flux>>> hRandFieldWithValues(
+ Publisher commands) {
+
+ return connection.execute(cmd -> Flux.from(commands).map(command -> {
+
+ Assert.notNull(command.getKey(), "Command.getKey() must not be null!");
+
+ Flux> flux = cmd.hrandfieldWithvalues(command.getKey(), command.getCount())
+ .handle((it, sink) -> {
+
+ if (it.isEmpty()) {
+ return;
+ }
+
+ sink.next(Converters.entryOf(it.getKey(), it.getValue()));
+ });
+
+ return new CommandResponse<>(command, flux);
+ }));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hKeys(org.reactivestreams.Publisher)
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 a46f061d07..b11e170302 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
@@ -156,6 +156,39 @@ public Flux> zIncrBy(Publisher>> zRandMember(
+ Publisher commands) {
+
+ return connection.execute(cmd -> Flux.from(commands).map(command -> {
+
+ Assert.notNull(command.getKey(), "Key must not be null!");
+
+ return new CommandResponse<>(command, cmd.zrandmember(command.getKey(), command.getCount()));
+ }));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRandMemberWithScore(Publisher)
+ */
+ @Override
+ public Flux>> zRandMemberWithScore(
+ Publisher commands) {
+
+ return connection.execute(cmd -> Flux.from(commands).map(command -> {
+
+ Assert.notNull(command.getKey(), "Key must not be null!");
+
+ return new CommandResponse<>(command, cmd.zrandmemberWithScores(command.getKey(), command.getCount())
+ .map(sc -> new DefaultTuple(getBytes(sc), sc.getScore())));
+ }));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRank(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 7376860d81..f7dc313523 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
@@ -105,6 +105,56 @@ public Double zIncrBy(byte[] key, double increment, byte[] value) {
return connection.invoke().just(RedisSortedSetAsyncCommands::zincrby, key, increment, value);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[])
+ */
+ @Override
+ public byte[] zRandMember(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(RedisSortedSetAsyncCommands::zrandmember, key);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMember(byte[], long)
+ */
+ @Override
+ public List zRandMember(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().just(RedisSortedSetAsyncCommands::zrandmember, key, count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[])
+ */
+ @Override
+ public Tuple zRandMemberWithScore(byte[] key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().from(RedisSortedSetAsyncCommands::zrandmemberWithScores, key)
+ .get(LettuceConverters::toTuple);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisZSetCommands#zRandMemberWithScore(byte[], long)
+ */
+ @Override
+ public List zRandMemberWithScore(byte[] key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return connection.invoke().fromMany(RedisSortedSetAsyncCommands::zrandmemberWithScores, key, count)
+ .toList(LettuceConverters::toTuple);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zRank(byte[], byte[])
diff --git a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java
index 53048d9b4a..40f85e559d 100644
--- a/src/main/java/org/springframework/data/redis/core/AbstractOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/AbstractOperations.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.core;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -223,7 +224,7 @@ Set deserializeValues(Set rawValues) {
return SerializationUtils.deserialize(rawValues, valueSerializer());
}
- Set> deserializeTupleValues(Collection rawValues) {
+ Set> deserializeTupleValues(Set rawValues) {
if (rawValues == null) {
return null;
}
@@ -234,6 +235,17 @@ Set> deserializeTupleValues(Collection rawValues) {
return set;
}
+ List> deserializeTupleValues(List rawValues) {
+ if (rawValues == null) {
+ return null;
+ }
+ List> set = new ArrayList<>(rawValues.size());
+ for (Tuple rawValue : rawValues) {
+ set.add(deserializeTuple(rawValue));
+ }
+ return set;
+ }
+
@SuppressWarnings({ "unchecked", "rawtypes" })
TypedTuple deserializeTuple(Tuple tuple) {
Object value = tuple.getValue();
@@ -277,6 +289,14 @@ Set deserializeHashKeys(Set rawKeys) {
return SerializationUtils.deserialize(rawKeys, hashKeySerializer());
}
+ @SuppressWarnings("unchecked")
+ List deserializeHashKeys(List rawKeys) {
+ if (hashKeySerializer() == null) {
+ return (List) rawKeys;
+ }
+ return SerializationUtils.deserialize(rawKeys, hashKeySerializer());
+ }
+
@SuppressWarnings("unchecked")
List deserializeHashValues(List rawValues) {
if (hashValueSerializer() == null) {
diff --git a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
index ca7483dbcd..47ce81ca52 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundHashOperations.java
@@ -88,6 +88,51 @@ public interface BoundHashOperations extends BoundKeyOperations {
@Nullable
Double increment(HK key, double delta);
+ /**
+ * Return a random field from the hash value stored at the bound key.
+ *
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ HK randomField();
+
+ /**
+ * Return a random field from the hash value stored at the bound key.
+ *
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map.Entry randomValue();
+
+ /**
+ * Return a random field from the hash value stored at the bound key. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List randomFields(long count);
+
+ /**
+ * Return a random field from the hash value stored at the bound key.
+ *
+ * @param count number of fields to return. Must be positive.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map randomValues(long count);
+
/**
* Get key set (fields) of hash at the bound key.
*
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 8ff3df2eee..59fe58c5f2 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;
@@ -102,6 +103,73 @@ public interface BoundZSetOperations extends BoundKeyOperations {
@Nullable
Double incrementScore(V value, double delta);
+ /**
+ * Get random element from set at the bound key.
+ *
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ V randomMember();
+
+ /**
+ * Get {@code count} distinct random elements from set at the bound key.
+ *
+ * @param count nr of members to return
+ * @return empty {@link Set} if {@code key} does not exist.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ Set distinctRandomMembers(long count);
+
+ /**
+ * Get {@code count} random elements from set at the bound key.
+ *
+ * @param count nr of members to return.
+ * @return empty {@link List} if {@code key} does not exist or {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List randomMembers(long count);
+
+ /**
+ * Get random element with its score from set at the bound key.
+ *
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ TypedTuple randomMemberWithScore();
+
+ /**
+ * Get {@code count} distinct random elements with their score from set at the bound key.
+ *
+ * @param count nr of members to return
+ * @return empty {@link Set} if {@code key} does not exist.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ Set> distinctRandomMembersWithScore(long count);
+
+ /**
+ * Get {@code count} random elements with their score from set at the bound key.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return.
+ * @return empty {@link List} if {@code key} does not exist or {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List> randomMembersWithScore(long count);
+
/**
* Determine the index of element with {@code value} in a sorted set.
*
diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashOperations.java
index 6b2ceb4b69..0493c294ec 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultBoundHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundHashOperations.java
@@ -110,6 +110,46 @@ public Double increment(HK key, double delta) {
return ops.increment(getKey(), key, delta);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundHashOperations#randomField()
+ */
+ @Nullable
+ @Override
+ public HK randomField() {
+ return ops.randomField(getKey());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundHashOperations#randomValue()
+ */
+ @Nullable
+ @Override
+ public Entry randomValue() {
+ return ops.randomValue(getKey());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundHashOperations#randomFields(long)
+ */
+ @Nullable
+ @Override
+ public List randomFields(long count) {
+ return ops.randomFields(getKey(), count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundHashOperations#randomValues(long)
+ */
+ @Nullable
+ @Override
+ public Map randomValues(long count) {
+ return ops.randomValues(getKey(), count);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundHashOperations#keys()
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 f9a4a42bee..92fb5134dd 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;
@@ -25,6 +26,7 @@
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
+import org.springframework.lang.Nullable;
/**
* Default implementation for {@link BoundZSetOperations}.
@@ -96,6 +98,64 @@ public Double incrementScore(V value, double delta) {
return ops.incrementScore(getKey(), value, delta);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#randomMember()
+ */
+ @Override
+ public V randomMember() {
+ return ops.randomMember(getKey());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#distinctRandomMembers(long)
+ */
+ @Nullable
+ @Override
+ public Set distinctRandomMembers(long count) {
+ return ops.distinctRandomMembers(getKey(), count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#randomMembers(long)
+ */
+ @Nullable
+ @Override
+ public List randomMembers(long count) {
+ return ops.randomMembers(getKey(), count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#randomMemberWithScore()
+ */
+ @Override
+ public TypedTuple randomMemberWithScore() {
+ return ops.randomMemberWithScore(getKey());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#distinctRandomMembersWithScore(long)
+ */
+ @Nullable
+ @Override
+ public Set> distinctRandomMembersWithScore(long count) {
+ return ops.distinctRandomMembersWithScore(getKey(), count);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#randomMembersWithScore(long)
+ */
+ @Nullable
+ @Override
+ public List> randomMembersWithScore(long count) {
+ return ops.randomMembersWithScore(getKey(), count);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#getOperations()
diff --git a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java
index bb3ba72b92..dc096ffb87 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultHashOperations.java
@@ -24,7 +24,9 @@
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
/**
* Default implementation of {@link HashOperations}.
@@ -91,6 +93,67 @@ public Double increment(K key, HK hashKey, double delta) {
return execute(connection -> connection.hIncrBy(rawKey, rawHashKey, delta), true);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.HashOperations#randomField(java.lang.Object)
+ */
+ @Nullable
+ @Override
+ public HK randomField(K key) {
+
+ byte[] rawKey = rawKey(key);
+ return deserializeHashKey(execute(connection -> connection.hRandField(rawKey), true));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.HashOperations#randomValue(java.lang.Object)
+ */
+ @Nullable
+ @Override
+ public Entry randomValue(K key) {
+
+ byte[] rawKey = rawKey(key);
+ Entry rawEntry = execute(connection -> connection.hRandFieldWithValues(rawKey), true);
+ return rawEntry == null ? null
+ : Converters.entryOf(deserializeHashKey(rawEntry.getKey()), deserializeHashValue(rawEntry.getValue()));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.HashOperations#randomFields(java.lang.Object, long)
+ */
+ @Nullable
+ @Override
+ public List randomFields(K key, long count) {
+
+ byte[] rawKey = rawKey(key);
+ List rawValues = execute(connection -> connection.hRandField(rawKey, count), true);
+ return deserializeHashKeys(rawValues);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.HashOperations#randomValues(java.lang.Object, long)
+ */
+ @Nullable
+ @Override
+ public Map randomValues(K key, long count) {
+
+ Assert.isTrue(count > 0, "Count must not be negative");
+ byte[] rawKey = rawKey(key);
+ List> rawEntries = execute(connection -> connection.hRandFieldWithValues(rawKey, count),
+ true);
+
+ if (rawEntries == null) {
+ return null;
+ }
+
+ Map rawMap = new LinkedHashMap<>(rawEntries.size());
+ rawEntries.forEach(entry -> rawMap.put(entry.getKey(), entry.getValue()));
+ return deserializeHashMap(rawMap);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.HashOperations#keys(java.lang.Object)
diff --git a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java
index f2ac85714a..a2fa529d5f 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveHashOperations.java
@@ -144,6 +144,58 @@ public Mono increment(H key, HK hashKey, double delta) {
.hIncrBy(rawKey(key), rawHashKey(hashKey), delta));
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveHashOperations#randomField(H)
+ */
+ @Override
+ public Mono randomField(H key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return template.createMono(connection -> connection //
+ .hashCommands().hRandField(rawKey(key))).map(this::readHashKey);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveHashOperations#randomValue(H)
+ */
+ @Override
+ public Mono> randomValue(H key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return template.createMono(connection -> connection //
+ .hashCommands().hRandFieldWithValues(rawKey(key))).map(this::deserializeHashEntry);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveHashOperations#randomFields(H, long)
+ */
+ @Override
+ public Flux randomFields(H key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return template.createFlux(connection -> connection //
+ .hashCommands().hRandField(rawKey(key), count)).map(this::readHashKey);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveHashOperations#randomValues(H, long)
+ */
+ @Override
+ public Flux> randomValues(H key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return template.createFlux(connection -> connection //
+ .hashCommands().hRandFieldWithValues(rawKey(key), count)).map(this::deserializeHashEntry);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveHashOperations#keys(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 0995bc96d0..893838c8fb 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultReactiveZSetOperations.java
@@ -119,6 +119,82 @@ public Mono incrementScore(K key, V value, double delta) {
return createMono(connection -> connection.zIncrBy(rawKey(key), delta, rawValue(value)));
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#randomMember(K)
+ */
+ @Override
+ public Mono randomMember(K key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return createMono(connection -> connection.zRandMember(rawKey(key))).map(this::readValue);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#distinctRandomMembers(K, long)
+ */
+ @Override
+ public Flux distinctRandomMembers(K key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+ Assert.isTrue(count > 0, "Negative count not supported. Use randomMembers to allow duplicate elements.");
+
+ return createFlux(connection -> connection.zRandMember(rawKey(key), count)).map(this::readValue);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#randomMembers(K, long)
+ */
+ @Override
+ public Flux randomMembers(K key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+ Assert.isTrue(count > 0, "Use a positive number for count. This method is already allowing duplicate elements.");
+
+ return createFlux(connection -> connection.zRandMember(rawKey(key), -count)).map(this::readValue);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#randomMemberWithScore(K)
+ */
+ @Override
+ public Mono> randomMemberWithScore(K key) {
+
+ Assert.notNull(key, "Key must not be null!");
+
+ return createMono(connection -> connection.zRandMemberWithScore(rawKey(key))).map(this::readTypedTuple);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#distinctRandomMembersWithScore(K, long)
+ */
+ @Override
+ public Flux> distinctRandomMembersWithScore(K key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+ Assert.isTrue(count > 0, "Negative count not supported. Use randomMembers to allow duplicate elements.");
+
+ return createFlux(connection -> connection.zRandMemberWithScore(rawKey(key), count)).map(this::readTypedTuple);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ReactiveZSetOperations#randomMembersWithScore(K, long)
+ */
+ @Override
+ public Flux> randomMembersWithScore(K key, long count) {
+
+ Assert.notNull(key, "Key must not be null!");
+ Assert.isTrue(count > 0, "Use a positive number for count. This method is already allowing duplicate elements.");
+
+ return createFlux(connection -> connection.zRandMemberWithScore(rawKey(key), -count)).map(this::readTypedTuple);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveZSetOperations#rank(java.lang.Object, java.lang.Object)
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 79ae492d80..45eb503763 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,8 @@
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Set;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
@@ -26,6 +28,7 @@
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs;
import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
/**
* Default implementation of {@link ZSetOperations}.
@@ -163,6 +166,90 @@ public Long intersectAndStore(K key, Collection otherKeys, K destKey, Aggrega
return execute(connection -> connection.zInterStore(rawDestKey, aggregate, weights, rawKeys), true);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#randomMember(java.lang.Object)
+ */
+ @Override
+ public V randomMember(K key) {
+
+ byte[] rawKey = rawKey(key);
+
+ return deserializeValue(execute(connection -> connection.zRandMember(rawKey), true));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#distinctRandomMembers(java.lang.Object, long)
+ */
+ @Override
+ public Set distinctRandomMembers(K key, long count) {
+
+ Assert.isTrue(count > 0, "Negative count not supported. Use randomMembers to allow duplicate elements.");
+
+ byte[] rawKey = rawKey(key);
+
+ List result = execute(connection -> connection.zRandMember(rawKey, count), true);
+ return result != null ? deserializeValues(new LinkedHashSet<>(result)) : null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#randomMembers(java.lang.Object, long)
+ */
+ @Override
+ public List randomMembers(K key, long count) {
+
+ Assert.isTrue(count > 0, "Use a positive number for count. This method is already allowing duplicate elements.");
+
+ byte[] rawKey = rawKey(key);
+
+ List result = execute(connection -> connection.zRandMember(rawKey, count), true);
+ return deserializeValues(result);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#randomMemberWithScore(java.lang.Object)
+ */
+ @Override
+ public TypedTuple randomMemberWithScore(K key) {
+
+ byte[] rawKey = rawKey(key);
+
+ return deserializeTuple(execute(connection -> connection.zRandMemberWithScore(rawKey), true));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#distinctRandomMembersWithScore(java.lang.Object, long)
+ */
+ @Override
+ public Set> distinctRandomMembersWithScore(K key, long count) {
+
+ Assert.isTrue(count > 0, "Negative count not supported. Use randomMembers to allow duplicate elements.");
+
+ byte[] rawKey = rawKey(key);
+
+ List result = execute(connection -> connection.zRandMemberWithScore(rawKey, count), true);
+ return result != null ? deserializeTupleValues(new LinkedHashSet<>(result)) : null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.ZSetOperations#randomMembersWithScore(java.lang.Object, long)
+ */
+ @Override
+ public List> randomMembersWithScore(K key, long count) {
+
+ Assert.isTrue(count > 0, "Use a positive number for count. This method is already allowing duplicate elements.");
+
+ byte[] rawKey = rawKey(key);
+
+ List result = execute(connection -> connection.zRandMemberWithScore(rawKey, count), true);
+ return result != null ? deserializeTupleValues(result) : null;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ZSetOperations#range(java.lang.Object, long, long)
diff --git a/src/main/java/org/springframework/data/redis/core/HashOperations.java b/src/main/java/org/springframework/data/redis/core/HashOperations.java
index a4e474fc9b..60b763e169 100644
--- a/src/main/java/org/springframework/data/redis/core/HashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/HashOperations.java
@@ -88,6 +88,55 @@ public interface HashOperations {
*/
Double increment(H key, HK hashKey, double delta);
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ HK randomField(H key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map.Entry randomValue(H key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ List randomFields(H key, long count);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return. Must be positive.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ @Nullable
+ Map randomValues(H key, long count);
+
/**
* Get key set (fields) of hash at {@code key}.
*
diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java
index 4d6f087774..562f6647dc 100644
--- a/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/ReactiveHashOperations.java
@@ -87,6 +87,54 @@ public interface ReactiveHashOperations {
*/
Mono increment(H key, HK hashKey, double delta);
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Mono randomField(H key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Mono> randomValue(H key);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Flux randomFields(H key, long count);
+
+ /**
+ * Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
+ * positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
+ * negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
+ * the number of returned fields is the absolute value of the specified count.
+ *
+ * @param key must not be {@literal null}.
+ * @param count number of fields to return.
+ * @return {@literal null} if key does not exist or when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: HRANDFIELD
+ */
+ Flux> randomValues(H key, long count);
+
/**
* Get key set (fields) of hash at {@code key}.
*
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 1914da9664..026939284d 100644
--- a/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/ReactiveZSetOperations.java
@@ -80,6 +80,74 @@ public interface ReactiveZSetOperations {
*/
Mono incrementScore(K key, V value, double delta);
+ /**
+ * Get random element from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Mono randomMember(K key);
+
+ /**
+ * Get {@code count} distinct random elements from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return
+ * @return
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux distinctRandomMembers(K key, long count);
+
+ /**
+ * Get {@code count} random elements from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return.
+ * @return
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux randomMembers(K key, long count);
+
+ /**
+ * Get random element with its score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Mono> randomMemberWithScore(K key);
+
+ /**
+ * Get {@code count} distinct random elements with their score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return
+ * @return
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux> distinctRandomMembersWithScore(K key, long count);
+
+ /**
+ * Get {@code count} random elements with their score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return.
+ * @return
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ Flux> randomMembersWithScore(K key, long count);
+
/**
* Determine the index of element with {@code value} in a sorted set.
*
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 822b8a47a7..76047a615c 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;
@@ -133,6 +134,78 @@ static TypedTuple of(V value, @Nullable Double score) {
@Nullable
Double incrementScore(K key, V value, double delta);
+ /**
+ * Get random element from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ V randomMember(K key);
+
+ /**
+ * Get {@code count} distinct random elements from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return
+ * @return empty {@link Set} if {@code key} does not exist.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ Set distinctRandomMembers(K key, long count);
+
+ /**
+ * Get {@code count} random elements from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return.
+ * @return empty {@link List} if {@code key} does not exist or {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List randomMembers(K key, long count);
+
+ /**
+ * Get random element with its score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ TypedTuple randomMemberWithScore(K key);
+
+ /**
+ * Get {@code count} distinct random elements with their score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return
+ * @return empty {@link Set} if {@code key} does not exist.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ Set> distinctRandomMembersWithScore(K key, long count);
+
+ /**
+ * Get {@code count} random elements with their score from set at {@code key}.
+ *
+ * @param key must not be {@literal null}.
+ * @param count nr of members to return.
+ * @return empty {@link List} if {@code key} does not exist or {@literal null} when used in pipeline / transaction.
+ * @throws IllegalArgumentException if count is negative.
+ * @since 2.6
+ * @see Redis Documentation: ZRANDMEMBER
+ */
+ @Nullable
+ List> randomMembersWithScore(K key, long count);
+
/**
* Determine the index of element with {@code value} in a 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 cacafc14fe..5c66d368a3 100644
--- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
@@ -1269,6 +1269,52 @@ void testHashIncrDecrByLong() {
verifyResults(Arrays.asList(new Object[] { true, largeNumber, -largeNumber }));
}
+ @Test // GH-2048
+ @EnabledOnCommand("HRANDFIELD")
+ void testHRandField() {
+
+ String key = "hash";
+ Map hash = new HashMap<>();
+ hash.put("key1", "val1");
+ hash.put("key2", "val2");
+ hash.put("key3", "val3");
+ hash.put("key4", "val4");
+
+ connection.hMSet(key, hash);
+ actual.add(connection.hRandField(key));
+ actual.add(connection.hRandField(key, 2));
+ actual.add(connection.hRandField(key, -10));
+
+ List