Skip to content

Commit

Permalink
Support GT, LT and CH in ZADD command (#2136)
Browse files Browse the repository at this point in the history
[SortedSetAdd](https://redis.io/commands/zadd/) currently not supporting GT and LT features.

Co-authored-by: Nick Craver <nrcraver@gmail.com>
  • Loading branch information
Avital-Fine and NickCraver authored Jun 14, 2022
1 parent e0d8320 commit 67297e3
Show file tree
Hide file tree
Showing 14 changed files with 402 additions and 92 deletions.
3 changes: 2 additions & 1 deletion docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
- Adds: Support for `OBJECT FREQ` with `.KeyFrequency()`/`.KeyFrequencyAsync()` ([#2105 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2105))
- Performance: Avoids allocations when computing cluster hash slots or testing key equality ([#2110 by Marc Gravell](https://github.com/StackExchange/StackExchange.Redis/pull/2110))
- Adds: Support for `SORT_RO` with `.Sort()`/`.SortAsync()` ([#2111 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2111))
- Adds: Support for `BIT | BYTE` to `BITCOUNT` and `BITPOS` with `.StringBitCount()`/`.StringBitCountAsync()` and `.StringBitPosition()`/`.StringBitPositionAsync()` [#2116 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2116))
- Adds: Support for `BIT | BYTE` to `BITCOUNT` and `BITPOS` with `.StringBitCount()`/`.StringBitCountAsync()` and `.StringBitPosition()`/`.StringBitPositionAsync()` ([#2116 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2116))
- Adds: Support for pub/sub payloads that are unary arrays ([#2118 by Marc Gravell](https://github.com/StackExchange/StackExchange.Redis/pull/2118))
- Fix: Sentinel timer race during dispose ([#2133 by ewisuri](https://github.com/StackExchange/StackExchange.Redis/pull/2133))
- Adds: Support for `GT`, `LT`, and `CH` on `ZADD` with `.SortedSetAdd()`/`.SortedSetAddAsync()` and `.SortedSetUpdate()`/`.SortedSetUpdateAsync()` ([#2136 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2136))
- Adds: Support for `COMMAND COUNT`, `COMMAND GETKEYS`, and `COMMAND LIST`, with `.CommandCount()`/`.CommandCountAsync()`, `.CommandGetKeys()`/`.CommandGetKeysAsync()`, and `.CommandList()`/`.CommandListAsync()` ([#2143 by shacharPash](https://github.com/StackExchange/StackExchange.Redis/pull/2143))


Expand Down
52 changes: 52 additions & 0 deletions src/StackExchange.Redis/Enums/SortedSetWhen.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;

namespace StackExchange.Redis
{
/// <summary>
/// Indicates when this operation should be performed (only some variations are legal in a given context).
/// </summary>
[Flags]
public enum SortedSetWhen
{
/// <summary>
/// The operation won't be prevented.
/// </summary>
Always = 0,
/// <summary>
/// The operation should only occur when there is an existing value.
/// </summary>
Exists = 1 << 0,
/// <summary>
/// The operation should only occur when the new score is greater than the current score.
/// </summary>
GreaterThan = 1 << 1,
/// <summary>
/// The operation should only occur when the new score is less than the current score.
/// </summary>
LessThan = 1 << 2,
/// <summary>
/// The operation should only occur when there is not an existing value.
/// </summary>
NotExists = 1 << 3,
}

internal static class SortedSetWhenExtensions
{
internal static uint CountBits(this SortedSetWhen when)
{
uint v = (uint)when;
v -= ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
uint c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
return c;
}

internal static SortedSetWhen Parse(When when)=> when switch
{
When.Always => SortedSetWhen.Always,
When.Exists => SortedSetWhen.Exists,
When.NotExists => SortedSetWhen.NotExists,
_ => throw new ArgumentOutOfRangeException(nameof(when))
};
}
}
58 changes: 37 additions & 21 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1590,18 +1590,14 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks><seealso href="https://redis.io/commands/sort"/></remarks>
long SortAndStore(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default, RedisValue[]? get = null, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key.
/// If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="member">The member to add to the sorted set.</param>
/// <param name="score">The score for the member to add to the sorted set.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns><see langword="true"/> if the value was added. <see langword="false"/> if it already existed (the score is still updated).</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
/// <inheritdoc cref="SortedSetAdd(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandFlags flags);

/// <inheritdoc cref="SortedSetAdd(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags= CommandFlags.None);

/// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key.
/// If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
Expand All @@ -1613,19 +1609,16 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <returns><see langword="true"/> if the value was added. <see langword="false"/> if it already existed (the score is still updated).</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None);
bool SortedSetAdd(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds all the specified members with the specified scores to the sorted set stored at key.
/// If a specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="values">The members and values to add to the sorted set.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements added to the sorted sets, not including elements already existing for which the score was updated.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
/// <inheritdoc cref="SortedSetAdd(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
long SortedSetAdd(RedisKey key, SortedSetEntry[] values, CommandFlags flags);

/// <inheritdoc cref="SortedSetAdd(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds all the specified members with the specified scores to the sorted set stored at key.
/// If a specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
Expand All @@ -1636,7 +1629,7 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements added to the sorted sets, not including elements already existing for which the score was updated.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None);
long SortedSetAdd(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
Expand Down Expand Up @@ -2160,6 +2153,29 @@ IEnumerable<SortedSetEntry> SortedSetScan(RedisKey key,
/// <remarks><seealso href="https://redis.io/commands/zmpop"/></remarks>
SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Same as <see cref="SortedSetAdd(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" /> but return the number of the elements changed.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="member">The member to add/update to the sorted set.</param>
/// <param name="score">The score for the member to add/update to the sorted set.</param>
/// <param name="when">What conditions to add the element under (defaults to always).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements changed.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
bool SortedSetUpdate(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Same as <see cref="SortedSetAdd(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" /> but return the number of the elements changed.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="values">The members and values to add/update to the sorted set.</param>
/// <param name="when">What conditions to add the element under (defaults to always).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements changed.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
long SortedSetUpdate(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, 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
58 changes: 37 additions & 21 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,18 +1554,14 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks><seealso href="https://redis.io/commands/sort"/></remarks>
Task<long> SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default, RedisValue[]? get = null, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key.
/// If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="member">The member to add to the sorted set.</param>
/// <param name="score">The score for the member to add to the sorted set.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns><see langword="true"/> if the value was added. <see langword="false"/> if it already existed (the score is still updated).</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
/// <inheritdoc cref="SortedSetAddAsync(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double score, CommandFlags flags);

/// <inheritdoc cref="SortedSetAddAsync(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double score, When when, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key.
/// If the specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
Expand All @@ -1577,19 +1573,16 @@ public interface IDatabaseAsync : IRedisAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <returns><see langword="true"/> if the value was added. <see langword="false"/> if it already existed (the score is still updated).</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None);
Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds all the specified members with the specified scores to the sorted set stored at key.
/// If a specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="values">The members and values to add to the sorted set.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements added to the sorted sets, not including elements already existing for which the score was updated.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
/// <inheritdoc cref="SortedSetAddAsync(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, CommandFlags flags);

/// <inheritdoc cref="SortedSetAddAsync(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds all the specified members with the specified scores to the sorted set stored at key.
/// If a specified member is already a member of the sorted set, the score is updated and the element reinserted at the right position to ensure the correct ordering.
Expand All @@ -1600,7 +1593,7 @@ public interface IDatabaseAsync : IRedisAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements added to the sorted sets, not including elements already existing for which the score was updated.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None);
Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
Expand Down Expand Up @@ -2073,6 +2066,29 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(RedisKey key,
/// <remarks><seealso href="https://redis.io/commands/zmscore"/></remarks>
Task<double?[]> SortedSetScoresAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Same as <see cref="SortedSetAddAsync(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" /> but return the number of the elements changed.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="member">The member to add/update to the sorted set.</param>
/// <param name="score">The score for the member to add/update to the sorted set.</param>
/// <param name="when">What conditions to add the element under (defaults to always).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements changed.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
Task<bool> SortedSetUpdateAsync(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Same as <see cref="SortedSetAddAsync(RedisKey, SortedSetEntry[], SortedSetWhen, CommandFlags)" /> but return the number of the elements changed.
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="values">The members and values to add/update to the sorted set.</param>
/// <param name="when">What conditions to add the element under (defaults to always).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements changed.</returns>
/// <remarks><seealso href="https://redis.io/commands/zadd"/></remarks>
Task<long> SortedSetUpdateAsync(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Removes and returns the first element from the sorted set stored at key, by default with the scores ordered from low to high.
/// </summary>
Expand Down
Loading

0 comments on commit 67297e3

Please sign in to comment.