Skip to content

Commit 54ad66b

Browse files
mp911dechristophstrobl
authored andcommitted
Add support for LMOVE and BLMOVE.
Closes: #2039 Original Pull Request: #2107
1 parent b6820f0 commit 54ad66b

28 files changed

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

+38
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,44 @@ public Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value) {
736736
return convertAndReturn(delegate.lInsert(key, where, pivot, value), Converters.identityConverter());
737737
}
738738

739+
/*
740+
* (non-Javadoc)
741+
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
742+
*/
743+
@Override
744+
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
745+
return convertAndReturn(delegate.lMove(sourceKey, destinationKey, from, to), Converters.identityConverter());
746+
}
747+
748+
/*
749+
* (non-Javadoc)
750+
* @see org.springframework.data.redis.connection.RedisListCommands#bLMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
751+
*/
752+
@Override
753+
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
754+
return convertAndReturn(delegate.bLMove(sourceKey, destinationKey, from, to, timeout),
755+
Converters.identityConverter());
756+
}
757+
758+
/*
759+
* (non-Javadoc)
760+
* @see org.springframework.data.redis.connection.StringRedisConnection#lMove(java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
761+
*/
762+
@Override
763+
public String lMove(String sourceKey, String destinationKey, Direction from, Direction to) {
764+
return convertAndReturn(delegate.lMove(serialize(sourceKey), serialize(destinationKey), from, to), bytesToString);
765+
}
766+
767+
/*
768+
* (non-Javadoc)
769+
* @see org.springframework.data.redis.connection.StringRedisConnection#bLMove(java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
770+
*/
771+
@Override
772+
public String bLMove(String sourceKey, String destinationKey, Direction from, Direction to, double timeout) {
773+
return convertAndReturn(delegate.bLMove(serialize(sourceKey), serialize(destinationKey), from, to, timeout),
774+
bytesToString);
775+
}
776+
739777
/*
740778
* (non-Javadoc)
741779
* @see org.springframework.data.redis.connection.RedisListCommands#lLen(byte[])

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

+14
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,20 @@ default Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value) {
714714
return listCommands().lInsert(key, where, pivot, value);
715715
}
716716

717+
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
718+
@Override
719+
@Deprecated
720+
default byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
721+
return listCommands().lMove(sourceKey, destinationKey, from, to);
722+
}
723+
724+
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
725+
@Override
726+
@Deprecated
727+
default byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
728+
return listCommands().bLMove(sourceKey, destinationKey, from, to, timeout);
729+
}
730+
717731
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
718732
@Override
719733
@Deprecated

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

+205-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,28 @@ public interface ReactiveListCommands {
5151
* @author Christoph Strobl
5252
*/
5353
enum Direction {
54-
LEFT, RIGHT
54+
55+
LEFT, RIGHT;
56+
57+
/**
58+
* Alias for {@link Direction#LEFT}.
59+
*
60+
* @since 2.6
61+
* @return
62+
*/
63+
public static Direction first() {
64+
return LEFT;
65+
}
66+
67+
/**
68+
* Alias for {@link Direction#RIGHT}.
69+
*
70+
* @since 2.6
71+
* @return
72+
*/
73+
public static Direction last() {
74+
return RIGHT;
75+
}
5576
}
5677

5778
/**
@@ -635,6 +656,189 @@ default Mono<Long> lInsert(ByteBuffer key, Position position, ByteBuffer pivot,
635656
*/
636657
Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands);
637658

