Skip to content

Commit dbd52f1

Browse files
ZMPOP and LMPOP (#2094)
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]>
1 parent e4a88dd commit dbd52f1

File tree

14 files changed

+599
-0
lines changed

14 files changed

+599
-0
lines changed

docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
- Adds: Support for `GEOSEARCH` with `.GeoSearch()`/`.GeoSearchAsync()` ([#2089 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2089))
2424
- Adds: Support for `GEOSEARCHSTORE` with `.GeoSearchAndStore()`/`.GeoSearchAndStoreAsync()` ([#2089 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2089))
2525
- Adds: Support for `HRANDFIELD` with `.HashRandomField()`/`.HashRandomFieldAsync()`, `.HashRandomFields()`/`.HashRandomFieldsAsync()`, and `.HashRandomFieldsWithValues()`/`.HashRandomFieldsWithValuesAsync()` ([#2090 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2090))
26+
- Adds: Support for `LMPOP` with `.ListLeftPop()`/`.ListLeftPopAsync()` and `.ListRightPop()`/`.ListRightPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
27+
- Adds: Support for `ZMPOP` with `.SortedSetPop()`/`.SortedSetPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
2628
- Adds: Support for `XAUTOCLAIM` with `.StreamAutoClaim()`/.`StreamAutoClaimAsync()` and `.StreamAutoClaimIdsOnly()`/.`StreamAutoClaimIdsOnlyAsync()` ([#2095 by ttingen](https://github.com/StackExchange/StackExchange.Redis/pull/2095))
2729

2830
## 2.5.61
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
3+
namespace StackExchange.Redis;
4+
5+
/// <summary>
6+
/// A contiguous portion of a redis list.
7+
/// </summary>
8+
public readonly struct ListPopResult
9+
{
10+
/// <summary>
11+
/// A null ListPopResult, indicating no results.
12+
/// </summary>
13+
public static ListPopResult Null { get; } = new ListPopResult(RedisKey.Null, Array.Empty<RedisValue>());
14+
15+
/// <summary>
16+
/// Whether this object is null/empty.
17+
/// </summary>
18+
public bool IsNull => Key.IsNull && Values == Array.Empty<RedisValue>();
19+
20+
/// <summary>
21+
/// The key of the list that this set of entries came form.
22+
/// </summary>
23+
public RedisKey Key { get; }
24+
25+
/// <summary>
26+
/// The values from the list.
27+
/// </summary>
28+
public RedisValue[] Values { get; }
29+
30+
internal ListPopResult(RedisKey key, RedisValue[] values)
31+
{
32+
Key = key;
33+
Values = values;
34+
}
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
3+
namespace StackExchange.Redis;
4+
5+
/// <summary>
6+
/// A contiguous portion of a redis sorted set.
7+
/// </summary>
8+
public readonly struct SortedSetPopResult
9+
{
10+
/// <summary>
11+
/// A null SortedSetPopResult, indicating no results.
12+
/// </summary>
13+
public static SortedSetPopResult Null { get; } = new SortedSetPopResult(RedisKey.Null, Array.Empty<SortedSetEntry>());
14+
15+
/// <summary>
16+
/// Whether this object is null/empty.
17+
/// </summary>
18+
public bool IsNull => Key.IsNull && Entries == Array.Empty<SortedSetEntry>();
19+
20+
/// <summary>
21+
/// The key of the sorted set these entries came form.
22+
/// </summary>
23+
public RedisKey Key { get; }
24+
25+
/// <summary>
26+
/// The provided entries of the sorted set.
27+
/// </summary>
28+
public SortedSetEntry[] Entries { get; }
29+
30+
internal SortedSetPopResult(RedisKey key, SortedSetEntry[] entries)
31+
{
32+
Key = key;
33+
Entries = entries;
34+
}
35+
}

src/StackExchange.Redis/Enums/RedisCommand.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ internal enum RedisCommand
9090
LINSERT,
9191
LLEN,
9292
LMOVE,
93+
LMPOP,
9394
LPOP,
9495
LPOS,
9596
LPUSH,
@@ -212,6 +213,7 @@ internal enum RedisCommand
212213
ZINTERCARD,
213214
ZINTERSTORE,
214215
ZLEXCOUNT,
216+
ZMPOP,
215217
ZMSCORE,
216218
ZPOPMAX,
217219
ZPOPMIN,
@@ -284,6 +286,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
284286
case RedisCommand.INCRBYFLOAT:
285287
case RedisCommand.LINSERT:
286288
case RedisCommand.LMOVE:
289+
case RedisCommand.LMPOP:
287290
case RedisCommand.LPOP:
288291
case RedisCommand.LPUSH:
289292
case RedisCommand.LPUSHX:
@@ -335,6 +338,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
335338
case RedisCommand.ZDIFFSTORE:
336339
case RedisCommand.ZINTERSTORE:
337340
case RedisCommand.ZINCRBY:
341+
case RedisCommand.ZMPOP:
338342
case RedisCommand.ZPOPMAX:
339343
case RedisCommand.ZPOPMIN:
340344
case RedisCommand.ZRANGESTORE:

src/StackExchange.Redis/Interfaces/IDatabase.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,17 @@ public interface IDatabase : IRedis, IDatabaseAsync
922922
/// <remarks><seealso href="https://redis.io/commands/lpop"/></remarks>
923923
RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None);
924924

925+
/// <summary>
926+
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
927+
/// Starts on the left side of the list.
928+
/// </summary>
929+
/// <param name="keys">The keys to look through for elements to pop.</param>
930+
/// <param name="count">The maximum number of elements to pop from the list.</param>
931+
/// <param name="flags">The flags to use for this operation.</param>
932+
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
933+
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
934+
ListPopResult ListLeftPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);
935+
925936
/// <summary>
926937
/// Scans through the list stored at <paramref name="key"/> looking for <paramref name="element"/>, returning the 0-based
927938
/// index of the first matching element.
@@ -1065,6 +1076,17 @@ public interface IDatabase : IRedis, IDatabaseAsync
10651076
/// <remarks><seealso href="https://redis.io/commands/rpop"/></remarks>
10661077
RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None);
10671078

1079+
/// <summary>
1080+
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
1081+
/// Starts on the right side of the list.
1082+
/// </summary>
1083+
/// <param name="keys">The keys to look through for elements to pop.</param>
1084+
/// <param name="count">The maximum number of elements to pop from the list.</param>
1085+
/// <param name="flags">The flags to use for this operation.</param>
1086+
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
1087+
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
1088+
ListPopResult ListRightPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);
1089+
10681090
/// <summary>
10691091
/// 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.
10701092
/// </summary>
@@ -2110,6 +2132,18 @@ IEnumerable<SortedSetEntry> SortedSetScan(RedisKey key,
21102132
/// </remarks>
21112133
SortedSetEntry[] SortedSetPop(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);
21122134

2135+
/// <summary>
2136+
/// Removes and returns up to <paramref name="count"/> entries from the first non-empty sorted set in <paramref name="keys"/>.
2137+
/// Returns <see cref="SortedSetPopResult.Null"/> if none of the sets exist or contain any elements.
2138+
/// </summary>
2139+
/// <param name="keys">The keys to check.</param>
2140+
/// <param name="count">The maximum number of records to pop out of the sorted set.</param>
2141+
/// <param name="order">The order to sort by when popping items out of the set.</param>
2142+
/// <param name="flags">The flags to use for the operation.</param>
2143+
/// <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>
2144+
/// <remarks><seealso href="https://redis.io/commands/zmpop"/></remarks>
2145+
SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);
2146+
21132147
/// <summary>
21142148
/// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged.
21152149
/// </summary>

src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,17 @@ public interface IDatabaseAsync : IRedisAsync
898898
/// <remarks><seealso href="https://redis.io/commands/lpop"/></remarks>
899899
Task<RedisValue[]> ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None);
900900

901+
/// <summary>
902+
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
903+
/// Starts on the left side of the list.
904+
/// </summary>
905+
/// <param name="keys">The keys to look through for elements to pop.</param>
906+
/// <param name="count">The maximum number of elements to pop from the list.</param>
907+
/// <param name="flags">The flags to use for this operation.</param>
908+
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
909+
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
910+
Task<ListPopResult> ListLeftPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);
911+
901912
/// <summary>
902913
/// Scans through the list stored at <paramref name="key"/> looking for <paramref name="element"/>, returning the 0-based
903914
/// index of the first matching element.
@@ -1041,6 +1052,17 @@ public interface IDatabaseAsync : IRedisAsync
10411052
/// <remarks><seealso href="https://redis.io/commands/rpop"/></remarks>
10421053
Task<RedisValue[]> ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None);
10431054

1055+
/// <summary>
1056+
/// Removes and returns at most <paramref name="count"/> elements from the first non-empty list in <paramref name="keys"/>.
1057+
/// Starts on the right side of the list.
1058+
/// </summary>
1059+
/// <param name="keys">The keys to look through for elements to pop.</param>
1060+
/// <param name="count">The maximum number of elements to pop from the list.</param>
1061+
/// <param name="flags">The flags to use for this operation.</param>
1062+
/// <returns>A span of contiguous elements from the list, or <see cref="ListPopResult.Null"/> if no non-empty lists are found.</returns>
1063+
/// <remarks><seealso href="https://redis.io/commands/lmpop"/></remarks>
1064+
Task<ListPopResult> ListRightPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None);
1065+
10441066
/// <summary>
10451067
/// 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.
10461068
/// </summary>
@@ -2063,6 +2085,18 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(RedisKey key,
20632085
/// </remarks>
20642086
Task<SortedSetEntry[]> SortedSetPopAsync(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);
20652087

2088+
/// <summary>
2089+
/// Removes and returns up to <paramref name="count"/> entries from the first non-empty sorted set in <paramref name="keys"/>.
2090+
/// Returns <see cref="SortedSetPopResult.Null"/> if none of the sets exist or contain any elements.
2091+
/// </summary>
2092+
/// <param name="keys">The keys to check.</param>
2093+
/// <param name="count">The maximum number of records to pop out of the sorted set.</param>
2094+
/// <param name="order">The order to sort by when popping items out of the set.</param>
2095+
/// <param name="flags">The flags to use for the operation.</param>
2096+
/// <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>
2097+
/// <remarks><seealso href="https://redis.io/commands/zmpop"/></remarks>
2098+
Task<SortedSetPopResult> SortedSetPopAsync(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);
2099+
20662100
/// <summary>
20672101
/// Allow the consumer to mark a pending message as correctly processed. Returns the number of messages acknowledged.
20682102
/// </summary>

src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.No
227227
public RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
228228
Inner.ListLeftPop(ToInner(key), count, flags);
229229

230+
public ListPopResult ListLeftPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
231+
Inner.ListLeftPop(ToInner(keys), count, flags);
232+
230233
public long ListPosition(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None) =>
231234
Inner.ListPosition(ToInner(key), element, rank, maxLength, flags);
232235

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

266+
public ListPopResult ListRightPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
267+
Inner.ListRightPop(ToInner(keys), count, flags);
268+
263269
public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) =>
264270
Inner.ListRightPopLeftPush(ToInner(source), ToInner(destination), flags);
265271

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

493+
public SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
494+
Inner.SortedSetPop(ToInner(keys), count, order, flags);
495+
487496
public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None) =>
488497
Inner.StreamAcknowledge(ToInner(key), groupName, messageId, flags);
489498

src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ public Task<RedisValue> ListLeftPopAsync(RedisKey key, CommandFlags flags = Comm
238238
public Task<RedisValue[]> ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) =>
239239
Inner.ListLeftPopAsync(ToInner(key), count, flags);
240240

241+
public Task<ListPopResult> ListLeftPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
242+
Inner.ListLeftPopAsync(ToInner(keys), count, flags);
243+
241244
public Task<long> ListPositionAsync(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None) =>
242245
Inner.ListPositionAsync(ToInner(key), element, rank, maxLength, flags);
243246

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

277+
public Task<ListPopResult> ListRightPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None) =>
278+
Inner.ListRightPopAsync(ToInner(keys), count, flags);
279+
274280
public Task<RedisValue> ListRightPopLeftPushAsync(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) =>
275281
Inner.ListRightPopLeftPushAsync(ToInner(source), ToInner(destination), flags);
276282

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

510+
public Task<SortedSetPopResult> SortedSetPopAsync(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) =>
511+
Inner.SortedSetPopAsync(ToInner(keys), count, order, flags);
512+
504513
public Task<long> StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None) =>
505514
Inner.StreamAcknowledgeAsync(ToInner(key), groupName, messageId, flags);
506515

0 commit comments

Comments
 (0)