Skip to content

Commit

Permalink
ZMPOP and LMPOP (#2094)
Browse files Browse the repository at this point in the history
Implementing [`ZMPOP`](https://redis.io/commands/zmpop/) and [`LMPOP`](https://redis.io/commands/lmpop/) as part of #2055 

Co-authored-by: Nick Craver <[email protected]>
  • Loading branch information
slorello89 and NickCraver authored Apr 19, 2022
1 parent e4a88dd commit dbd52f1
Show file tree
Hide file tree
Showing 14 changed files with 599 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
- Adds: Support for `GEOSEARCH` with `.GeoSearch()`/`.GeoSearchAsync()` ([#2089 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2089))
- Adds: Support for `GEOSEARCHSTORE` with `.GeoSearchAndStore()`/`.GeoSearchAndStoreAsync()` ([#2089 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2089))
- Adds: Support for `HRANDFIELD` with `.HashRandomField()`/`.HashRandomFieldAsync()`, `.HashRandomFields()`/`.HashRandomFieldsAsync()`, and `.HashRandomFieldsWithValues()`/`.HashRandomFieldsWithValuesAsync()` ([#2090 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2090))
- Adds: Support for `LMPOP` with `.ListLeftPop()`/`.ListLeftPopAsync()` and `.ListRightPop()`/`.ListRightPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
- Adds: Support for `ZMPOP` with `.SortedSetPop()`/`.SortedSetPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
- Adds: Support for `XAUTOCLAIM` with `.StreamAutoClaim()`/.`StreamAutoClaimAsync()` and `.StreamAutoClaimIdsOnly()`/.`StreamAutoClaimIdsOnlyAsync()` ([#2095 by ttingen](https://github.com/StackExchange/StackExchange.Redis/pull/2095))

## 2.5.61
Expand Down
35 changes: 35 additions & 0 deletions src/StackExchange.Redis/APITypes/ListPopResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;

namespace StackExchange.Redis;

/// <summary>
/// A contiguous portion of a redis list.
/// </summary>
public readonly struct ListPopResult
{
/// <summary>
/// A null ListPopResult, indicating no results.
/// </summary>
public static ListPopResult Null { get; } = new ListPopResult(RedisKey.Null, Array.Empty<RedisValue>());

/// <summary>
/// Whether this object is null/empty.
/// </summary>
public bool IsNull => Key.IsNull && Values == Array.Empty<RedisValue>();

/// <summary>
/// The key of the list that this set of entries came form.
/// </summary>
public RedisKey Key { get; }

/// <summary>
/// The values from the list.
/// </summary>
public RedisValue[] Values { get; }

internal ListPopResult(RedisKey key, RedisValue[] values)
{
Key = key;
Values = values;
}
}
35 changes: 35 additions & 0 deletions src/StackExchange.Redis/APITypes/SortedSetPopResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;

namespace StackExchange.Redis;

/// <summary>
/// A contiguous portion of a redis sorted set.
/// </summary>
public readonly struct SortedSetPopResult
{
/// <summary>
/// A null SortedSetPopResult, indicating no results.
/// </summary>
public static SortedSetPopResult Null { get; } = new SortedSetPopResult(RedisKey.Null, Array.Empty<SortedSetEntry>());

/// <summary>
/// Whether this object is null/empty.
/// </summary>
public bool IsNull => Key.IsNull && Entries == Array.Empty<SortedSetEntry>();

/// <summary>
/// The key of the sorted set these entries came form.
/// </summary>
public RedisKey Key { get; }

/// <summary>
/// The provided entries of the sorted set.
/// </summary>
public SortedSetEntry[] Entries { get; }

internal SortedSetPopResult(RedisKey key, SortedSetEntry[] entries)
{
Key = key;
Entries = entries;
}
}
4 changes: 4 additions & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ internal enum RedisCommand
LINSERT,
LLEN,
LMOVE,
LMPOP,
LPOP,
LPOS,
LPUSH,
Expand Down Expand Up @@ -212,6 +213,7 @@ internal enum RedisCommand
ZINTERCARD,
ZINTERSTORE,
ZLEXCOUNT,
ZMPOP,
ZMSCORE,
ZPOPMAX,
ZPOPMIN,
Expand Down Expand Up @@ -284,6 +286,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.INCRBYFLOAT:
case RedisCommand.LINSERT:
case RedisCommand.LMOVE:
case RedisCommand.LMPOP:
case RedisCommand.LPOP:
case RedisCommand.LPUSH:
case RedisCommand.LPUSHX:
Expand Down Expand Up @@ -335,6 +338,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.ZDIFFSTORE:
case RedisCommand.ZINTERSTORE:
case RedisCommand.ZINCRBY:
case RedisCommand.ZMPOP:
case RedisCommand.ZPOPMAX:
case RedisCommand.ZPOPMIN:
case RedisCommand.ZRANGESTORE:
Expand Down
34 changes: 34 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,17 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks><seealso href="https://redis.io/commands/lpop"/></remarks>
RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
/// Starts on the left side of the list.
/// </summary>
/// <param name="keys">The keys to look through for elements to pop.</param>
/// <param name="count">The maximum number of elements to pop from the list.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
ListPopResult ListLeftPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Scans through the list stored at <paramref name="key"/> looking for <paramref name="element"/>, returning the 0-based
/// index of the first matching element.
Expand Down Expand Up @@ -1065,6 +1076,17 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks><seealso href="https://redis.io/commands/rpop"/></remarks>
RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
/// Starts on the right side of the list.
/// </summary>
/// <param name="keys">The keys to look through for elements to pop.</param>
/// <param name="count">The maximum number of elements to pop from the list.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
ListPopResult ListRightPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at the first element (head) of the list stored at destination.
/// </summary>
Expand Down Expand Up @@ -2110,6 +2132,18 @@ IEnumerable<SortedSetEntry> SortedSetScan(RedisKey key,
/// </remarks>
SortedSetEntry[] SortedSetPop(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns up to <paramref name="count"/> entries from the first non-empty sorted set in <paramref name="keys"/>.
/// Returns <see cref="SortedSetPopResult.Null"/> if none of the sets exist or contain any elements.
/// </summary>
/// <param name="keys">The keys to check.</param>
/// <param name="count">The maximum number of records to pop out of the sorted set.</param>
/// <param name="order">The order to sort by when popping items out of the set.</param>
/// <param name="flags">The flags to use for the operation.</param>
/// <returns>A contiguous collection of sorted set entries with the key they were popped from, or <see cref="SortedSetPopResult.Null"/> if no non-empty sorted sets are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/zmpop"/></remarks>
SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged.
/// </summary>
Expand Down
34 changes: 34 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,17 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks><seealso href="https://redis.io/commands/lpop"/></remarks>
Task<RedisValue[]> ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
/// Starts on the left side of the list.
/// </summary>
/// <param name="keys">The keys to look through for elements to pop.</param>
/// <param name="count">The maximum number of elements to pop from the list.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
Task<ListPopResult> ListLeftPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Scans through the list stored at <paramref name="key"/> looking for <paramref name="element"/>, returning the 0-based
/// index of the first matching element.
Expand Down Expand Up @@ -1041,6 +1052,17 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks><seealso href="https://redis.io/commands/rpop"/></remarks>
Task<RedisValue[]> ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
/// Starts on the right side of the list.
/// </summary>
/// <param name="keys">The keys to look through for elements to pop.</param>
/// <param name="count">The maximum number of elements to pop from the list.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
Task<ListPopResult> ListRightPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at the first element (head) of the list stored at destination.
/// </summary>
Expand Down Expand Up @@ -2063,6 +2085,18 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(RedisKey key,
/// </remarks>
Task<SortedSetEntry[]> SortedSetPopAsync(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns up to <paramref name="count"/> entries from the first non-empty sorted set in <paramref name="keys"/>.
/// Returns <see cref="SortedSetPopResult.Null"/> if none of the sets exist or contain any elements.
/// </summary>
/// <param name="keys">The keys to check.</param>
/// <param name="count">The maximum number of records to pop out of the sorted set.</param>
/// <param name="order">The order to sort by when popping items out of the set.</param>
/// <param name="flags">The flags to use for the operation.</param>
/// <returns>A contiguous collection of sorted set entries with the key they were popped from, or <see cref="SortedSetPopResult.Null"/> if no non-empty sorted sets are found.</returns>
/// <remarks><seealso href="https://redis.io/commands/zmpop"/></remarks>
Task<SortedSetPopResult> SortedSetPopAsync(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.No
public RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListLeftPop(ToInner(key), count, flags);

public ListPopResult ListLeftPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListLeftPop(ToInner(keys), count, flags);

public long ListPosition(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None) =>
Inner.ListPosition(ToInner(key), element, rank, maxLength, flags);

Expand Down Expand Up @@ -260,6 +263,9 @@ public RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.N
public RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPop(ToInner(key), count, flags);

public ListPopResult ListRightPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPop(ToInner(keys), count, flags);

public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPopLeftPush(ToInner(source), ToInner(destination), flags);

Expand Down Expand Up @@ -484,6 +490,9 @@ public long SortedSetRemoveRangeByValue(RedisKey key, RedisValue min, RedisValue
public SortedSetEntry[] SortedSetPop(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetPop(ToInner(key), count, order, flags);

public SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetPop(ToInner(keys), count, order, flags);

public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None) =>
Inner.StreamAcknowledge(ToInner(key), groupName, messageId, flags);

Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ public Task<RedisValue> ListLeftPopAsync(RedisKey key, CommandFlags flags = Comm
public Task<RedisValue[]> ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListLeftPopAsync(ToInner(key), count, flags);

public Task<ListPopResult> ListLeftPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListLeftPopAsync(ToInner(keys), count, flags);

public Task<long> ListPositionAsync(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None) =>
Inner.ListPositionAsync(ToInner(key), element, rank, maxLength, flags);

Expand Down Expand Up @@ -271,6 +274,9 @@ public Task<RedisValue> ListRightPopAsync(RedisKey key, CommandFlags flags = Com
public Task<RedisValue[]> ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPopAsync(ToInner(key), count, flags);

public Task<ListPopResult> ListRightPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPopAsync(ToInner(keys), count, flags);

public Task<RedisValue> ListRightPopLeftPushAsync(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) =>
Inner.ListRightPopLeftPushAsync(ToInner(source), ToInner(destination), flags);

Expand Down Expand Up @@ -501,6 +507,9 @@ public IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(RedisKey key, RedisVa
public Task<SortedSetEntry[]> SortedSetPopAsync(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetPopAsync(ToInner(key), count, order, flags);

public Task<SortedSetPopResult> SortedSetPopAsync(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetPopAsync(ToInner(keys), count, order, flags);

public Task<long> StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None) =>
Inner.StreamAcknowledgeAsync(ToInner(key), groupName, messageId, flags);

Expand Down
Loading

0 comments on commit dbd52f1

Please sign in to comment.