diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 94ef52e215..dcffca6d03 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -24,6 +24,7 @@ import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.Protocol.Keyword; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.params.*; @@ -719,6 +720,14 @@ public void sort(final byte[] key, final SortingParams sortingParameters) { sendCommand(SORT, args.toArray(new byte[args.size()][])); } + public void lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { + sendCommand(LMOVE, srcKey, dstKey, from.getRaw(), to.getRaw()); + } + + public void blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, int timeout) { + sendCommand(BLMOVE, srcKey, dstKey, from.getRaw(), to.getRaw(), toByteArray(timeout)); + } + public void blpop(final byte[][] args) { sendCommand(BLPOP, args); } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index e9abd97d58..1ef7b80bf9 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -21,6 +21,7 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.commands.AdvancedBinaryJedisCommands; @@ -2370,6 +2371,42 @@ public List sort(final byte[] key, final SortingParams sortingParameters return client.getBinaryMultiBulkReply(); } + /** + * Pop an element from a list, push it to another list and return it + * @param srcKey + * @param dstKey + * @param from + * @param to + * @return + */ + @Override + public byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { + checkIsInMultiOrPipeline(); + client.lmove(srcKey, dstKey, from, to); + return client.getBinaryBulkReply(); + } + + /** + * Pop an element from a list, push it to another list and return it; or block until one is available + * @param srcKey + * @param dstKey + * @param from + * @param to + * @param timeout + * @return + */ + @Override + public byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, int timeout) { + checkIsInMultiOrPipeline(); + client.blmove(srcKey, dstKey, from, to, timeout); + client.setTimeoutInfinite(); + try { + return client.getBinaryBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCluster.java b/src/main/java/redis/clients/jedis/BinaryJedisCluster.java index fb8eb5d596..8e1eef2cef 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedisCluster.java +++ b/src/main/java/redis/clients/jedis/BinaryJedisCluster.java @@ -1,5 +1,6 @@ package redis.clients.jedis; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.commands.BinaryJedisClusterCommands; import redis.clients.jedis.commands.JedisClusterBinaryScriptingCommands; @@ -1752,6 +1753,28 @@ public Long execute(Jedis connection) { }.runBinary(keys.length, keys); } + @Override + public byte[] lmove(final byte[] srcKey, final byte[] dstKey, final ListDirection from, + final ListDirection to) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.lmove(srcKey, dstKey, from, to); + } + }.runBinary(2, srcKey, dstKey); + } + + @Override + public byte[] blmove(final byte[] srcKey, final byte[] dstKey, final ListDirection from, + final ListDirection to, final int timeout) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public byte[] execute(Jedis connection) { + return connection.blmove(srcKey, dstKey, from, to, timeout); + } + }.runBinary(2, srcKey, dstKey); + } + @Override public List blpop(final int timeout, final byte[]... keys) { return new JedisClusterCommand>(connectionHandler, maxAttempts) { diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 4b43b13443..591d49335c 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -13,6 +13,7 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.commands.Commands; import redis.clients.jedis.params.*; import redis.clients.jedis.util.SafeEncoder; @@ -610,6 +611,16 @@ public void sort(final String key, final SortingParams sortingParameters) { sort(SafeEncoder.encode(key), sortingParameters); } + @Override + public void lmove(String srcKey, String dstKey, ListDirection from, ListDirection to) { + lmove(SafeEncoder.encode(srcKey), SafeEncoder.encode(dstKey), from, to); + } + + @Override + public void blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, int timeout) { + blmove(SafeEncoder.encode(srcKey), SafeEncoder.encode(dstKey), from, to, timeout); + } + @Override public void blpop(final String[] args) { blpop(SafeEncoder.encodeMany(args)); diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 760b6c35c4..033996d23e 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -13,6 +13,7 @@ import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.commands.*; import redis.clients.jedis.params.*; import redis.clients.jedis.args.UnblockType; @@ -2027,6 +2028,27 @@ public List sort(final String key, final SortingParams sortingParameters return client.getMultiBulkReply(); } + @Override + public String lmove(final String srcKey, final String dstKey, final ListDirection from, + final ListDirection to) { + checkIsInMultiOrPipeline(); + client.lmove(srcKey, dstKey, from, to); + return client.getBulkReply(); + } + + @Override + public String blmove(final String srcKey, final String dstKey, final ListDirection from, + final ListDirection to, final int timeout) { + checkIsInMultiOrPipeline(); + client.blmove(srcKey, dstKey, from, to, timeout); + client.setTimeoutInfinite(); + try { + return client.getBulkReply(); + } finally { + client.rollbackTimeout(); + } + } + /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 9d4c0220c5..adc27fd4f6 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1,5 +1,6 @@ package redis.clients.jedis; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.commands.JedisClusterCommands; import redis.clients.jedis.commands.JedisClusterScriptingCommands; import redis.clients.jedis.commands.MultiKeyJedisClusterCommands; @@ -1819,6 +1820,27 @@ public Long execute(Jedis connection) { }.run(keys.length, keys); } + @Override + public String lmove(String srcKey, String dstKey, ListDirection from, ListDirection to) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.lmove(srcKey, dstKey, from, to); + } + }.run(2, srcKey, dstKey); + } + + @Override + public String blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, + int timeout) { + return new JedisClusterCommand(connectionHandler, maxAttempts) { + @Override + public String execute(Jedis connection) { + return connection.blmove(srcKey, dstKey, from, to, timeout); + } + }.run(2, srcKey, dstKey); + } + @Override public List blpop(final int timeout, final String... keys) { return new JedisClusterCommand>(connectionHandler, maxAttempts) { diff --git a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java index 3e508e371e..7c9a64db62 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java +++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java @@ -1,5 +1,6 @@ package redis.clients.jedis; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.commands.*; import redis.clients.jedis.params.*; @@ -14,6 +15,34 @@ public abstract class MultiKeyPipelineBase extends PipelineBase implements protected Client client = null; + @Override + public Response lmove(byte[] srcKey, byte[] dstKey, ListDirection from, + ListDirection to) { + client.lmove(srcKey, dstKey, from, to); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, + int timeout) { + client.blmove(srcKey, dstKey, from, to, timeout); + return getResponse(BuilderFactory.BYTE_ARRAY); + } + + @Override + public Response lmove(String srcKey, String dstKey, ListDirection from, + ListDirection to) { + client.lmove(srcKey, dstKey, from, to); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, + int timeout) { + client.blmove(srcKey, dstKey, from, to, timeout); + return getResponse(BuilderFactory.STRING); + } + @Override public Response> brpop(String... args) { client.brpop(args); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 0e6e79ed5f..4b1f55cd8c 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -264,7 +264,7 @@ public static enum Command implements ProtocolCommand { READONLY, GEOADD, GEODIST, GEOHASH, GEOPOS, GEORADIUS, GEORADIUS_RO, GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, MODULE, BITFIELD, HSTRLEN, TOUCH, SWAPDB, MEMORY, XADD, XLEN, XDEL, XTRIM, XRANGE, XREVRANGE, XREAD, XACK, XGROUP, XREADGROUP, XPENDING, XCLAIM, ACL, XINFO, - BITFIELD_RO, LPOS, SMISMEMBER, ZMSCORE, BZPOPMIN, BZPOPMAX; + BITFIELD_RO, LPOS, SMISMEMBER, ZMSCORE, BZPOPMIN, BZPOPMAX, BLMOVE, LMOVE; private final byte[] raw; diff --git a/src/main/java/redis/clients/jedis/args/ListDirection.java b/src/main/java/redis/clients/jedis/args/ListDirection.java new file mode 100644 index 0000000000..0c5d71c766 --- /dev/null +++ b/src/main/java/redis/clients/jedis/args/ListDirection.java @@ -0,0 +1,21 @@ +package redis.clients.jedis.args; + +import redis.clients.jedis.util.SafeEncoder; + +/** + * Direction for {@code LMOVE} and {@code BLMOVE} command. + */ +public enum ListDirection implements Rawable { + LEFT, RIGHT; + + private final byte[] raw; + + ListDirection() { + raw = SafeEncoder.encode(this.name()); + } + + @Override + public byte[] getRaw() { + return raw; + } +} diff --git a/src/main/java/redis/clients/jedis/commands/Commands.java b/src/main/java/redis/clients/jedis/commands/Commands.java index a1f4cab597..1bcf5f8d5d 100644 --- a/src/main/java/redis/clients/jedis/commands/Commands.java +++ b/src/main/java/redis/clients/jedis/commands/Commands.java @@ -8,6 +8,7 @@ import redis.clients.jedis.ListPosition; import redis.clients.jedis.ScanParams; import redis.clients.jedis.SortingParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.ZParams; import redis.clients.jedis.params.GetExParams; @@ -274,6 +275,10 @@ default void setex(String key, int seconds, String value) { void sort(String key, SortingParams sortingParameters); + void lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); + + void blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, int timeout); + void blpop(String[] args); void sort(String key, SortingParams sortingParameters, String dstkey); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryCommands.java index 2958a385b6..b75e43109e 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryCommands.java @@ -7,6 +7,7 @@ import redis.clients.jedis.SortingParams; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.XReadGroupParams; @@ -24,6 +25,10 @@ public interface MultiKeyBinaryCommands { Long exists(byte[]... keys); + byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to); + + byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, int timeout); + List blpop(int timeout, byte[]... keys); List brpop(int timeout, byte[]... keys); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java index 86946fc3de..efa5c834a8 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryJedisClusterCommands.java @@ -9,6 +9,7 @@ import redis.clients.jedis.SortingParams; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.XReadGroupParams; @@ -26,6 +27,10 @@ public interface MultiKeyBinaryJedisClusterCommands { Long exists(byte[]... keys); + byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to); + + byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, int timeout); + List blpop(int timeout, byte[]... keys); List brpop(int timeout, byte[]... keys); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java index 48cfb4fc81..7c6721f3d4 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyBinaryRedisPipeline.java @@ -7,6 +7,7 @@ import redis.clients.jedis.SortingParams; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.*; import java.util.List; @@ -24,6 +25,10 @@ public interface MultiKeyBinaryRedisPipeline { Response exists(byte[]... keys); + Response lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to); + + Response blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, int timeout); + Response> blpop(byte[]... args); Response> brpop(byte[]... args); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyCommands.java b/src/main/java/redis/clients/jedis/commands/MultiKeyCommands.java index bf0fe757af..0d75511f1f 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyCommands.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyCommands.java @@ -11,6 +11,7 @@ import redis.clients.jedis.StreamEntry; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.XReadGroupParams; @@ -27,6 +28,10 @@ public interface MultiKeyCommands { Long exists(String... keys); + String lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); + + String blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, int timeout); + List blpop(int timeout, String... keys); List brpop(int timeout, String... keys); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java b/src/main/java/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java index 2eb16346f0..b5a9375c34 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyCommandsPipeline.java @@ -9,6 +9,7 @@ import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.*; import java.util.List; @@ -25,6 +26,10 @@ public interface MultiKeyCommandsPipeline { Response exists(String... keys); + Response lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); + + Response blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, int timeout); + Response> blpop(String... args); Response> brpop(String... args); diff --git a/src/main/java/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java b/src/main/java/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java index 2620cc0cc3..864cfde40c 100644 --- a/src/main/java/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java +++ b/src/main/java/redis/clients/jedis/commands/MultiKeyJedisClusterCommands.java @@ -11,6 +11,7 @@ import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.XReadGroupParams; @@ -28,6 +29,10 @@ public interface MultiKeyJedisClusterCommands { Long exists(String... keys); + String lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); + + String blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, int timeout); + List blpop(int timeout, String... keys); List brpop(int timeout, String... keys); diff --git a/src/test/java/redis/clients/jedis/tests/commands/ListCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/ListCommandsTest.java index cec9f5bd68..fbaccbc378 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/ListCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/ListCommandsTest.java @@ -16,6 +16,7 @@ import redis.clients.jedis.Jedis; import redis.clients.jedis.ListPosition; +import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.LPosParams; @@ -654,4 +655,51 @@ public void lpos() { assertEquals(expected.subList(1, 2), posList); } + + @Test + public void lmove() { + jedis.rpush("foo", "bar1", "bar2", "bar3"); + assertEquals("bar3", jedis.lmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT)); + assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); + assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); + + // Binary + jedis.rpush(bfoo, b1, b2, b3); + assertArrayEquals(b3, jedis.lmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT)); + assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); + assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); + } + + @Test + public void blmove() { + new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + try (Jedis j = createJedis()) { + j.rpush("foo", "bar1", "bar2", "bar3"); + } + }).start(); + + assertEquals("bar3", jedis.blmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT, 0)); + assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); + assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); + + // Binary + new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // ignore + } + try (Jedis j = createJedis()) { + j.rpush(bfoo, b1, b2, b3); + } + }).start(); + assertArrayEquals(b3, jedis.blmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT, 0)); + assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); + assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); + } }