Skip to content

Commit

Permalink
Sorted set new commands: ZDIFF, ZDIFFSTORE, ZINTER, ZINTERCARD, and Z…
Browse files Browse the repository at this point in the history
…UNION (#2075)

Adds support for ZDIFF, ZDIFFSTORE, ZINTER, ZINTERCARD, ZUNION (#2055)

Co-authored-by: Nick Craver <nrcraver@gmail.com>
  • Loading branch information
Avital-Fine and NickCraver authored Apr 12, 2022
1 parent fffb5c1 commit e18b2c1
Show file tree
Hide file tree
Showing 12 changed files with 600 additions and 17 deletions.
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Adds: Support for `LMOVE` with `.ListMove()`/`.ListMoveAsync()` ([#2065 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2065))
- Adds: Support for `ZRANDMEMBER` with `.SortedSetRandomMember()`/`.SortedSetRandomMemberAsync()`, `.SortedSetRandomMembers()`/`.SortedSetRandomMembersAsync()`, and `.SortedSetRandomMembersWithScores()`/`.SortedSetRandomMembersWithScoresAsync()` ([#2076 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2076))
- Adds: Support for `SMISMEMBER` with `.SetContains()`/`.SetContainsAsync()` ([#2077 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2077))
- Adds: Support for `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZINTERCARD`, and `ZUNION` with `.SortedSetCombine()`/`.SortedSetCombineAsync()`, `.SortedSetCombineWithScores()`/`.SortedSetCombineWithScoresAsync()`, and `.SortedSetIntersectionLength()`/`.SortedSetIntersectionLengthAsync()` ([#2075 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2075))
- Adds: Support for `SINTERCARD` with `.SetIntersectionLength()`/`.SetIntersectionLengthAsync()` ([#2078 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2078))
- Adds: Support for `LPOS` with `.ListPosition()`/`.ListPositionAsync()` and `.ListPositions()`/`.ListPositionsAsync()` ([#2080 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2080))

Expand Down
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ internal enum RedisCommand
ZADD,
ZCARD,
ZCOUNT,
ZDIFF,
ZDIFFSTORE,
ZINCRBY,
ZINTER,
ZINTERCARD,
ZINTERSTORE,
ZLEXCOUNT,
ZPOPMAX,
Expand All @@ -218,6 +222,7 @@ internal enum RedisCommand
ZREVRANK,
ZSCAN,
ZSCORE,
ZUNION,
ZUNIONSTORE,

UNKNOWN,
Expand Down
18 changes: 17 additions & 1 deletion src/StackExchange.Redis/Enums/SetOperation.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace StackExchange.Redis
using System;

namespace StackExchange.Redis
{
/// <summary>
/// Describes an algebraic set operation that can be performed to combine multiple sets.
Expand All @@ -18,4 +20,18 @@ public enum SetOperation
/// </summary>
Difference,
}

internal static class SetOperationExtensions
{
public static RedisCommand ToCommand(this SetOperation operation, bool store) => operation switch
{
SetOperation.Intersect when store => RedisCommand.ZINTERSTORE,
SetOperation.Intersect => RedisCommand.ZINTER,
SetOperation.Union when store => RedisCommand.ZUNIONSTORE,
SetOperation.Union => RedisCommand.ZUNION,
SetOperation.Difference when store => RedisCommand.ZDIFFSTORE,
SetOperation.Difference => RedisCommand.ZDIFF,
_ => throw new ArgumentOutOfRangeException(nameof(operation)),
};
}
}
46 changes: 46 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1380,9 +1380,42 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>https://redis.io/commands/zadd</remarks>
long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
/// optionally performing a specific aggregation (defaults to <see cref="Aggregate.Sum"/>).
/// <see cref="SetOperation.Difference"/> cannot be used with weights or aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="weights">The optional weights per set that correspond to <paramref name="keys"/>.</param>
/// <param name="aggregate">The aggregation method (defaults to <see cref="Aggregate.Sum"/>).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunion</remarks>
/// <remarks>https://redis.io/commands/zinter</remarks>
/// <remarks>https://redis.io/commands/zdiff</remarks>
/// <returns>The resulting sorted set.</returns>
RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
/// optionally performing a specific aggregation (defaults to <see cref="Aggregate.Sum"/>).
/// <see cref="SetOperation.Difference"/> cannot be used with weights or aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="weights">The optional weights per set that correspond to <paramref name="keys"/>.</param>
/// <param name="aggregate">The aggregation method (defaults to <see cref="Aggregate.Sum"/>).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunion</remarks>
/// <remarks>https://redis.io/commands/zinter</remarks>
/// <remarks>https://redis.io/commands/zdiff</remarks>
/// <returns>The resulting sorted set with scores.</returns>
SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum).
/// <see cref="SetOperation.Difference"/> cannot be used with aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="destination">The key to store the results in.</param>
Expand All @@ -1392,12 +1425,14 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunionstore</remarks>
/// <remarks>https://redis.io/commands/zinterstore</remarks>
/// <remarks>https://redis.io/commands/zdiffstore</remarks>
/// <returns>The number of elements in the resulting sorted set at destination.</returns>
long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation over multiple sorted sets (optionally using per-set weights), and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum).
/// <see cref="SetOperation.Difference"/> cannot be used with aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="destination">The key to store the results in.</param>
Expand All @@ -1407,6 +1442,7 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunionstore</remarks>
/// <remarks>https://redis.io/commands/zinterstore</remarks>
/// <remarks>https://redis.io/commands/zdiffstore</remarks>
/// <returns>The number of elements in the resulting sorted set at destination.</returns>
long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

Expand All @@ -1433,6 +1469,16 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>https://redis.io/commands/zincrby</remarks>
double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the cardinality of the intersection of the sorted sets at <paramref name="keys"/>.
/// </summary>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="limit">If the intersection cardinality reaches <paramref name="limit"/> partway through the computation, the algorithm will exit and yield <paramref name="limit"/> as the cardinality (defaults to 0 meaning unlimited).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements in the resulting intersection.</returns>
/// <remarks>https://redis.io/commands/zintercard</remarks>
long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the sorted set cardinality (number of elements) of the sorted set stored at key.
/// </summary>
Expand Down
46 changes: 46 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1344,9 +1344,42 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>https://redis.io/commands/zadd</remarks>
Task<long> SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
/// optionally performing a specific aggregation (defaults to <see cref="Aggregate.Sum"/>).
/// <see cref="SetOperation.Difference"/> cannot be used with weights or aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="weights">The optional weights per set that correspond to <paramref name="keys"/>.</param>
/// <param name="aggregate">The aggregation method (defaults to <see cref="Aggregate.Sum"/>).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunion</remarks>
/// <remarks>https://redis.io/commands/zinter</remarks>
/// <remarks>https://redis.io/commands/zdiff</remarks>
/// <returns>The resulting sorted set.</returns>
Task<RedisValue[]> SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation for multiple sorted sets (optionally using per-set <paramref name="weights"/>),
/// optionally performing a specific aggregation (defaults to <see cref="Aggregate.Sum"/>).
/// <see cref="SetOperation.Difference"/> cannot be used with weights or aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="weights">The optional weights per set that correspond to <paramref name="keys"/>.</param>
/// <param name="aggregate">The aggregation method (defaults to <see cref="Aggregate.Sum"/>).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunion</remarks>
/// <remarks>https://redis.io/commands/zinter</remarks>
/// <remarks>https://redis.io/commands/zdiff</remarks>
/// <returns>The resulting sorted set with scores.</returns>
Task<SortedSetEntry[]> SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum).
/// <see cref="SetOperation.Difference"/> cannot be used with aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="destination">The key to store the results in.</param>
Expand All @@ -1356,12 +1389,14 @@ public interface IDatabaseAsync : IRedisAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunionstore</remarks>
/// <remarks>https://redis.io/commands/zinterstore</remarks>
/// <remarks>https://redis.io/commands/zdiffstore</remarks>
/// <returns>The number of elements in the resulting sorted set at destination.</returns>
Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Computes a set operation over multiple sorted sets (optionally using per-set weights), and stores the result in destination, optionally performing
/// a specific aggregation (defaults to sum).
/// <see cref="SetOperation.Difference"/> cannot be used with aggregation.
/// </summary>
/// <param name="operation">The operation to perform.</param>
/// <param name="destination">The key to store the results in.</param>
Expand All @@ -1371,6 +1406,7 @@ public interface IDatabaseAsync : IRedisAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <remarks>https://redis.io/commands/zunionstore</remarks>
/// <remarks>https://redis.io/commands/zinterstore</remarks>
/// <remarks>https://redis.io/commands/zdiffstore</remarks>
/// <returns>The number of elements in the resulting sorted set at destination.</returns>
Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None);

Expand All @@ -1397,6 +1433,16 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>https://redis.io/commands/zincrby</remarks>
Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the cardinality of the intersection of the sorted sets at <paramref name="keys"/>.
/// </summary>
/// <param name="keys">The keys of the sorted sets.</param>
/// <param name="limit">If the intersection cardinality reaches <paramref name="limit"/> partway through the computation, the algorithm will exit and yield <paramref name="limit"/> as the cardinality (defaults to 0 meaning unlimited).</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of elements in the resulting intersection.</returns>
/// <remarks>https://redis.io/commands/zintercard</remarks>
Task<long> SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the sorted set cardinality (number of elements) of the sorted set stored at key.
/// </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 @@ -351,6 +351,12 @@ public bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandF
public bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetAdd(ToInner(key), member, score, when, flags);

public RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombine(operation, keys, weights, aggregate, flags);

public SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombineWithScores(operation, keys, weights, aggregate, flags);

public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombineAndStore(operation, ToInner(destination), ToInner(keys), weights, aggregate, flags);

Expand All @@ -363,6 +369,9 @@ public double SortedSetDecrement(RedisKey key, RedisValue member, double value,
public double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIncrement(ToInner(key), member, value, flags);

public long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIntersectionLength(keys, limit, flags);

public long SortedSetLength(RedisKey key, double min = -1.0 / 0.0, double max = 1.0 / 0.0, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetLength(ToInner(key), min, max, exclude, 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 @@ -364,6 +364,12 @@ public Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double scor
public Task<bool> SortedSetAddAsync(RedisKey key, RedisValue member, double score, When when = When.Always, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetAddAsync(ToInner(key), member, score, when, flags);

public Task<RedisValue[]> SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombineAsync(operation, keys, weights, aggregate, flags);

public Task<SortedSetEntry[]> SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombineWithScoresAsync(operation, keys, weights, aggregate, flags);

public Task<long> SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetCombineAndStoreAsync(operation, ToInner(destination), ToInner(keys), weights, aggregate, flags);

Expand All @@ -376,6 +382,9 @@ public Task<double> SortedSetDecrementAsync(RedisKey key, RedisValue member, dou
public Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIncrementAsync(ToInner(key), member, value, flags);

public Task<long> SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIntersectionLengthAsync(keys, limit, flags);

public Task<long> SortedSetLengthAsync(RedisKey key, double min = -1.0 / 0.0, double max = 1.0 / 0.0, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetLengthAsync(ToInner(key), min, max, exclude, flags);

Expand Down
Loading

0 comments on commit e18b2c1

Please sign in to comment.