659+
/**
660+
* {@code LMOVE} command parameters.
661+
*
662+
* @author Mark Paluch
663+
* @since 2.6
664+
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
665+
*/
666+
class LMoveCommand extends KeyCommand {
667+
668+
private final @Nullable ByteBuffer destinationKey;
669+
private final @Nullable Direction from;
670+
private final @Nullable Direction to;
671+
672+
public LMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
673+
@Nullable Direction to) {
674+
super(sourceKey);
675+
this.destinationKey = destinationKey;
676+
this.from = from;
677+
this.to = to;
678+
}
679+
680+
/**
681+
* Creates a new {@link LMoveCommand} given a {@link ByteBuffer sourceKey}.
682+
*
683+
* @param sourceKey must not be {@literal null}.
684+
* @param sourceDirection must not be {@literal null}.
685+
* @return a new {@link LMoveCommand} for {@link ByteBuffer value}.
686+
*/
687+
public static LMoveCommand from(ByteBuffer sourceKey, Direction sourceDirection) {
688+
689+
Assert.notNull(sourceKey, "Source key must not be null!");
690+
Assert.notNull(sourceDirection, "Direction must not be null!");
691+
692+
return new LMoveCommand(sourceKey, null, sourceDirection, null);
693+
}
694+
695+
/**
696+
* Applies the {@link ByteBuffer destinationKey}. Constructs a new command instance with all previously configured
697+
* properties.
698+
*
699+
* @param destinationKey must not be {@literal null}.
700+
* @param to must not be {@literal null}.
701+
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
702+
*/
703+
public LMoveCommand to(ByteBuffer destinationKey, Direction destinationDirection) {
704+
705+
Assert.notNull(destinationKey, "Destination key must not be null!");
706+
Assert.notNull(destinationDirection, "Direction must not be null!");
707+
708+
return new LMoveCommand(getKey(), destinationKey, from, destinationDirection);
709+
}
710+
711+
/**
712+
* Applies the {@link Duration timeout}. Constructs a new command instance with all previously configured
713+
* properties.
714+
*
715+
* @param timeout must not be {@literal null}.
716+
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
717+
*/
718+
public BLMoveCommand timeout(Duration timeout) {
719+
720+
Assert.notNull(timeout, "Timeout must not be null!");
721+
722+
return new BLMoveCommand(getKey(), destinationKey, from, to, timeout);
723+
}
724+
725+
@Nullable
726+
public ByteBuffer getDestinationKey() {
727+
return destinationKey;
728+
}
729+
730+
@Nullable
731+
public Direction getFrom() {
732+
return from;
733+
}
734+
735+
@Nullable
736+
public Direction getTo() {
737+
return to;
738+
}
739+
}
740+
741+
/**
742+
* {@code BLMOVE} command parameters.
743+
*
744+
* @author Mark Paluch
745+
* @since 2.6
746+
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
747+
*/
748+
class BLMoveCommand extends LMoveCommand {
749+
750+
private final @Nullable Duration timeout;
751+
752+
private BLMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
753+
@Nullable Direction to, @Nullable Duration timeout) {
754+
super(sourceKey, destinationKey, from, to);
755+
this.timeout = timeout;
756+
}
757+
758+
@Nullable
759+
public Duration getTimeout() {
760+
return timeout;
761+
}
762+
}
763+
764+
/**
765+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
766+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
767+
* {@code to} argument) of the list stored at {@code destinationKey}.
768+
*
769+
* @param sourceKey must not be {@literal null}.
770+
* @param destinationKey must not be {@literal null}.
771+
* @param from must not be {@literal null}.
772+
* @param to must not be {@literal null}.
773+
* @return
774+
* @since 2.6
775+
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
776+
*/
777+
default Mono<ByteBuffer> lMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to) {
778+
779+
Assert.notNull(sourceKey, "Source key must not be null!");
780+
Assert.notNull(destinationKey, "Destination key must not be null!");
781+
Assert.notNull(from, "From direction must not be null!");
782+
Assert.notNull(to, "To direction must not be null!");
783+
784+
return lMove(Mono.just(LMoveCommand.from(sourceKey, from).to(destinationKey, to))).map(CommandResponse::getOutput)
785+
.next();
786+
}
787+
788+
/**
789+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
790+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
791+
* {@code to} argument) of the list stored at {@code destinationKey}.
792+
*
793+
* @param commands must not be {@literal null}.
794+
* @return
795+
* @since 2.6
796+
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
797+
*/
798+
Flux<ByteBufferResponse<LMoveCommand>> lMove(Publisher<? extends LMoveCommand> commands);
799+
800+
/**
801+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
802+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
803+
* {@code to} argument) of the list stored at {@code destinationKey}.
804+
*
805+
* @param sourceKey must not be {@literal null}.
806+
* @param destinationKey must not be {@literal null}.
807+
* @param from must not be {@literal null}.
808+
* @param to must not be {@literal null}.
809+
* @param timeout
810+
* @return
811+
* @since 2.6
812+
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
813+
*/
814+
default Mono<ByteBuffer> bLMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to,
815+
Duration timeout) {
816+
817+
Assert.notNull(sourceKey, "Source key must not be null!");
818+
Assert.notNull(destinationKey, "Destination key must not be null!");
819+
Assert.notNull(from, "From direction must not be null!");
820+
Assert.notNull(to, "To direction must not be null!");
821+
Assert.notNull(timeout, "Timeout must not be null!");
822+
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative!");
823+
824+
return bLMove(Mono.just(BLMoveCommand.from(sourceKey, from).to(destinationKey, to).timeout(timeout)))
825+
.map(CommandResponse::getOutput).next();
826+
}
827+
828+
/**
829+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
830+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
831+
* {@code to} argument) of the list stored at {@code destinationKey}.
832+
* <p/>
833+
* <b>Blocks connection</b> until element available or {@code timeout} reached.
834+
*
835+
* @param commands must not be {@literal null}.
836+
* @return
837+
* @since 2.6
838+
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
839+
*/
840+
Flux<ByteBufferResponse<BLMoveCommand>> bLMove(Publisher<BLMoveCommand> commands);
841+
638842
/**
639843
* {@code LSET} command parameters.
640844
*

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

+64
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@ enum Position {
3737
BEFORE, AFTER
3838
}
3939

40+
/**
41+
* List move direction.
42+
*
43+
* @since 2.6
44+
*/
45+
enum Direction {
46+
LEFT, RIGHT;
47+
48+
/**
49+
* Alias for {@link Direction#LEFT}.
50+
*
51+
* @return
52+
*/
53+
public static Direction first() {
54+
return LEFT;
55+
}
56+
57+
/**
58+
* Alias for {@link Direction#RIGHT}.
59+
*
60+
* @return
61+
*/
62+
public static Direction last() {
63+
return RIGHT;
64+
}
65+
}
66+
4067
/**
4168
* Append {@code values} to {@code key}.
4269
*
@@ -169,6 +196,43 @@ default Long lPos(byte[] key, byte[] element) {
169196
@Nullable
170197
Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value);
171198

199+
/**
200+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
201+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
202+
* {@code to} argument) of the list stored at {@code destinationKey}.
203+
*
204+
* @param sourceKey must not be {@literal null}.
205+
* @param destinationKey must not be {@literal null}.
206+
* @param from must not be {@literal null}.
207+
* @param to must not be {@literal null}.
208+
* @return {@literal null} when used in pipeline / transaction.
209+
* @since 2.6
210+
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
211+
* @see #bLMove(byte[], byte[], Direction, Direction, double)
212+
*/
213+
@Nullable
214+
byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to);
215+
216+
/**
217+
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
218+
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
219+
* {@code to} argument) of the list stored at {@code destinationKey}.
220+
* <p/>
221+
* <b>Blocks connection</b> until element available or {@code timeout} reached.
222+
*
223+
* @param sourceKey must not be {@literal null}.
224+
* @param destinationKey must not be {@literal null}.
225+
* @param from must not be {@literal null}.
226+
* @param to must not be {@literal null}.
227+
* @param timeout
228+
* @return {@literal null} when used in pipeline / transaction.
229+
* @since 2.6
230+
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
231+
* @see #lMove(byte[], byte[], Direction, Direction)
232+
*/
233+
@Nullable
234+
byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout);
235+
172236
/**
173237
* Set the {@code value} list element at {@code index}.
174238
*

0 commit comments

Comments
 (0)