Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support LMOVE #2065

Merged
merged 13 commits into from
Apr 10, 2022
17 changes: 17 additions & 0 deletions src/StackExchange.Redis/Enums/ListSide.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace StackExchange.Redis
{
/// <summary>
/// Specifies what side of the list to refer to.
/// </summary>
public enum ListSide
{
/// <summary>
/// The head of the list.
/// </summary>
Left,
/// <summary>
/// The tail of the list.
/// </summary>
Right,
}
}
1 change: 1 addition & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ internal enum RedisCommand
LINDEX,
LINSERT,
LLEN,
LMOVE,
LPOP,
LPUSH,
LPUSHX,
Expand Down
13 changes: 13 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,19 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>https://redis.io/commands/llen</remarks>
long ListLength(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns and removes the first or last element of the list stored at <paramref name="sourceKey"/>, and pushes the element
/// as the first or last element of the list stored at <paramref name="destinationKey"/>.
/// </summary>
/// <param name="sourceKey">The key of the list to remove from.</param>
/// <param name="destinationKey">The key of the list to move to.</param>
/// <param name="whereFrom">What side of the <paramref name="sourceKey"/> list to remove from.</param>
/// <param name="whereTo">What side of the <paramref name="destinationKey"/> list to move to.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The element being popped and pushed or null if there is no element to move.</returns>
/// <remarks>https://redis.io/commands/lmove</remarks>
RedisValue ListMove(RedisKey sourceKey, RedisKey destinationKey, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the specified elements of the list stored at key.
/// The offsets start and stop are zero-based indexes, with 0 being the first element of the list (the head of the list), 1 being the next element and so on.
Expand Down
13 changes: 13 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,19 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>https://redis.io/commands/llen</remarks>
Task<long> ListLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns and removes the first/last element of the list stored at source, and pushes the element
/// at the first/last element of the list stored at destination.
/// </summary>
/// <param name="sourceKey">The key of the list to remove from.</param>
/// <param name="destinationKey">The key of the list to move to.</param>
/// <param name="whereFrom">What side of the list to remove from.</param>
/// <param name="whereTo">What side of the list to move to.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The element being popped and pushed or null if there is no element to move.</returns>
/// <remarks>https://redis.io/commands/lmove</remarks>
Avital-Fine marked this conversation as resolved.
Show resolved Hide resolved
Task<RedisValue> ListMoveAsync(RedisKey sourceKey, RedisKey destinationKey, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the specified elements of the list stored at key.
/// The offsets start and stop are zero-based indexes, with 0 being the first element of the list (the head of the list), 1 being the next element and so on.
Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ public long ListLeftPush(RedisKey key, RedisValue value, When when = When.Always
public long ListLength(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.ListLength(ToInner(key), flags);

public RedisValue ListMove(RedisKey source, RedisKey destination, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None) =>
Inner.ListMove(ToInner(source), ToInner(destination), whereFrom, whereTo);

public RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None) =>
Inner.ListRange(ToInner(key), start, stop, flags);

Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ public Task<long> ListLeftPushAsync(RedisKey key, RedisValue value, When when =
public Task<long> ListLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.ListLengthAsync(ToInner(key), flags);

public Task<RedisValue> ListMoveAsync(RedisKey sourceKey, RedisKey destinationKey, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None) =>
Inner.ListMoveAsync(ToInner(sourceKey), ToInner(destinationKey), whereFrom, whereTo);

public Task<RedisValue[]> ListRangeAsync(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None) =>
Inner.ListRangeAsync(ToInner(key), start, stop, flags);

Expand Down
6 changes: 6 additions & 0 deletions src/StackExchange.Redis/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,9 @@ StackExchange.Redis.GeoUnit.Feet = 3 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Kilometers = 1 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Meters = 0 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Miles = 2 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.ListSide
StackExchange.Redis.ListSide.Left = 0 -> StackExchange.Redis.ListSide
StackExchange.Redis.ListSide.Right = 1 -> StackExchange.Redis.ListSide
StackExchange.Redis.HashEntry
StackExchange.Redis.HashEntry.Equals(StackExchange.Redis.HashEntry other) -> bool
StackExchange.Redis.HashEntry.HashEntry() -> void
Expand Down Expand Up @@ -537,6 +540,7 @@ StackExchange.Redis.IDatabase.KeyType(StackExchange.Redis.RedisKey key, StackExc
StackExchange.Redis.IDatabase.ListGetByIndex(StackExchange.Redis.RedisKey key, long index, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue
StackExchange.Redis.IDatabase.ListInsertAfter(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pivot, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.ListInsertBefore(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pivot, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.ListMove(StackExchange.Redis.RedisKey sourceKey, StackExchange.Redis.RedisKey destinationKey, StackExchange.Redis.ListSide whereFrom, StackExchange.Redis.ListSide whereTo, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue
StackExchange.Redis.IDatabase.ListLeftPop(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue[]!
StackExchange.Redis.IDatabase.ListLeftPop(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue
StackExchange.Redis.IDatabase.ListLeftPush(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
Expand Down Expand Up @@ -726,6 +730,7 @@ StackExchange.Redis.IDatabaseAsync.KeyTypeAsync(StackExchange.Redis.RedisKey key
StackExchange.Redis.IDatabaseAsync.ListGetByIndexAsync(StackExchange.Redis.RedisKey key, long index, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue>!
StackExchange.Redis.IDatabaseAsync.ListInsertAfterAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pivot, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
StackExchange.Redis.IDatabaseAsync.ListInsertBeforeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pivot, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
StackExchange.Redis.IDatabaseAsync.ListMoveAsync(StackExchange.Redis.RedisKey sourceKey, StackExchange.Redis.RedisKey destinationKey, StackExchange.Redis.ListSide whereFrom, StackExchange.Redis.ListSide whereTo, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue>!
StackExchange.Redis.IDatabaseAsync.ListLeftPopAsync(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue[]!>!
StackExchange.Redis.IDatabaseAsync.ListLeftPopAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue>!
StackExchange.Redis.IDatabaseAsync.ListLeftPushAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
Expand Down Expand Up @@ -1161,6 +1166,7 @@ StackExchange.Redis.RedisFeatures.MultipleRandom.get -> bool
StackExchange.Redis.RedisFeatures.Persist.get -> bool
StackExchange.Redis.RedisFeatures.PushIfNotExists.get -> bool
StackExchange.Redis.RedisFeatures.PushMultiple.get -> bool
StackExchange.Redis.RedisFeatures.ListMove.get -> bool
StackExchange.Redis.RedisFeatures.RedisFeatures() -> void
StackExchange.Redis.RedisFeatures.RedisFeatures(System.Version! version) -> void
StackExchange.Redis.RedisFeatures.ReplicaCommands.get -> bool
Expand Down
12 changes: 12 additions & 0 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,18 @@ public Task<long> ListLengthAsync(RedisKey key, CommandFlags flags = CommandFlag
return ExecuteAsync(msg, ResultProcessor.Int64);
}

public RedisValue ListMove(RedisKey sourceKey, RedisKey destinationKey, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.LMOVE, sourceKey, destinationKey, whereFrom == ListSide.Left ? "Left" : "right", whereTo == ListSide.Left ? "Left" : "right");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be consistent in Left vs right here :)

return ExecuteSync(msg, ResultProcessor.RedisValue);
}

public Task<RedisValue> ListMoveAsync(RedisKey sourceKey, RedisKey destinationKey, ListSide whereFrom, ListSide whereTo, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.LMOVE, sourceKey, destinationKey, whereFrom == ListSide.Left ? "Left" : "right", whereTo == ListSide.Left ? "Left" : "right");
return ExecuteAsync(msg, ResultProcessor.RedisValue);
}

public RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.LRANGE, key, start, stop);
Expand Down
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/RedisFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ public RedisFeatures(Version version)
/// </summary>
public bool PushMultiple => Version >= v4_0_0;

/// <summary>
/// Is LMOVE available?
/// </summary>
public bool ListMove => Version >= v6_2_0;
NickCraver marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// The Redis version of the server
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,13 @@ public void ListLength()
mock.Verify(_ => _.ListLength("prefix:key", CommandFlags.None));
}

[Fact]
public void ListMove()
{
wrapper.ListMove("key", "destination", ListSide.Left, ListSide.Right, CommandFlags.None);
mock.Verify(_ => _.ListMove("prefix:key", "prefix:destination", ListSide.Left, ListSide.Right, CommandFlags.None));
}

[Fact]
public void ListRange()
{
Expand Down
Loading