Add support for LMOVE and BLMOVE.
Closes: #2039 Original Pull Request: #2107
This commit is contained in:
committed by
Christoph Strobl
parent
b6820f0f61
commit
54ad66b62c
@@ -736,6 +736,44 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
|
||||
return convertAndReturn(delegate.lInsert(key, where, pivot, value), Converters.identityConverter());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
|
||||
return convertAndReturn(delegate.lMove(sourceKey, destinationKey, from, to), Converters.identityConverter());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
|
||||
return convertAndReturn(delegate.bLMove(sourceKey, destinationKey, from, to, timeout),
|
||||
Converters.identityConverter());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public String lMove(String sourceKey, String destinationKey, Direction from, Direction to) {
|
||||
return convertAndReturn(delegate.lMove(serialize(sourceKey), serialize(destinationKey), from, to), bytesToString);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public String bLMove(String sourceKey, String destinationKey, Direction from, Direction to, double timeout) {
|
||||
return convertAndReturn(delegate.bLMove(serialize(sourceKey), serialize(destinationKey), from, to, timeout),
|
||||
bytesToString);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lLen(byte[])
|
||||
|
||||
@@ -714,6 +714,20 @@ public interface DefaultedRedisConnection extends RedisConnection {
|
||||
return listCommands().lInsert(key, where, pivot, value);
|
||||
}
|
||||
|
||||
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
|
||||
@Override
|
||||
@Deprecated
|
||||
default byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
|
||||
return listCommands().lMove(sourceKey, destinationKey, from, to);
|
||||
}
|
||||
|
||||
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
|
||||
@Override
|
||||
@Deprecated
|
||||
default byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
|
||||
return listCommands().bLMove(sourceKey, destinationKey, from, to, timeout);
|
||||
}
|
||||
|
||||
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
|
||||
@Override
|
||||
@Deprecated
|
||||
|
||||
@@ -51,7 +51,28 @@ public interface ReactiveListCommands {
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
enum Direction {
|
||||
LEFT, RIGHT
|
||||
|
||||
LEFT, RIGHT;
|
||||
|
||||
/**
|
||||
* Alias for {@link Direction#LEFT}.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return
|
||||
*/
|
||||
public static Direction first() {
|
||||
return LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link Direction#RIGHT}.
|
||||
*
|
||||
* @since 2.6
|
||||
* @return
|
||||
*/
|
||||
public static Direction last() {
|
||||
return RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,6 +656,189 @@ public interface ReactiveListCommands {
|
||||
*/
|
||||
Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code LMOVE} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
class LMoveCommand extends KeyCommand {
|
||||
|
||||
private final @Nullable ByteBuffer destinationKey;
|
||||
private final @Nullable Direction from;
|
||||
private final @Nullable Direction to;
|
||||
|
||||
public LMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
|
||||
@Nullable Direction to) {
|
||||
super(sourceKey);
|
||||
this.destinationKey = destinationKey;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LMoveCommand} given a {@link ByteBuffer sourceKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param sourceDirection must not be {@literal null}.
|
||||
* @return a new {@link LMoveCommand} for {@link ByteBuffer value}.
|
||||
*/
|
||||
public static LMoveCommand from(ByteBuffer sourceKey, Direction sourceDirection) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(sourceDirection, "Direction must not be null!");
|
||||
|
||||
return new LMoveCommand(sourceKey, null, sourceDirection, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@link ByteBuffer destinationKey}. Constructs a new command instance with all previously configured
|
||||
* properties.
|
||||
*
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
|
||||
*/
|
||||
public LMoveCommand to(ByteBuffer destinationKey, Direction destinationDirection) {
|
||||
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(destinationDirection, "Direction must not be null!");
|
||||
|
||||
return new LMoveCommand(getKey(), destinationKey, from, destinationDirection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the {@link Duration timeout}. Constructs a new command instance with all previously configured
|
||||
* properties.
|
||||
*
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
|
||||
*/
|
||||
public BLMoveCommand timeout(Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return new BLMoveCommand(getKey(), destinationKey, from, to, timeout);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ByteBuffer getDestinationKey() {
|
||||
return destinationKey;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Direction getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Direction getTo() {
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code BLMOVE} command parameters.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
class BLMoveCommand extends LMoveCommand {
|
||||
|
||||
private final @Nullable Duration timeout;
|
||||
|
||||
private BLMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
|
||||
@Nullable Direction to, @Nullable Duration timeout) {
|
||||
super(sourceKey, destinationKey, from, to);
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
default Mono<ByteBuffer> lMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return lMove(Mono.just(LMoveCommand.from(sourceKey, from).to(destinationKey, to))).map(CommandResponse::getOutput)
|
||||
.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
Flux<ByteBufferResponse<LMoveCommand>> lMove(Publisher<? extends LMoveCommand> commands);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
default Mono<ByteBuffer> bLMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to,
|
||||
Duration timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative!");
|
||||
|
||||
return bLMove(Mono.just(BLMoveCommand.from(sourceKey, from).to(destinationKey, to).timeout(timeout)))
|
||||
.map(CommandResponse::getOutput).next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
Flux<ByteBufferResponse<BLMoveCommand>> bLMove(Publisher<BLMoveCommand> commands);
|
||||
|
||||
/**
|
||||
* {@code LSET} command parameters.
|
||||
*
|
||||
|
||||
@@ -37,6 +37,33 @@ public interface RedisListCommands {
|
||||
BEFORE, AFTER
|
||||
}
|
||||
|
||||
/**
|
||||
* List move direction.
|
||||
*
|
||||
* @since 2.6
|
||||
*/
|
||||
enum Direction {
|
||||
LEFT, RIGHT;
|
||||
|
||||
/**
|
||||
* Alias for {@link Direction#LEFT}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Direction first() {
|
||||
return LEFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for {@link Direction#RIGHT}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Direction last() {
|
||||
return RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key}.
|
||||
*
|
||||
@@ -169,6 +196,43 @@ public interface RedisListCommands {
|
||||
@Nullable
|
||||
Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
* @see #bLMove(byte[], byte[], Direction, Direction, double)
|
||||
*/
|
||||
@Nullable
|
||||
byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
* @see #lMove(byte[], byte[], Direction, Direction)
|
||||
*/
|
||||
@Nullable
|
||||
byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
|
||||
@@ -846,6 +846,43 @@ public interface StringRedisConnection extends RedisConnection {
|
||||
*/
|
||||
Long lInsert(String key, Position where, String pivot, String value);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
* @see #bLMove(byte[], byte[], Direction, Direction, double)
|
||||
* @see #lMove(byte[], byte[], Direction, Direction)
|
||||
*/
|
||||
@Nullable
|
||||
String lMove(String sourceKey, String destinationKey, Direction from, Direction to);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: BLMOVE</a>
|
||||
* @see #lMove(byte[], byte[], Direction, Direction)
|
||||
* @see #bLMove(byte[], byte[], Direction, Direction, double)
|
||||
*/
|
||||
@Nullable
|
||||
String bLMove(String sourceKey, String destinationKey, Direction from, Direction to, double timeout);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.jedis;
|
||||
|
||||
import redis.clients.jedis.args.ListDirection;
|
||||
import redis.clients.jedis.params.LPosParams;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -220,6 +221,46 @@ class JedisClusterListCommands implements RedisListCommands {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
try {
|
||||
return connection.getCluster().lmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
|
||||
ListDirection.valueOf(to.name()));
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
try {
|
||||
return connection.getCluster().blmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
|
||||
ListDirection.valueOf(to.name()), timeout);
|
||||
} catch (Exception ex) {
|
||||
throw convertJedisAccessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.redis.connection.jedis;
|
||||
import redis.clients.jedis.BinaryJedis;
|
||||
import redis.clients.jedis.MultiKeyPipelineBase;
|
||||
import redis.clients.jedis.Protocol;
|
||||
import redis.clients.jedis.args.ListDirection;
|
||||
import redis.clients.jedis.params.LPosParams;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -177,6 +178,38 @@ class JedisListCommands implements RedisListCommands {
|
||||
JedisConverters.toListPosition(where), pivot, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return connection.invoke().just(BinaryJedis::lmove, MultiKeyPipelineBase::lmove, sourceKey, destinationKey,
|
||||
ListDirection.valueOf(from.name()), ListDirection.valueOf(to.name()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return connection.invoke().just(BinaryJedis::blmove, MultiKeyPipelineBase::blmove, sourceKey, destinationKey,
|
||||
ListDirection.valueOf(from.name()), ListDirection.valueOf(to.name()), timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisNode.NodeType;
|
||||
@@ -1025,6 +1026,27 @@ public abstract class LettuceConverters extends Converters {
|
||||
return getUpperBound(range).orElse(INDEXED_RANGE_END);
|
||||
}
|
||||
|
||||
static LMoveArgs toLmoveArgs(Enum<?> from, Enum<?> to) {
|
||||
|
||||
if (from.name().equals(Direction.LEFT.name()) && to.name().equals(Direction.LEFT.name())) {
|
||||
return LMoveArgs.Builder.leftLeft();
|
||||
}
|
||||
|
||||
if (from.name().equals(Direction.LEFT.name()) && to.name().equals(Direction.RIGHT.name())) {
|
||||
return LMoveArgs.Builder.leftRight();
|
||||
}
|
||||
|
||||
if (from.name().equals(Direction.RIGHT.name()) && to.name().equals(Direction.LEFT.name())) {
|
||||
return LMoveArgs.Builder.rightLeft();
|
||||
}
|
||||
|
||||
if (from.name().equals(Direction.RIGHT.name()) && to.name().equals(Direction.RIGHT.name())) {
|
||||
return LMoveArgs.Builder.rightRight();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Unsupported combination of arguments: %s/%s", from, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 1.8
|
||||
|
||||
@@ -72,7 +72,8 @@ class LettuceListCommands implements RedisListCommands {
|
||||
return connection.invoke().just(RedisListAsyncCommands::lpos, key, element, count, args);
|
||||
}
|
||||
|
||||
return connection.invoke().from(RedisListAsyncCommands::lpos, key, element, args).getOrElse(Collections::singletonList, Collections::emptyList);
|
||||
return connection.invoke().from(RedisListAsyncCommands::lpos, key, element, args)
|
||||
.getOrElse(Collections::singletonList, Collections::emptyList);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -176,6 +177,39 @@ class LettuceListCommands implements RedisListCommands {
|
||||
value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return connection.invoke().just(RedisListAsyncCommands::lmove, sourceKey, destinationKey,
|
||||
LettuceConverters.toLmoveArgs(from, to));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @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)
|
||||
*/
|
||||
@Override
|
||||
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return connection.invoke(connection.getAsyncDedicatedConnection()).just(RedisListAsyncCommands::blmove, sourceKey,
|
||||
destinationKey, LettuceConverters.toLmoveArgs(from, to), timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import io.lettuce.core.LMoveArgs;
|
||||
import io.lettuce.core.LPosArgs;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -22,8 +23,10 @@ import reactor.core.publisher.Mono;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands;
|
||||
@@ -34,6 +37,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyComm
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.core.TimeoutUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -212,6 +216,50 @@ class LettuceReactiveListCommands implements ReactiveListCommands {
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lMove(Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<LMoveCommand>> lMove(Publisher<? extends LMoveCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Source key must not be null!");
|
||||
Assert.notNull(command.getFrom(), "Source direction must not be null!");
|
||||
Assert.notNull(command.getDestinationKey(), "Destination key must not be null!");
|
||||
Assert.notNull(command.getTo(), "Destination direction must not be null!");
|
||||
|
||||
LMoveArgs lMoveArgs = LettuceConverters.toLmoveArgs(command.getFrom(), command.getTo());
|
||||
|
||||
return cmd.lmove(command.getKey(), command.getDestinationKey(), lMoveArgs)
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#bLMove(Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<BLMoveCommand>> bLMove(Publisher<BLMoveCommand> commands) {
|
||||
|
||||
return connection.executeDedicated(cmd -> Flux.from(commands).concatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Source key must not be null!");
|
||||
Assert.notNull(command.getFrom(), "Source direction must not be null!");
|
||||
Assert.notNull(command.getDestinationKey(), "Destination key must not be null!");
|
||||
Assert.notNull(command.getTo(), "Destination direction must not be null!");
|
||||
Assert.notNull(command.getTimeout(), "Timeout must not be null!");
|
||||
|
||||
LMoveArgs lMoveArgs = LettuceConverters.toLmoveArgs(command.getFrom(), command.getTo());
|
||||
double timeout = TimeoutUtils.toDoubleSeconds(command.getTimeout().toMillis(), TimeUnit.MILLISECONDS);
|
||||
|
||||
return cmd.blmove(command.getKey(), command.getDestinationKey(), lMoveArgs, timeout)
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lSet(org.reactivestreams.Publisher)
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -139,6 +140,58 @@ public interface BoundListOperations<K, V> extends BoundKeyOperations<K> {
|
||||
@Nullable
|
||||
Long rightPush(V pivot, V value);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
V move(Direction from, K destinationKey, Direction to);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
V move(Direction from, K destinationKey, Direction to, Duration timeout);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
V move(Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
|
||||
@@ -15,16 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link BoundListOperations}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundListOperations<K, V> {
|
||||
|
||||
@@ -227,6 +229,33 @@ class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> impl
|
||||
return ops.rightPush(getKey(), pivot, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public V move(Direction from, K destinationKey, Direction to) {
|
||||
return ops.move(getKey(), from, destinationKey, to);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public V move(Direction from, K destinationKey, Direction to, Duration timeout) {
|
||||
return ops.move(getKey(), from, destinationKey, to, timeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public V move(Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit) {
|
||||
return ops.move(getKey(), from, destinationKey, to, timeout, unit);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.BoundListOperations#trim(long, long)
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Direction;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -363,6 +364,40 @@ class DefaultListOperations<K, V> extends AbstractOperations<K, V> implements Li
|
||||
}, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ListOperations#move(java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public V move(K sourceKey, Direction from, K destinationKey, Direction to) {
|
||||
|
||||
byte[] rawDestKey = rawKey(destinationKey);
|
||||
return execute(new ValueDeserializingRedisCallback(sourceKey) {
|
||||
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
|
||||
return connection.lMove(rawSourceKey, rawDestKey, from, to);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ListOperations#move(java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public V move(K sourceKey, Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit) {
|
||||
|
||||
byte[] rawDestKey = rawKey(destinationKey);
|
||||
return execute(new ValueDeserializingRedisCallback(sourceKey) {
|
||||
|
||||
@Override
|
||||
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
|
||||
return connection.bLMove(rawSourceKey, rawDestKey, from, to, TimeoutUtils.toDoubleSeconds(timeout, unit));
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ListOperations#set(java.lang.Object, long, java.lang.Object)
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands.Direction;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands.LPosCommand;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
@@ -199,6 +200,39 @@ class DefaultReactiveListOperations<K, V> implements ReactiveListOperations<K, V
|
||||
return createMono(connection -> connection.lInsert(rawKey(key), Position.AFTER, rawValue(pivot), rawValue(value)));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#move(K, Direction, K, Direction)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
|
||||
return createMono(
|
||||
connection -> connection.lMove(rawKey(sourceKey), rawKey(destinationKey), from, to).map(this::readValue));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#move(K, Direction, K, Direction, Duration)
|
||||
*/
|
||||
@Override
|
||||
public Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout) {
|
||||
|
||||
Assert.notNull(sourceKey, "Source key must not be null!");
|
||||
Assert.notNull(destinationKey, "Destination key must not be null!");
|
||||
Assert.notNull(from, "From direction must not be null!");
|
||||
Assert.notNull(to, "To direction must not be null!");
|
||||
Assert.notNull(timeout, "Timeout must not be null!");
|
||||
|
||||
return createMono(connection -> connection.bLMove(rawKey(sourceKey), rawKey(destinationKey), from, to, timeout)
|
||||
.map(this::readValue));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.core.ReactiveListOperations#set(java.lang.Object, long, java.lang.Object)
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
import static org.springframework.data.redis.connection.RedisListCommands.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -181,6 +183,168 @@ public interface ListOperations<K, V> {
|
||||
@Nullable
|
||||
Long rightPush(K key, V pivot, V value);
|
||||
|
||||
/**
|
||||
* Value object representing the {@code where from} part for the {@code LMOVE} command.
|
||||
*
|
||||
* @param <K>
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
class MoveFrom<K> {
|
||||
|
||||
final K key;
|
||||
final Direction direction;
|
||||
|
||||
MoveFrom(K key, Direction direction) {
|
||||
this.key = key;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public static <K> MoveFrom<K> fromHead(K key) {
|
||||
return new MoveFrom<>(key, Direction.first());
|
||||
}
|
||||
|
||||
public static <K> MoveFrom<K> fromTail(K key) {
|
||||
return new MoveFrom<>(key, Direction.last());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object representing the {@code where to} from part for the {@code LMOVE} command.
|
||||
*
|
||||
* @param <K>
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
class MoveTo<K> {
|
||||
|
||||
final K key;
|
||||
final Direction direction;
|
||||
|
||||
MoveTo(K key, Direction direction) {
|
||||
this.key = key;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public static <K> MoveTo<K> toHead(K key) {
|
||||
return new MoveTo<>(key, Direction.first());
|
||||
}
|
||||
|
||||
public static <K> MoveTo<K> toTail(K key) {
|
||||
return new MoveTo<>(key, Direction.last());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
default V move(MoveFrom<K> from, MoveTo<K> to) {
|
||||
|
||||
Assert.notNull(from, "Move from must not be null");
|
||||
Assert.notNull(to, "Move to must not be null");
|
||||
|
||||
return move(from.key, from.direction, to.key, to.direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
V move(K sourceKey, Direction from, K destinationKey, Direction to);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null} or negative.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
default V move(MoveFrom<K> from, MoveTo<K> to, Duration timeout) {
|
||||
|
||||
Assert.notNull(from, "Move from must not be null");
|
||||
Assert.notNull(to, "Move to must not be null");
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
|
||||
|
||||
return move(from.key, from.direction, to.key, to.direction,
|
||||
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null} or negative.
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
default V move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
|
||||
|
||||
return move(sourceKey, from, destinationKey, to, TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS),
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
@Nullable
|
||||
V move(K sourceKey, Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
|
||||
@@ -15,12 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.redis.core;
|
||||
|
||||
import static org.springframework.data.redis.connection.ReactiveListCommands.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.redis.core.ListOperations.MoveFrom;
|
||||
import org.springframework.data.redis.core.ListOperations.MoveTo;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Redis list specific operations.
|
||||
*
|
||||
@@ -165,6 +171,83 @@ public interface ReactiveListOperations<K, V> {
|
||||
*/
|
||||
Mono<Long> rightPush(K key, V pivot, V value);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
default Mono<V> move(MoveFrom<K> from, MoveTo<K> to) {
|
||||
|
||||
Assert.notNull(from, "Move from must not be null");
|
||||
Assert.notNull(to, "Move to must not be null");
|
||||
|
||||
return move(from.key, Direction.valueOf(from.direction.name()), to.key, Direction.valueOf(to.direction.name()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
|
||||
*/
|
||||
Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
default Mono<V> move(MoveFrom<K> from, MoveTo<K> to, Duration timeout) {
|
||||
|
||||
Assert.notNull(from, "Move from must not be null");
|
||||
Assert.notNull(to, "Move to must not be null");
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
|
||||
|
||||
return move(from.key, Direction.valueOf(from.direction.name()), to.key, Direction.valueOf(to.direction.name()),
|
||||
timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
|
||||
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
|
||||
* {@code to} argument) of the list stored at {@code destinationKey}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @return {@literal null} when used in pipeline / transaction.
|
||||
* @since 2.6
|
||||
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
|
||||
*/
|
||||
Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout);
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
|
||||
@@ -64,7 +64,26 @@ public abstract class TimeoutUtils {
|
||||
*/
|
||||
public static long toSeconds(long timeout, TimeUnit unit) {
|
||||
return roundUpIfNecessary(timeout, unit.toSeconds(timeout));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given timeout to seconds with a fraction of seconds.
|
||||
*
|
||||
* @param timeout The timeout to convert
|
||||
* @param unit The timeout's unit
|
||||
* @return The converted timeout
|
||||
* @since 2.6
|
||||
*/
|
||||
public static double toDoubleSeconds(long timeout, TimeUnit unit) {
|
||||
|
||||
switch (unit) {
|
||||
case MILLISECONDS:
|
||||
case MICROSECONDS:
|
||||
case NANOSECONDS:
|
||||
return unit.toMillis(timeout) / 1000d;
|
||||
default:
|
||||
return unit.toSeconds(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,9 +24,11 @@ import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.RedisListCommands;
|
||||
import org.springframework.data.redis.core.BoundListOperations;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation for {@link RedisList}. Suitable for not just lists, but also queues (FIFO ordering) or stacks
|
||||
@@ -36,6 +38,7 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements RedisList<E> {
|
||||
|
||||
@@ -100,6 +103,78 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
|
||||
capped = (maxSize > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.support.collections.RedisList#moveFirstTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public E moveFirstTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition) {
|
||||
|
||||
Assert.notNull(destination, "Destination must not be null");
|
||||
Assert.notNull(destinationPosition, "Destination position must not be null");
|
||||
|
||||
E result = listOps.move(RedisListCommands.Direction.first(), destination.getKey(), destinationPosition);
|
||||
potentiallyCap(destination);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.support.collections.RedisList#moveFirstTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public E moveFirstTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition, long timeout,
|
||||
TimeUnit unit) {
|
||||
|
||||
Assert.notNull(destination, "Destination must not be null");
|
||||
Assert.notNull(destinationPosition, "Destination position must not be null");
|
||||
Assert.notNull(unit, "TimeUnit must not be null");
|
||||
|
||||
E result = listOps.move(RedisListCommands.Direction.first(), destination.getKey(), destinationPosition, timeout,
|
||||
unit);
|
||||
potentiallyCap(destination);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.support.collections.RedisList#moveLastTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction)
|
||||
*/
|
||||
@Override
|
||||
public E moveLastTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition) {
|
||||
|
||||
Assert.notNull(destination, "Destination must not be null");
|
||||
Assert.notNull(destinationPosition, "Destination position must not be null");
|
||||
|
||||
E result = listOps.move(RedisListCommands.Direction.last(), destination.getKey(), destinationPosition);
|
||||
potentiallyCap(destination);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.support.collections.RedisList#moveLastTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
|
||||
*/
|
||||
@Override
|
||||
public E moveLastTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition, long timeout,
|
||||
TimeUnit unit) {
|
||||
|
||||
Assert.notNull(destination, "Destination must not be null");
|
||||
Assert.notNull(destinationPosition, "Destination position must not be null");
|
||||
Assert.notNull(unit, "TimeUnit must not be null");
|
||||
|
||||
E result = listOps.move(RedisListCommands.Direction.last(), destination.getKey(), destinationPosition, timeout,
|
||||
unit);
|
||||
potentiallyCap(destination);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void potentiallyCap(RedisList<E> destination) {
|
||||
if (destination instanceof DefaultRedisList) {
|
||||
((DefaultRedisList<Object>) destination).cap();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.support.collections.RedisList#range(long, long)
|
||||
|
||||
@@ -15,19 +15,146 @@
|
||||
*/
|
||||
package org.springframework.data.redis.support.collections;
|
||||
|
||||
import static org.springframework.data.redis.connection.RedisListCommands.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.BlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.core.TimeoutUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Redis extension for the {@link List} contract. Supports {@link List}, {@link Queue} and {@link Deque} contracts as
|
||||
* well as their equivalent blocking siblings {@link BlockingDeque} and {@link BlockingDeque}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque<E> {
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveFirstTo(RedisList<E> destination, Direction destinationPosition);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null} or negative.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
default E moveFirstTo(RedisList<E> destination, Direction destinationPosition, Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
|
||||
|
||||
return moveFirstTo(destination, destinationPosition,
|
||||
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveFirstTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveLastTo(RedisList<E> destination, Direction destinationPosition);
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null} or negative.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
default E moveLastTo(RedisList<E> destination, Direction destinationPosition, Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Timeout must not be null");
|
||||
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
|
||||
|
||||
return moveLastTo(destination, destinationPosition,
|
||||
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
|
||||
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
|
||||
* at {@link RedisList destination}.
|
||||
* <p/>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param destination must not be {@literal null}.
|
||||
* @param destinationPosition must not be {@literal null}.
|
||||
* @param timeout
|
||||
* @param unit must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.6
|
||||
* @see Direction#first()
|
||||
* @see Direction#last()
|
||||
*/
|
||||
@Nullable
|
||||
E moveLastTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
|
||||
|
||||
List<E> range(long begin, long end);
|
||||
|
||||
RedisList<E> trim(int begin, int end);
|
||||
|
||||
Reference in New Issue
Block a user