Skip to content

Commit 0b8fbae

Browse files
mp911dechristophstrobl
authored andcommitted
Add support for HRANDFIELD.
Closes: #2048 Original Pull Request: #2104
1 parent 961ba90 commit 0b8fbae

22 files changed

+1125
-19
lines changed

src/main/asciidoc/new-features.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele
77
== New in Spring Data Redis 2.6
88

99
* 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.
10-
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`).
10+
* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`, `COPY`, `GETEX`, `GETDEL`, `ZPOPMIN`, `BZPOPMIN`, `ZPOPMAX`, `BZPOPMAX`, `ZMSCORE`, `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZUNION`, `HRANDFIELD`).
1111

1212
[[new-in-2.5.0]]
1313
== New in Spring Data Redis 2.5

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

+103-18
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,26 @@ public StringRecord convert(ByteRecord source) {
106106
@SuppressWarnings("rawtypes") private Queue<Converter> txConverters = new LinkedList<>();
107107
private boolean deserializePipelineAndTxResults = false;
108108

109+
private Entry<String, String> convertEntry(Entry<byte[], byte[]> source) {
110+
return new Entry<String, String>() {
111+
112+
@Override
113+
public String getKey() {
114+
return bytesToString.convert(source.getKey());
115+
}
116+
117+
@Override
118+
public String getValue() {
119+
return bytesToString.convert(source.getValue());
120+
}
121+
122+
@Override
123+
public String setValue(String value) {
124+
throw new UnsupportedOperationException("Cannot set value for entry");
125+
}
126+
};
127+
}
128+
109129
private class DeserializingConverter implements Converter<byte[], String> {
110130
public String convert(byte[] source) {
111131
return serializer.deserialize(source);
@@ -2307,6 +2327,88 @@ public Double hIncrBy(String key, String field, double delta) {
23072327
return hIncrBy(serialize(key), serialize(field), delta);
23082328
}
23092329

2330+
/*
2331+
* (non-Javadoc)
2332+
* @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[])
2333+
*/
2334+
@Nullable
2335+
@Override
2336+
public byte[] hRandField(byte[] key) {
2337+
return convertAndReturn(delegate.hRandField(key), Converters.identityConverter());
2338+
}
2339+
2340+
/*
2341+
* (non-Javadoc)
2342+
* @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[])
2343+
*/
2344+
@Nullable
2345+
@Override
2346+
public Entry<byte[], byte[]> hRandFieldWithValues(byte[] key) {
2347+
return convertAndReturn(delegate.hRandFieldWithValues(key), Converters.identityConverter());
2348+
}
2349+
2350+
/*
2351+
* (non-Javadoc)
2352+
* @see org.springframework.data.redis.connection.RedisHashCommands#hRandField(byte[], long)
2353+
*/
2354+
@Nullable
2355+
@Override
2356+
public List<byte[]> hRandField(byte[] key, long count) {
2357+
return convertAndReturn(delegate.hRandField(key, count), Converters.identityConverter());
2358+
}
2359+
2360+
/*
2361+
* (non-Javadoc)
2362+
* @see org.springframework.data.redis.connection.RedisHashCommands#hRandFieldWithValues(byte[], long)
2363+
*/
2364+
@Nullable
2365+
@Override
2366+
public List<Entry<byte[], byte[]>> hRandFieldWithValues(byte[] key, long count) {
2367+
return convertAndReturn(delegate.hRandFieldWithValues(key, count), Converters.identityConverter());
2368+
}
2369+
2370+
/*
2371+
* (non-Javadoc)
2372+
* @see org.springframework.data.redis.connection.StringRedisConnection#hRandField(java.lang.String)
2373+
*/
2374+
@Nullable
2375+
@Override
2376+
public String hRandField(String key) {
2377+
return convertAndReturn(delegate.hRandField(serialize(key)), bytesToString);
2378+
}
2379+
2380+
/*
2381+
* (non-Javadoc)
2382+
* @see org.springframework.data.redis.connection.StringRedisConnection#hRandFieldWithValues(java.lang.String)
2383+
*/
2384+
@Nullable
2385+
@Override
2386+
public Entry<String, String> hRandFieldWithValues(String key) {
2387+
return convertAndReturn(delegate.hRandFieldWithValues(serialize(key)),
2388+
(Converter<Entry<byte[], byte[]>, Entry<String, String>>) this::convertEntry);
2389+
}
2390+
2391+
/*
2392+
* (non-Javadoc)
2393+
* @see org.springframework.data.redis.connection.StringRedisConnection#hRandField(java.lang.String, long)
2394+
*/
2395+
@Nullable
2396+
@Override
2397+
public List<String> hRandField(String key, long count) {
2398+
return convertAndReturn(delegate.hRandField(serialize(key), count), byteListToStringList);
2399+
}
2400+
2401+
/*
2402+
* (non-Javadoc)
2403+
* @see org.springframework.data.redis.connection.StringRedisConnection#hRandFieldWithValues(java.lang.String, long)
2404+
*/
2405+
@Nullable
2406+
@Override
2407+
public List<Entry<String, String>> hRandFieldWithValues(String key, long count) {
2408+
return convertAndReturn(delegate.hRandFieldWithValues(serialize(key), count),
2409+
new ListConverter<>(this::convertEntry));
2410+
}
2411+
23102412
/*
23112413
* (non-Javadoc)
23122414
* @see org.springframework.data.redis.connection.StringRedisConnection#hKeys(java.lang.String)
@@ -3924,24 +4026,7 @@ public String getClientName() {
39244026
@Override
39254027
public Cursor<Entry<String, String>> hScan(String key, ScanOptions options) {
39264028

3927-
return new ConvertingCursor<>(this.delegate.hScan(this.serialize(key), options),
3928-
source -> new Entry<String, String>() {
3929-
3930-
@Override
3931-
public String getKey() {
3932-
return bytesToString.convert(source.getKey());
3933-
}
3934-
3935-
@Override
3936-
public String getValue() {
3937-
return bytesToString.convert(source.getValue());
3938-
}
3939-
3940-
@Override
3941-
public String setValue(String value) {
3942-
throw new UnsupportedOperationException("Cannot set value for entry in cursor");
3943-
}
3944-
});
4029+
return new ConvertingCursor<>(this.delegate.hScan(this.serialize(key), options), this::convertEntry);
39454030
}
39464031

39474032
/*

src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java

+28
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,34 @@ default Long hIncrBy(byte[] key, byte[] field, long delta) {
13291329
return hashCommands().hIncrBy(key, field, delta);
13301330
}
13311331

1332+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1333+
@Override
1334+
@Deprecated
1335+
default byte[] hRandField(byte[] key) {
1336+
return hashCommands().hRandField(key);
1337+
}
1338+
1339+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1340+
@Override
1341+
@Deprecated
1342+
default Entry<byte[], byte[]> hRandFieldWithValues(byte[] key) {
1343+
return hashCommands().hRandFieldWithValues(key);
1344+
}
1345+
1346+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1347+
@Override
1348+
@Deprecated
1349+
default List<byte[]> hRandField(byte[] key, long count) {
1350+
return hashCommands().hRandField(key, count);
1351+
}
1352+
1353+
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
1354+
@Override
1355+
@Deprecated
1356+
default List<Entry<byte[], byte[]>> hRandFieldWithValues(byte[] key, long count) {
1357+
return hashCommands().hRandFieldWithValues(key, count);
1358+
}
1359+
13321360
/** @deprecated in favor of {@link RedisConnection#hashCommands()}}. */
13331361
@Override
13341362
@Deprecated

src/main/java/org/springframework/data/redis/connection/ReactiveHashCommands.java

+139
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,145 @@ default Mono<Long> hLen(ByteBuffer key) {
512512
*/
513513
Flux<NumericResponse<KeyCommand, Long>> hLen(Publisher<KeyCommand> commands);
514514

515+
/**
516+
* {@literal HRANDFIELD} {@link Command}.
517+
*
518+
* @author Mark Paluch
519+
* @since 2.6
520+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
521+
*/
522+
class HRandFieldCommand extends KeyCommand {
523+
524+
private long count;
525+
526+
private HRandFieldCommand(@Nullable ByteBuffer key, long count) {
527+
528+
super(key);
529+
530+
this.count = count;
531+
}
532+
533+
/**
534+
* Applies the hash {@literal key}. Constructs a new command instance with all previously configured properties.
535+
*
536+
* @param key must not be {@literal null}.
537+
* @return a new {@link HRandFieldCommand} with {@literal key} applied.
538+
*/
539+
public static HRandFieldCommand key(ByteBuffer key) {
540+
541+
Assert.notNull(key, "Key must not be null!");
542+
543+
return new HRandFieldCommand(key, 1);
544+
}
545+
546+
/**
547+
* Applies the {@literal count}. Constructs a new command instance with all previously configured properties. If the
548+
* provided {@code count} argument is positive, return a list of distinct fields, capped either at {@code count} or
549+
* the hash size. If {@code count} is negative, the behavior changes and the command is allowed to return the same
550+
* field multiple times. In this case, the number of returned fields is the absolute value of the specified count.
551+
*
552+
* @param count
553+
* @return a new {@link HRandFieldCommand} with {@literal key} applied.
554+
*/
555+
public HRandFieldCommand count(long count) {
556+
return new HRandFieldCommand(getKey(), count);
557+
}
558+
559+
public long getCount() {
560+
return count;
561+
}
562+
}
563+
564+
/**
565+
* Return a random field from the hash value stored at {@code key}.
566+
*
567+
* @param key must not be {@literal null}.
568+
* @return
569+
* @since 2.6
570+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
571+
*/
572+
default Mono<ByteBuffer> hRandField(ByteBuffer key) {
573+
574+
Assert.notNull(key, "Key must not be null!");
575+
576+
return hRandField(Mono.just(HRandFieldCommand.key(key).count(1))).flatMap(CommandResponse::getOutput).next();
577+
}
578+
579+
/**
580+
* Return a random field from the hash value stored at {@code key}.
581+
*
582+
* @param key must not be {@literal null}.
583+
* @return
584+
* @since 2.6
585+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
586+
*/
587+
default Mono<Map.Entry<ByteBuffer, ByteBuffer>> hRandFieldWithValues(ByteBuffer key) {
588+
589+
Assert.notNull(key, "Key must not be null!");
590+
591+
return hRandFieldWithValues(Mono.just(HRandFieldCommand.key(key).count(1))).flatMap(CommandResponse::getOutput)
592+
.next();
593+
}
594+
595+
/**
596+
* Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
597+
* positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
598+
* negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
599+
* the number of returned fields is the absolute value of the specified count.
600+
*
601+
* @param key must not be {@literal null}.
602+
* @param count number of fields to return.
603+
* @return
604+
* @since 2.6
605+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
606+
*/
607+
default Flux<ByteBuffer> hRandField(ByteBuffer key, long count) {
608+
609+
Assert.notNull(key, "Key must not be null!");
610+
611+
return hRandField(Mono.just(HRandFieldCommand.key(key).count(count))).flatMap(CommandResponse::getOutput);
612+
}
613+
614+
/**
615+
* Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
616+
* positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
617+
* negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
618+
* the number of returned fields is the absolute value of the specified count.
619+
*
620+
* @param key must not be {@literal null}.
621+
* @param count number of fields to return.
622+
* @return {@literal null} if key does not exist or when used in pipeline / transaction.
623+
* @since 2.6
624+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
625+
*/
626+
default Flux<Map.Entry<ByteBuffer, ByteBuffer>> hRandFieldWithValues(ByteBuffer key, long count) {
627+
628+
Assert.notNull(key, "Key must not be null!");
629+
630+
return hRandFieldWithValues(Mono.just(HRandFieldCommand.key(key).count(count))).flatMap(CommandResponse::getOutput);
631+
}
632+
633+
/**
634+
* Get random fields of hash at {@literal key}.
635+
*
636+
* @param commands must not be {@literal null}.
637+
* @return
638+
* @since 2.6
639+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
640+
*/
641+
Flux<CommandResponse<HRandFieldCommand, Flux<ByteBuffer>>> hRandField(Publisher<HRandFieldCommand> commands);
642+
643+
/**
644+
* Get random fields along their values of hash at {@literal key}.
645+
*
646+
* @param commands must not be {@literal null}.
647+
* @return
648+
* @since 2.6
649+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
650+
*/
651+
Flux<CommandResponse<HRandFieldCommand, Flux<Map.Entry<ByteBuffer, ByteBuffer>>>> hRandFieldWithValues(
652+
Publisher<HRandFieldCommand> commands);
653+
515654
/**
516655
* Get key set (fields) of hash at {@literal key}.
517656
*

src/main/java/org/springframework/data/redis/connection/RedisHashCommands.java

+52
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,58 @@ public interface RedisHashCommands {
173173
@Nullable
174174
Map<byte[], byte[]> hGetAll(byte[] key);
175175

176+
/**
177+
* Return a random field from the hash value stored at {@code key}.
178+
*
179+
* @param key must not be {@literal null}.
180+
* @return {@literal null} if key does not exist or when used in pipeline / transaction.
181+
* @since 2.6
182+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
183+
*/
184+
@Nullable
185+
byte[] hRandField(byte[] key);
186+
187+
/**
188+
* Return a random field from the hash value stored at {@code key}.
189+
*
190+
* @param key must not be {@literal null}.
191+
* @return {@literal null} if key does not exist or when used in pipeline / transaction.
192+
* @since 2.6
193+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
194+
*/
195+
@Nullable
196+
Map.Entry<byte[], byte[]> hRandFieldWithValues(byte[] key);
197+
198+
/**
199+
* Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
200+
* positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
201+
* negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
202+
* the number of returned fields is the absolute value of the specified count.
203+
*
204+
* @param key must not be {@literal null}.
205+
* @param count number of fields to return.
206+
* @return {@literal null} if key does not exist or when used in pipeline / transaction.
207+
* @since 2.6
208+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
209+
*/
210+
@Nullable
211+
List<byte[]> hRandField(byte[] key, long count);
212+
213+
/**
214+
* Return a random field from the hash value stored at {@code key}. If the provided {@code count} argument is
215+
* positive, return a list of distinct fields, capped either at {@code count} or the hash size. If {@code count} is
216+
* negative, the behavior changes and the command is allowed to return the same field multiple times. In this case,
217+
* the number of returned fields is the absolute value of the specified count.
218+
*
219+
* @param key must not be {@literal null}.
220+
* @param count number of fields to return.
221+
* @return {@literal null} if key does not exist or when used in pipeline / transaction.
222+
* @since 2.6
223+
* @see <a href="https://redis.io/commands/hrandfield">Redis Documentation: HRANDFIELD</a>
224+
*/
225+
@Nullable
226+
List<Map.Entry<byte[], byte[]>> hRandFieldWithValues(byte[] key, long count);
227+
176228
/**
177229
* Use a {@link Cursor} to iterate over entries in hash at {@code key}.
178230
*

0 commit comments

Comments
 (0)