From 0622accb252dec95128963403ce29e1dd2b46ddc Mon Sep 17 00:00:00 2001 From: Avital-Fine Date: Sun, 10 Apr 2022 13:51:11 +0300 Subject: [PATCH 01/11] Sotred set new commands --- src/StackExchange.Redis/Enums/RedisCommand.cs | 5 + .../Interfaces/IDatabase.cs | 42 +++++ .../Interfaces/IDatabaseAsync.cs | 42 +++++ .../KeyspaceIsolation/DatabaseWrapper.cs | 9 + .../KeyspaceIsolation/WrapperBase.cs | 11 +- src/StackExchange.Redis/PublicAPI.Shipped.txt | 6 + src/StackExchange.Redis/RedisDatabase.cs | 104 +++++++++++- .../DatabaseWrapperTests.cs | 24 +++ tests/StackExchange.Redis.Tests/SortedSets.cs | 156 ++++++++++++++++++ .../WrapperBaseTests.cs | 16 ++ 10 files changed, 406 insertions(+), 9 deletions(-) diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index 09d067184..45542ee22 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -193,7 +193,11 @@ internal enum RedisCommand ZADD, ZCARD, ZCOUNT, + ZDIFF, + ZDIFFSTORE, ZINCRBY, + ZINTER, + ZINTERCARD, ZINTERSTORE, ZLEXCOUNT, ZPOPMAX, @@ -213,6 +217,7 @@ internal enum RedisCommand ZREVRANK, ZSCAN, ZSCORE, + ZUNION, ZUNIONSTORE, UNKNOWN, diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 0c4c4acce..8e09d5960 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1312,6 +1312,36 @@ public interface IDatabase : IRedis, IDatabaseAsync /// https://redis.io/commands/zadd long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// + /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client, optionally performing + /// a specific aggregation (defaults to sum). + /// + /// The operation to perform. + /// The keys of the sorted sets. + /// The optional weights per set that correspond to . + /// The aggregation method (defaults to sum). + /// The flags to use for this operation. + /// https://redis.io/commands/zunion + /// https://redis.io/commands/zinter + /// https://redis.io/commands/zdiff + /// The resulting sorted set. + RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); + + /// + /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client with scores, optionally performing + /// a specific aggregation (defaults to sum). + /// + /// The operation to perform. + /// The keys of the sorted sets. + /// The optional weights per set that correspond to . + /// The aggregation method (defaults to sum). + /// The flags to use for this operation. + /// https://redis.io/commands/zunion + /// https://redis.io/commands/zinter + /// https://redis.io/commands/zdiff + /// The resulting sorted set with scores. + SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); + /// /// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing /// a specific aggregation (defaults to sum). @@ -1324,6 +1354,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// The flags to use for this operation. /// https://redis.io/commands/zunionstore /// https://redis.io/commands/zinterstore + /// https://redis.io/commands/zdiffstore /// The number of elements in the resulting sorted set at destination. long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); @@ -1339,6 +1370,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// The flags to use for this operation. /// https://redis.io/commands/zunionstore /// https://redis.io/commands/zinterstore + /// https://redis.io/commands/zdiffstore /// The number of elements in the resulting sorted set at destination. long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); @@ -1365,6 +1397,16 @@ public interface IDatabase : IRedis, IDatabaseAsync /// https://redis.io/commands/zincrby double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None); + /// + /// Returns the cardinality of the intersaction of the Sorted sets. + /// + /// The keys of the sorted sets. + /// If the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality (defaults to 0 and means unlimited). + /// The flags to use for this operation. + /// The number of elements in the resulting intersection. + /// https://redis.io/commands/zintercard + long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None); + /// /// Returns the sorted set cardinality (number of elements) of the sorted set stored at key. /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 81d4d55af..f045427ba 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -1277,6 +1277,36 @@ public interface IDatabaseAsync : IRedisAsync /// https://redis.io/commands/zadd Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// + /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client, optionally performing + /// a specific aggregation (defaults to sum). + /// + /// The operation to perform. + /// The keys of the sorted sets. + /// The optional weights per set that correspond to . + /// The aggregation method (defaults to sum). + /// The flags to use for this operation. + /// https://redis.io/commands/zunion + /// https://redis.io/commands/zinter + /// https://redis.io/commands/zdiff + /// The resulting sorted set. + Task SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); + + /// + /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client with scores, optionally performing + /// a specific aggregation (defaults to sum). + /// + /// The operation to perform. + /// The keys of the sorted sets. + /// The optional weights per set that correspond to . + /// The aggregation method (defaults to sum). + /// The flags to use for this operation. + /// https://redis.io/commands/zunion + /// https://redis.io/commands/zinter + /// https://redis.io/commands/zdiff + /// The resulting sorted set with scores. + Task SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); + /// /// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing /// a specific aggregation (defaults to sum). @@ -1289,6 +1319,7 @@ public interface IDatabaseAsync : IRedisAsync /// The flags to use for this operation. /// https://redis.io/commands/zunionstore /// https://redis.io/commands/zinterstore + /// https://redis.io/commands/zdiffstore /// The number of elements in the resulting sorted set at destination. Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); @@ -1304,6 +1335,7 @@ public interface IDatabaseAsync : IRedisAsync /// The flags to use for this operation. /// https://redis.io/commands/zunionstore /// https://redis.io/commands/zinterstore + /// https://redis.io/commands/zdiffstore /// The number of elements in the resulting sorted set at destination. Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); @@ -1330,6 +1362,16 @@ public interface IDatabaseAsync : IRedisAsync /// https://redis.io/commands/zincrby Task SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None); + /// + /// Returns the cardinality of the intersaction of the Sorted sets. + /// + /// The keys of the sorted sets. + /// If the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality (defaults to 0 and means unlimited). + /// The flags to use for this operation. + /// The number of elements in the resulting intersection. + /// https://redis.io/commands/zintercard + Task SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None); + /// /// Returns the sorted set cardinality (number of elements) of the sorted set stored at key. /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index e755c2d34..068028c44 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -336,6 +336,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); @@ -348,6 +354,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); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 2da36d504..ac9fe29d8 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -349,6 +349,12 @@ public Task SortedSetAddAsync(RedisKey key, RedisValue member, double scor public Task 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 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 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 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); @@ -361,6 +367,9 @@ public Task SortedSetDecrementAsync(RedisKey key, RedisValue member, dou public Task SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) => Inner.SortedSetIncrementAsync(ToInner(key), member, value, flags); + public Task SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetIntersectionLengthAsync(keys, limit, flags); + public Task 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); @@ -532,7 +541,7 @@ public Task StringGetAsync(RedisKey[] keys, CommandFlags flags = C public Task StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None) => Inner.StringGetAsync(ToInner(key), flags); - + public Task StringGetSetExpiryAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) => Inner.StringGetSetExpiryAsync(ToInner(key), expiry, flags); diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 008027d61..ed243c788 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -586,10 +586,13 @@ StackExchange.Redis.IDatabase.SortedSetAdd(StackExchange.Redis.RedisKey key, Sta StackExchange.Redis.IDatabase.SortedSetAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double score, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool StackExchange.Redis.IDatabase.SortedSetAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.SortedSetAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.SortedSetCombine(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue[]! +StackExchange.Redis.IDatabase.SortedSetCombineWithScores(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.SortedSetEntry[]! StackExchange.Redis.IDatabase.SortedSetCombineAndStore(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetCombineAndStore(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetDecrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double StackExchange.Redis.IDatabase.SortedSetIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double +StackExchange.Redis.IDatabase.SortedSetIntersectionLength(StackExchange.Redis.RedisKey[]! keys, long limit = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetLength(StackExchange.Redis.RedisKey key, double min = -Infinity, double max = Infinity, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetLengthByValue(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue min, StackExchange.Redis.RedisValue max, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetPop(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.SortedSetEntry[]! @@ -774,10 +777,13 @@ StackExchange.Redis.IDatabaseAsync.SortedSetAddAsync(StackExchange.Redis.RedisKe StackExchange.Redis.IDatabaseAsync.SortedSetAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double score, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.SortedSetEntry[]! values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetCombineAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetCombineWithScoresAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetCombineAndStoreAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetCombineAndStoreAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, double[]? weights = null, StackExchange.Redis.Aggregate aggregate = StackExchange.Redis.Aggregate.Sum, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetDecrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetIntersectionLengthAsync(StackExchange.Redis.RedisKey[]! keys, long limit = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetLengthAsync(StackExchange.Redis.RedisKey key, double min = -Infinity, double max = Infinity, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetLengthByValueAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue min, StackExchange.Redis.RedisValue max, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetPopAsync(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 6eb4ed0bf..26da5a8ed 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1580,6 +1580,30 @@ public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When return ExecuteAsync(msg, ResultProcessor.Int64); } + public RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, false, flags); + return ExecuteSync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); + } + + public Task SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, false, flags); + return ExecuteAsync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); + } + + public SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, true, flags); + return ExecuteSync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); + } + + public Task SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, true, flags); + return ExecuteAsync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); + } + public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { var msg = GetSortedSetCombineAndStoreCommandMessage(operation, destination, new[] { first, second }, null, aggregate, flags); @@ -1626,6 +1650,18 @@ public Task SortedSetIncrementAsync(RedisKey key, RedisValue member, dou return ExecuteAsync(msg, ResultProcessor.Double); } + public long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetIntersectionLengthMessage(keys, limit, flags); + return ExecuteSync(msg, ResultProcessor.Int64); + } + + public Task SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) + { + var msg = GetSortedSetIntersectionLengthMessage(keys, limit, flags); + return ExecuteAsync(msg, ResultProcessor.Int64); + } + public long SortedSetLength(RedisKey key, double min = double.NegativeInfinity, double max = double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) { var msg = GetSortedSetLengthMessage(key, min, max, exclude, flags); @@ -3116,32 +3152,67 @@ private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation { SetOperation.Intersect => RedisCommand.ZINTERSTORE, SetOperation.Union => RedisCommand.ZUNIONSTORE, + SetOperation.Difference => RedisCommand.ZDIFFSTORE, _ => throw new ArgumentOutOfRangeException(nameof(operation)), }; if (keys == null) throw new ArgumentNullException(nameof(keys)); + // Difference operation can't have weights or aggregation + if (operation == SetOperation.Difference && (weights != null || aggregate != Aggregate.Sum)) + throw new ArgumentException("ZDIFFSTORE cannot be used with weights or with aggregation."); - List? values = null; + List? values = new List(); + AddWeightsAndAggregation(ref values, weights, aggregate); + return new SortedSetCombineAndStoreCommandMessage(Database, flags, command, destination, keys, values?.ToArray() ?? RedisValue.EmptyArray); + } + + private Message GetSortedSetCombineCommandMessage(SetOperation operation, RedisKey[] keys, double[]? weights, Aggregate aggregate, bool withScores, CommandFlags flags) + { + var command = operation switch + { + SetOperation.Intersect => RedisCommand.ZINTER, + SetOperation.Union => RedisCommand.ZUNION, + SetOperation.Difference => RedisCommand.ZDIFF, + _ => throw new ArgumentOutOfRangeException(nameof(operation)), + }; + if (keys == null) throw new ArgumentNullException(nameof(keys)); + // Difference operation can't have weights or aggregation + if (operation == SetOperation.Difference && (weights != null || aggregate != Aggregate.Sum)) + throw new ArgumentException("ZDIFF cannot be used with weights or with aggregation."); + + List? values = new List(); + values.Add(keys.Length); + foreach(var key in keys) + values.Add(key.AsRedisValue()); + AddWeightsAndAggregation(ref values, weights, aggregate); + if (withScores) + { + values.Add(RedisLiterals.WITHSCORES); + } + return Message.Create(Database, flags, command, values?.ToArray() ?? RedisValue.EmptyArray); + } + + private void AddWeightsAndAggregation(ref List args, double[]? weights, Aggregate aggregate) + { if (weights != null && weights.Length != 0) { - (values ??= new List()).Add(RedisLiterals.WEIGHTS); + (args ??= new List()).Add(RedisLiterals.WEIGHTS); foreach (var weight in weights) - values.Add(weight); + args.Add(weight); } switch (aggregate) { case Aggregate.Sum: break; // default case Aggregate.Min: - (values ??= new List()).Add(RedisLiterals.AGGREGATE); - values.Add(RedisLiterals.MIN); + (args ??= new List()).Add(RedisLiterals.AGGREGATE); + args.Add(RedisLiterals.MIN); break; case Aggregate.Max: - (values ??= new List()).Add(RedisLiterals.AGGREGATE); - values.Add(RedisLiterals.MAX); + (args ??= new List()).Add(RedisLiterals.AGGREGATE); + args.Add(RedisLiterals.MAX); break; default: throw new ArgumentOutOfRangeException(nameof(aggregate)); } - return new SortedSetCombineAndStoreCommandMessage(Database, flags, command, destination, keys, values?.ToArray() ?? RedisValue.EmptyArray); } private Message GetSortedSetLengthMessage(RedisKey key, double min, double max, Exclude exclude, CommandFlags flags) @@ -3154,6 +3225,23 @@ private Message GetSortedSetLengthMessage(RedisKey key, double min, double max, return Message.Create(Database, flags, RedisCommand.ZCOUNT, key, from, to); } + private Message GetSortedSetIntersectionLengthMessage(RedisKey[] keys, long limit, CommandFlags flags) + { + if (keys == null) throw new ArgumentNullException(nameof(keys)); + + List? values = new List(); + values.Add(keys.Length); + foreach(var key in keys) + values.Add(key.AsRedisValue()); + + if (limit > 0) { + values.Add(RedisLiterals.LIMIT); + values.Add(limit); + } + + return Message.Create(Database, flags, RedisCommand.ZINTERCARD, values); + } + private Message GetSortedSetRangeByScoreMessage(RedisKey key, double start, double stop, Exclude exclude, Order order, long skip, long take, CommandFlags flags, bool withScores) { // usage: {ZRANGEBYSCORE|ZREVRANGEBYSCORE} key from to [WITHSCORES] [LIMIT offset count] diff --git a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs index 0ddd9981b..f14efacd0 100644 --- a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs +++ b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs @@ -718,6 +718,22 @@ public void SortedSetAdd_2() mock.Verify(_ => _.SortedSetAdd("prefix:key", values, When.Exists, CommandFlags.None)); } + [Fact] + public void SortedSetCombine() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetCombine(SetOperation.Intersect, keys); + mock.Verify(_ => _.SortedSetCombine(SetOperation.Intersect, keys, null, Aggregate.Sum, CommandFlags.None)); + } + + [Fact] + public void SortedSetCombineWithScores() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetCombineWithScores(SetOperation.Intersect, keys); + mock.Verify(_ => _.SortedSetCombineWithScores(SetOperation.Intersect, keys, null, Aggregate.Sum, CommandFlags.None)); + } + [Fact] public void SortedSetCombineAndStore_1() { @@ -748,6 +764,14 @@ public void SortedSetIncrement() mock.Verify(_ => _.SortedSetIncrement("prefix:key", "member", 1.23, CommandFlags.None)); } + [Fact] + public void SortedSetIntersectionLength() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetIntersectionLength(keys, 1, CommandFlags.None); + mock.Verify(_ => _.SortedSetIntersectionLength(keys, 1, CommandFlags.None)); + } + [Fact] public void SortedSetLength() { diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index abfd33664..65ac7e510 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -38,6 +38,15 @@ public SortedSets(ITestOutputHelper output, SharedConnectionFixture fixture) : b new SortedSetEntry("j", 512) }; + private static readonly SortedSetEntry[] entriesPow3 = new SortedSetEntry[] + { + new SortedSetEntry("a", 1), + new SortedSetEntry("c", 4), + new SortedSetEntry("e", 16), + new SortedSetEntry("g", 64), + new SortedSetEntry("i", 256), + }; + private static readonly SortedSetEntry[] lexEntries = new SortedSetEntry[] { new SortedSetEntry("a", 0), @@ -52,6 +61,153 @@ public SortedSets(ITestOutputHelper output, SharedConnectionFixture fixture) : b new SortedSetEntry("j", 0) }; + [Fact] + public void SortedSetCombine() + { + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff.Length); + Assert.Equal("b", diff[0]); + + var inter = db.SortedSetCombine(SetOperation.Intersect, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter.Length); + Assert.Equal("a", inter[0]); + + var union = db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union.Length); + Assert.Equal("a", union[0]); + } + } + + [Fact] + public void SortedSetCombineWithScores() + { + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff.Length); + Assert.Equal(new SortedSetEntry("b", 2), diff[0]); + + var inter = db.SortedSetCombineWithScores(SetOperation.Intersect, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter.Length); + Assert.Equal(new SortedSetEntry("a", 2), inter[0]); + + var union = db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union.Length); + Assert.Equal(new SortedSetEntry("a", 2), union[0]); + } + } + + [Fact] + public void SortedSetCombineAndStore() + { + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + var destination = Me() + "dest"; + db.KeyDelete(destination, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff); + + var inter = db.SortedSetCombineAndStore(SetOperation.Intersect, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter); + + var union = db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union); + } + } + + [Fact] + public void SortedSetCombineErrors() + { + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + var destination = Me() + "dest"; + db.KeyDelete(destination, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + // ZDIFF can't be used with weights + Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + // ZDIFF can't be used with aggregation + Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + // too many weights + Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + } + } + + [Fact] + public void SortedSetIntersectionLength() + { + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v6_9_240); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter); + + // with limit + inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}, 3); + Assert.Equal(3, inter); + } + } + [Fact] public void SortedSetPopMulti_Multi() { diff --git a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs index 13b5fd8cd..ed08a04c2 100644 --- a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs +++ b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs @@ -664,6 +664,22 @@ public void SortedSetAddAsync_2() mock.Verify(_ => _.SortedSetAddAsync("prefix:key", values, When.Exists, CommandFlags.None)); } + [Fact] + public void SortedSetCombineAsync() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetCombineAsync(SetOperation.Intersect, keys); + mock.Verify(_ => _.SortedSetCombineAsync(SetOperation.Intersect, keys, null, Aggregate.Sum, CommandFlags.None)); + } + + [Fact] + public void SortedSetCombineWithScoresAsync() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetCombineWithScoresAsync(SetOperation.Intersect, keys); + mock.Verify(_ => _.SortedSetCombineWithScoresAsync(SetOperation.Intersect, keys, null, Aggregate.Sum, CommandFlags.None)); + } + [Fact] public void SortedSetCombineAndStoreAsync_1() { From 9388dfc6e55197c2aab66c61ae3a57d963e8ef53 Mon Sep 17 00:00:00 2001 From: Avital-Fine Date: Sun, 10 Apr 2022 13:56:27 +0300 Subject: [PATCH 02/11] add async test --- tests/StackExchange.Redis.Tests/WrapperBaseTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs index ed08a04c2..aec1d0e3b 100644 --- a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs +++ b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs @@ -710,6 +710,14 @@ public void SortedSetIncrementAsync() mock.Verify(_ => _.SortedSetIncrementAsync("prefix:key", "member", 1.23, CommandFlags.None)); } + [Fact] + public void SortedSetIntersectionLengthAsync() + { + RedisKey[] keys = new RedisKey[] { "a", "b" }; + wrapper.SortedSetIntersectionLengthAsync(keys, 1, CommandFlags.None); + mock.Verify(_ => _.SortedSetIntersectionLengthAsync(keys, 1, CommandFlags.None)); + } + [Fact] public void SortedSetLengthAsync() { From 6cda19af2cb6888f0c744404b66cf910c136eede Mon Sep 17 00:00:00 2001 From: Avital-Fine Date: Mon, 11 Apr 2022 12:16:26 +0300 Subject: [PATCH 03/11] some fixes --- .../Interfaces/IDatabase.cs | 4 + src/StackExchange.Redis/RedisDatabase.cs | 38 +- tests/StackExchange.Redis.Tests/SortedSets.cs | 446 +++++++++--------- 3 files changed, 237 insertions(+), 251 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 8e09d5960..4be124c2a 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1315,6 +1315,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client, optionally performing /// a specific aggregation (defaults to sum). + /// The different operations can't be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. @@ -1330,6 +1331,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client with scores, optionally performing /// a specific aggregation (defaults to sum). + /// The different operations can't be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. @@ -1345,6 +1347,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing /// a specific aggregation (defaults to sum). + /// The different operations can't be used with weights or aggregation. /// /// The operation to perform. /// The key to store the results in. @@ -1361,6 +1364,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// 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). + /// The different operations can't be used with weights or aggregation. /// /// The operation to perform. /// The key to store the results in. diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 26da5a8ed..b3f2d0524 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1582,25 +1582,25 @@ public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When public RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { - var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, false, flags); + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, withScores: false, flags); return ExecuteSync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); } public Task SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { - var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, false, flags); + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, withScores: false, flags); return ExecuteAsync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); } public SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { - var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, true, flags); + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, withScores: true, flags); return ExecuteSync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); } public Task SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None) { - var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, true, flags); + var msg = GetSortedSetCombineCommandMessage(operation, keys, weights, aggregate, withScores: true, flags); return ExecuteAsync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); } @@ -3160,7 +3160,7 @@ private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation if (operation == SetOperation.Difference && (weights != null || aggregate != Aggregate.Sum)) throw new ArgumentException("ZDIFFSTORE cannot be used with weights or with aggregation."); - List? values = new List(); + List? values = null; AddWeightsAndAggregation(ref values, weights, aggregate); return new SortedSetCombineAndStoreCommandMessage(Database, flags, command, destination, keys, values?.ToArray() ?? RedisValue.EmptyArray); } @@ -3175,40 +3175,42 @@ private Message GetSortedSetCombineCommandMessage(SetOperation operation, RedisK _ => throw new ArgumentOutOfRangeException(nameof(operation)), }; if (keys == null) throw new ArgumentNullException(nameof(keys)); - // Difference operation can't have weights or aggregation - if (operation == SetOperation.Difference && (weights != null || aggregate != Aggregate.Sum)) - throw new ArgumentException("ZDIFF cannot be used with weights or with aggregation."); - List? values = new List(); + var values = new List( + keys.Length + + (weights != null ? 1 + weights.Length : 0) + + (aggregate != Aggregate.Sum ? 2 : 0) + ); + values.Add(keys.Length); foreach(var key in keys) values.Add(key.AsRedisValue()); AddWeightsAndAggregation(ref values, weights, aggregate); if (withScores) { - values.Add(RedisLiterals.WITHSCORES); + values?.Add(RedisLiterals.WITHSCORES); } return Message.Create(Database, flags, command, values?.ToArray() ?? RedisValue.EmptyArray); } - private void AddWeightsAndAggregation(ref List args, double[]? weights, Aggregate aggregate) + private void AddWeightsAndAggregation(ref List? values, double[]? weights, Aggregate aggregate) { if (weights != null && weights.Length != 0) { - (args ??= new List()).Add(RedisLiterals.WEIGHTS); + (values ??= new List()).Add(RedisLiterals.WEIGHTS); foreach (var weight in weights) - args.Add(weight); + values.Add(weight); } switch (aggregate) { case Aggregate.Sum: break; // default case Aggregate.Min: - (args ??= new List()).Add(RedisLiterals.AGGREGATE); - args.Add(RedisLiterals.MIN); + (values ??= new List()).Add(RedisLiterals.AGGREGATE); + values.Add(RedisLiterals.MIN); break; case Aggregate.Max: - (args ??= new List()).Add(RedisLiterals.AGGREGATE); - args.Add(RedisLiterals.MAX); + (values ??= new List()).Add(RedisLiterals.AGGREGATE); + values.Add(RedisLiterals.MAX); break; default: throw new ArgumentOutOfRangeException(nameof(aggregate)); @@ -3229,7 +3231,7 @@ private Message GetSortedSetIntersectionLengthMessage(RedisKey[] keys, long limi { if (keys == null) throw new ArgumentNullException(nameof(keys)); - List? values = new List(); + var values = new List(1 + keys.Length + (limit > 0 ? 2 : 0)); values.Add(keys.Length); foreach(var key in keys) values.Add(key.AsRedisValue()); diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index 65ac7e510..76b9c3cde 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -64,286 +64,266 @@ public SortedSets(ITestOutputHelper output, SharedConnectionFixture fixture) : b [Fact] public void SortedSetCombine() { - using (var conn = Create()) - { - Skip.IfBelow(conn, RedisFeatures.v6_2_0); + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); - var db = conn.GetDatabase(); - var key1 = Me(); - db.KeyDelete(key1, CommandFlags.FireAndForget); - var key2 = Me() + "2"; - db.KeyDelete(key2, CommandFlags.FireAndForget); + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); - db.SortedSetAdd(key1, entries); - db.SortedSetAdd(key2, entriesPow3); + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); - var diff = db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2}); - Assert.Equal(5, diff.Length); - Assert.Equal("b", diff[0]); + var diff = db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff.Length); + Assert.Equal("b", diff[0]); - var inter = db.SortedSetCombine(SetOperation.Intersect, new RedisKey[]{ key1, key2}); - Assert.Equal(5, inter.Length); - Assert.Equal("a", inter[0]); + var inter = db.SortedSetCombine(SetOperation.Intersect, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter.Length); + Assert.Equal("a", inter[0]); - var union = db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2}); - Assert.Equal(10, union.Length); - Assert.Equal("a", union[0]); - } + var union = db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union.Length); + Assert.Equal("a", union[0]); } [Fact] public void SortedSetCombineWithScores() { - using (var conn = Create()) - { - Skip.IfBelow(conn, RedisFeatures.v6_2_0); + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); - var db = conn.GetDatabase(); - var key1 = Me(); - db.KeyDelete(key1, CommandFlags.FireAndForget); - var key2 = Me() + "2"; - db.KeyDelete(key2, CommandFlags.FireAndForget); + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); - db.SortedSetAdd(key1, entries); - db.SortedSetAdd(key2, entriesPow3); + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); - var diff = db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2}); - Assert.Equal(5, diff.Length); - Assert.Equal(new SortedSetEntry("b", 2), diff[0]); + var diff = db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff.Length); + Assert.Equal(new SortedSetEntry("b", 2), diff[0]); - var inter = db.SortedSetCombineWithScores(SetOperation.Intersect, new RedisKey[]{ key1, key2}); - Assert.Equal(5, inter.Length); - Assert.Equal(new SortedSetEntry("a", 2), inter[0]); + var inter = db.SortedSetCombineWithScores(SetOperation.Intersect, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter.Length); + Assert.Equal(new SortedSetEntry("a", 2), inter[0]); - var union = db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2}); - Assert.Equal(10, union.Length); - Assert.Equal(new SortedSetEntry("a", 2), union[0]); - } + var union = db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union.Length); + Assert.Equal(new SortedSetEntry("a", 2), union[0]); } [Fact] public void SortedSetCombineAndStore() { - using (var conn = Create()) - { - Skip.IfBelow(conn, RedisFeatures.v6_2_0); + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); - var db = conn.GetDatabase(); - var key1 = Me(); - db.KeyDelete(key1, CommandFlags.FireAndForget); - var key2 = Me() + "2"; - db.KeyDelete(key2, CommandFlags.FireAndForget); - var destination = Me() + "dest"; - db.KeyDelete(destination, CommandFlags.FireAndForget); + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + var destination = Me() + "dest"; + db.KeyDelete(destination, CommandFlags.FireAndForget); - db.SortedSetAdd(key1, entries); - db.SortedSetAdd(key2, entriesPow3); + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); - var diff = db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2}); - Assert.Equal(5, diff); + var diff = db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(5, diff); - var inter = db.SortedSetCombineAndStore(SetOperation.Intersect, destination, new RedisKey[]{ key1, key2}); - Assert.Equal(5, inter); + var inter = db.SortedSetCombineAndStore(SetOperation.Intersect, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter); - var union = db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2}); - Assert.Equal(10, union); - } + var union = db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2}); + Assert.Equal(10, union); } [Fact] public void SortedSetCombineErrors() { - using (var conn = Create()) - { - Skip.IfBelow(conn, RedisFeatures.v6_2_0); - - var db = conn.GetDatabase(); - var key1 = Me(); - db.KeyDelete(key1, CommandFlags.FireAndForget); - var key2 = Me() + "2"; - db.KeyDelete(key2, CommandFlags.FireAndForget); - var destination = Me() + "dest"; - db.KeyDelete(destination, CommandFlags.FireAndForget); - - db.SortedSetAdd(key1, entries); - db.SortedSetAdd(key2, entriesPow3); - - // ZDIFF can't be used with weights - Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); - // ZDIFF can't be used with aggregation - Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - // too many weights - Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); - } + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + var destination = Me() + "dest"; + db.KeyDelete(destination, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + // ZDIFF can't be used with weights + Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + // ZDIFF can't be used with aggregation + Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); + // too many weights + Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); } [Fact] public void SortedSetIntersectionLength() { - using (var conn = Create()) - { - Skip.IfBelow(conn, RedisFeatures.v6_9_240); + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_9_240); - var db = conn.GetDatabase(); - var key1 = Me(); - db.KeyDelete(key1, CommandFlags.FireAndForget); - var key2 = Me() + "2"; - db.KeyDelete(key2, CommandFlags.FireAndForget); + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); - db.SortedSetAdd(key1, entries); - db.SortedSetAdd(key2, entriesPow3); + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); - var inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}); - Assert.Equal(5, inter); + var inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}); + Assert.Equal(5, inter); - // with limit - inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}, 3); - Assert.Equal(3, inter); - } + // with limit + inter = db.SortedSetIntersectionLength(new RedisKey[]{ key1, key2}, 3); + Assert.Equal(3, inter); } [Fact] public void SortedSetPopMulti_Multi() { - using (var conn = Create()) - { - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); + using var conn = Create(); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var first = db.SortedSetPop(key, Order.Ascending); - Assert.True(first.HasValue); - Assert.Equal(entries[0], first.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var first = db.SortedSetPop(key, Order.Ascending); + Assert.True(first.HasValue); + Assert.Equal(entries[0], first.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var lasts = db.SortedSetPop(key, 2, Order.Descending); - Assert.Equal(2, lasts.Length); - Assert.Equal(entries[9], lasts[0]); - Assert.Equal(entries[8], lasts[1]); - Assert.Equal(7, db.SortedSetLength(key)); - } + var lasts = db.SortedSetPop(key, 2, Order.Descending); + Assert.Equal(2, lasts.Length); + Assert.Equal(entries[9], lasts[0]); + Assert.Equal(entries[8], lasts[1]); + Assert.Equal(7, db.SortedSetLength(key)); } [Fact] public void SortedSetPopMulti_Single() { - using (var conn = Create()) - { - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); + using var conn = Create(); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var last = db.SortedSetPop(key, Order.Descending); - Assert.True(last.HasValue); - Assert.Equal(entries[9], last.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var last = db.SortedSetPop(key, Order.Descending); + Assert.True(last.HasValue); + Assert.Equal(entries[9], last.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var firsts = db.SortedSetPop(key, 1, Order.Ascending); - Assert.Single(firsts); - Assert.Equal(entries[0], firsts[0]); - Assert.Equal(8, db.SortedSetLength(key)); - } + var firsts = db.SortedSetPop(key, 1, Order.Ascending); + Assert.Single(firsts); + Assert.Equal(entries[0], firsts[0]); + Assert.Equal(8, db.SortedSetLength(key)); } [Fact] public async Task SortedSetPopMulti_Multi_Async() { - using (var conn = Create()) - { - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); + using var conn = Create(); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var last = await db.SortedSetPopAsync(key, Order.Descending).ForAwait(); - Assert.True(last.HasValue); - Assert.Equal(entries[9], last.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var last = await db.SortedSetPopAsync(key, Order.Descending).ForAwait(); + Assert.True(last.HasValue); + Assert.Equal(entries[9], last.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var moreLasts = await db.SortedSetPopAsync(key, 2, Order.Descending).ForAwait(); - Assert.Equal(2, moreLasts.Length); - Assert.Equal(entries[8], moreLasts[0]); - Assert.Equal(entries[7], moreLasts[1]); - Assert.Equal(7, db.SortedSetLength(key)); - } + var moreLasts = await db.SortedSetPopAsync(key, 2, Order.Descending).ForAwait(); + Assert.Equal(2, moreLasts.Length); + Assert.Equal(entries[8], moreLasts[0]); + Assert.Equal(entries[7], moreLasts[1]); + Assert.Equal(7, db.SortedSetLength(key)); } [Fact] public async Task SortedSetPopMulti_Single_Async() { - using (var conn = Create()) - { - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); + using var conn = Create(); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var first = await db.SortedSetPopAsync(key).ForAwait(); - Assert.True(first.HasValue); - Assert.Equal(entries[0], first.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var first = await db.SortedSetPopAsync(key).ForAwait(); + Assert.True(first.HasValue); + Assert.Equal(entries[0], first.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var moreFirsts = await db.SortedSetPopAsync(key, 1).ForAwait(); - Assert.Single(moreFirsts); - Assert.Equal(entries[1], moreFirsts[0]); - Assert.Equal(8, db.SortedSetLength(key)); - } + var moreFirsts = await db.SortedSetPopAsync(key, 1).ForAwait(); + Assert.Single(moreFirsts); + Assert.Equal(entries[1], moreFirsts[0]); + Assert.Equal(8, db.SortedSetLength(key)); } [Fact] public async Task SortedSetPopMulti_Zero_Async() { - using (var conn = Create()) - { - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); + using var conn = Create(); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetPop), r => r.SortedSetPop); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var t = db.SortedSetPopAsync(key, count: 0); - Assert.True(t.IsCompleted); // sync - var arr = await t; - Assert.NotNull(arr); - Assert.Empty(arr); + var t = db.SortedSetPopAsync(key, count: 0); + Assert.True(t.IsCompleted); // sync + var arr = await t; + Assert.NotNull(arr); + Assert.Empty(arr); - Assert.Equal(10, db.SortedSetLength(key)); - } + Assert.Equal(10, db.SortedSetLength(key)); } [Fact] public async Task SortedSetRangeStoreByRankAsync() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 0, -1); Assert.Equal(entries.Length, res); @@ -353,14 +333,14 @@ public async Task SortedSetRangeStoreByRankAsync() public async Task SortedSetRangeStoreByRankLimitedAsync() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 1, 4); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -375,14 +355,14 @@ public async Task SortedSetRangeStoreByRankLimitedAsync() public async Task SortedSetRangeStoreByScoreAsync() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 64, 128, SortedSetOrder.ByScore); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -397,14 +377,14 @@ public async Task SortedSetRangeStoreByScoreAsync() public async Task SortedSetRangeStoreByScoreAsyncDefault() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -419,14 +399,14 @@ public async Task SortedSetRangeStoreByScoreAsyncDefault() public async Task SortedSetRangeStoreByScoreAsyncLimited() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore, skip: 1, take: 6); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -441,14 +421,14 @@ public async Task SortedSetRangeStoreByScoreAsyncLimited() public async Task SortedSetRangeStoreByScoreAsyncExclusiveRange() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 32, 256, SortedSetOrder.ByScore, exclude: Exclude.Both); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -463,14 +443,14 @@ public async Task SortedSetRangeStoreByScoreAsyncExclusiveRange() public async Task SortedSetRangeStoreByScoreAsyncReverse() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, start: double.PositiveInfinity, double.NegativeInfinity, SortedSetOrder.ByScore, order: Order.Descending); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -485,19 +465,19 @@ public async Task SortedSetRangeStoreByScoreAsyncReverse() public async Task SortedSetRangeStoreByLexAsync() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, "a", "j", SortedSetOrder.ByLex); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); Assert.Equal(10, res); - for (var i = 0; i r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, "a", "j", SortedSetOrder.ByLex, Exclude.Both); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); Assert.Equal(8, res); - for (var i = 1; i r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, "j", "a", SortedSetOrder.ByLex, exclude:Exclude.None, order: Order.Descending); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -551,14 +531,14 @@ public async Task SortedSetRangeStoreByLexRevRangeAsync() public void SortedSetRangeStoreByRank() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entries, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, 0, -1); Assert.Equal(entries.Length, res); @@ -568,14 +548,14 @@ public void SortedSetRangeStoreByRank() public void SortedSetRangeStoreByRankLimited() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entries, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, 1, 4); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -590,14 +570,14 @@ public void SortedSetRangeStoreByRankLimited() public void SortedSetRangeStoreByScore() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, 64, 128, SortedSetOrder.ByScore); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -612,14 +592,14 @@ public void SortedSetRangeStoreByScore() public void SortedSetRangeStoreByScoreDefault() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -634,14 +614,14 @@ public void SortedSetRangeStoreByScoreDefault() public void SortedSetRangeStoreByScoreLimited() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey,double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore, skip: 1, take: 6); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -656,14 +636,14 @@ public void SortedSetRangeStoreByScoreLimited() public void SortedSetRangeStoreByScoreExclusiveRange() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, 32, 256, SortedSetOrder.ByScore, exclude: Exclude.Both); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -678,14 +658,14 @@ public void SortedSetRangeStoreByScoreExclusiveRange() public void SortedSetRangeStoreByScoreReverse() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, start: double.PositiveInfinity, double.NegativeInfinity, SortedSetOrder.ByScore, order: Order.Descending); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -700,19 +680,19 @@ public void SortedSetRangeStoreByScoreReverse() public void SortedSetRangeStoreByLex() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, "a", "j", SortedSetOrder.ByLex); var range = db.SortedSetRangeByRankWithScores(destinationKey); Assert.Equal(10, res); - for (var i = 0; i r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, "a", "j", SortedSetOrder.ByLex, Exclude.Both); var range = db.SortedSetRangeByRankWithScores(destinationKey); Assert.Equal(8, res); - for (var i = 1; i r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = db.SortedSetRangeAndStore(sourceKey, destinationKey, "j", "a", SortedSetOrder.ByLex, Exclude.None, Order.Descending); var range = db.SortedSetRangeByRankWithScores(destinationKey); @@ -766,16 +746,16 @@ public void SortedSetRangeStoreByLexRevRange() public void SortedSetRangeStoreFailErroneousTake() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); - var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey,0,-1, take:5)); + var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey, 0, -1, take:5)); Assert.Equal("take", exception.ParamName); } @@ -783,16 +763,16 @@ public void SortedSetRangeStoreFailErroneousTake() public void SortedSetRangeStoreFailExclude() { using var conn = Create(); - Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r=> r.SortedSetRangeStore); + Skip.IfMissingFeature(conn, nameof(RedisFeatures.SortedSetRangeStore), r => r.SortedSetRangeStore); var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); - var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey,0,-1, exclude: Exclude.Both)); + var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey, 0, -1, exclude: Exclude.Both)); Assert.Equal("exclude", exception.ParamName); } } From 5c0d00ffe49302581b4f5ef9ca826f844d032b56 Mon Sep 17 00:00:00 2001 From: Avital-Fine Date: Mon, 11 Apr 2022 13:39:17 +0300 Subject: [PATCH 04/11] use v7_0_0_rc1 --- tests/StackExchange.Redis.Tests/SortedSets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index 15d9eb67a..292f58458 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -179,7 +179,7 @@ public void SortedSetCombineErrors() public void SortedSetIntersectionLength() { using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v6_9_240); + Skip.IfBelow(conn, RedisFeatures.v7_0_0_rc1); var db = conn.GetDatabase(); var key1 = Me(); From 697a15a01674156e5243840c85fe311a9050dfd3 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Mon, 11 Apr 2022 19:56:37 -0400 Subject: [PATCH 05/11] Doc updates --- .../Interfaces/IDatabase.cs | 22 +++++++++---------- .../Interfaces/IDatabaseAsync.cs | 20 ++++++++++------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 0b74192e3..fec07218d 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1342,14 +1342,14 @@ public interface IDatabase : IRedis, IDatabaseAsync long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); /// - /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client, optionally performing - /// a specific aggregation (defaults to sum). - /// The different operations can't be used with weights or aggregation. + /// Computes a set operation for multiple sorted sets (optionally using per-set ), + /// optionally performing a specific aggregation (defaults to ). + /// cannot be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. /// The optional weights per set that correspond to . - /// The aggregation method (defaults to sum). + /// The aggregation method (defaults to ). /// The flags to use for this operation. /// https://redis.io/commands/zunion /// https://redis.io/commands/zinter @@ -1358,14 +1358,14 @@ public interface IDatabase : IRedis, IDatabaseAsync RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); /// - /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client with scores, optionally performing - /// a specific aggregation (defaults to sum). - /// The different operations can't be used with weights or aggregation. + /// Computes a set operation for multiple sorted sets (optionally using per-set ), + /// optionally performing a specific aggregation (defaults to ). + /// cannot be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. /// The optional weights per set that correspond to . - /// The aggregation method (defaults to sum). + /// The aggregation method (defaults to ). /// The flags to use for this operation. /// https://redis.io/commands/zunion /// https://redis.io/commands/zinter @@ -1376,7 +1376,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing /// a specific aggregation (defaults to sum). - /// The different operations can't be used with weights or aggregation. + /// cannot be used with aggregation. /// /// The operation to perform. /// The key to store the results in. @@ -1393,7 +1393,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// /// 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). - /// The different operations can't be used with weights or aggregation. + /// cannot be used with aggregation. /// /// The operation to perform. /// The key to store the results in. @@ -1431,7 +1431,7 @@ public interface IDatabase : IRedis, IDatabaseAsync double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None); /// - /// Returns the cardinality of the intersaction of the Sorted sets. + /// Returns the cardinality of the intersection of the sorted sets at . /// /// The keys of the sorted sets. /// If the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality (defaults to 0 and means unlimited). diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 79c3d286f..ecb5174b6 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -1307,13 +1307,14 @@ public interface IDatabaseAsync : IRedisAsync Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); /// - /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client, optionally performing - /// a specific aggregation (defaults to sum). + /// Computes a set operation for multiple sorted sets (optionally using per-set ), + /// optionally performing a specific aggregation (defaults to ). + /// cannot be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. /// The optional weights per set that correspond to . - /// The aggregation method (defaults to sum). + /// The aggregation method (defaults to ). /// The flags to use for this operation. /// https://redis.io/commands/zunion /// https://redis.io/commands/zinter @@ -1322,13 +1323,14 @@ public interface IDatabaseAsync : IRedisAsync Task SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None); /// - /// Computes a set operation multiple sorted sets (optionally using per-set weights), and returns to the client with scores, optionally performing - /// a specific aggregation (defaults to sum). + /// Computes a set operation for multiple sorted sets (optionally using per-set ), + /// optionally performing a specific aggregation (defaults to ). + /// cannot be used with weights or aggregation. /// /// The operation to perform. /// The keys of the sorted sets. /// The optional weights per set that correspond to . - /// The aggregation method (defaults to sum). + /// The aggregation method (defaults to ). /// The flags to use for this operation. /// https://redis.io/commands/zunion /// https://redis.io/commands/zinter @@ -1339,6 +1341,7 @@ public interface IDatabaseAsync : IRedisAsync /// /// Computes a set operation over two sorted sets, and stores the result in destination, optionally performing /// a specific aggregation (defaults to sum). + /// cannot be used with aggregation. /// /// The operation to perform. /// The key to store the results in. @@ -1355,6 +1358,7 @@ public interface IDatabaseAsync : IRedisAsync /// /// 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). + /// cannot be used with aggregation. /// /// The operation to perform. /// The key to store the results in. @@ -1392,10 +1396,10 @@ public interface IDatabaseAsync : IRedisAsync Task SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None); /// - /// Returns the cardinality of the intersaction of the Sorted sets. + /// Returns the cardinality of the intersection of the sorted sets at . /// /// The keys of the sorted sets. - /// If the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality (defaults to 0 and means unlimited). + /// If the intersection cardinality reaches partway through the computation, the algorithm will exit and yield as the cardinality (defaults to 0 meaning unlimited). /// The flags to use for this operation. /// The number of elements in the resulting intersection. /// https://redis.io/commands/zintercard From 39be688c2cfe50b5beea2cc5ec6f12b3bf029c42 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Mon, 11 Apr 2022 21:13:34 -0400 Subject: [PATCH 06/11] Tweak efficiency and error messages --- .../Interfaces/IDatabase.cs | 2 +- src/StackExchange.Redis/RedisDatabase.cs | 114 +++++++++++------- tests/StackExchange.Redis.Tests/SortedSets.cs | 29 +++-- 3 files changed, 93 insertions(+), 52 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index fec07218d..7babbba1a 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1434,7 +1434,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// Returns the cardinality of the intersection of the sorted sets at . /// /// The keys of the sorted sets. - /// If the intersection cardinality reaches limit partway through the computation, the algorithm will exit and yield limit as the cardinality (defaults to 0 and means unlimited). + /// If the intersection cardinality reaches partway through the computation, the algorithm will exit and yield as the cardinality (defaults to 0 meaning unlimited). /// The flags to use for this operation. /// The number of elements in the resulting intersection. /// https://redis.io/commands/zintercard diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index afbc9e659..15ac93058 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3196,17 +3196,31 @@ private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation { SetOperation.Intersect => RedisCommand.ZINTERSTORE, SetOperation.Union => RedisCommand.ZUNIONSTORE, - SetOperation.Difference => RedisCommand.ZDIFFSTORE, + SetOperation.Difference => + // Difference can't have weights or aggregation + weights != null || aggregate != Aggregate.Sum + ? throw new ArgumentException("ZDIFFSTORE cannot be used with weights or aggregation.") + : RedisCommand.ZDIFFSTORE, _ => throw new ArgumentOutOfRangeException(nameof(operation)), }; - if (keys == null) throw new ArgumentNullException(nameof(keys)); - // Difference operation can't have weights or aggregation - if (operation == SetOperation.Difference && (weights != null || aggregate != Aggregate.Sum)) - throw new ArgumentException("ZDIFFSTORE cannot be used with weights or with aggregation."); + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + if (weights != null && keys.Length != weights.Length) + { + throw new ArgumentException("Keys and weights should have the same number of elements.", nameof(weights)); + } + + RedisValue[] values = RedisValue.EmptyArray; - List? values = null; - AddWeightsAndAggregation(ref values, weights, aggregate); - return new SortedSetCombineAndStoreCommandMessage(Database, flags, command, destination, keys, values?.ToArray() ?? RedisValue.EmptyArray); + var argsLength = (weights?.Length > 0 ? 1 + weights.Length : 0) + (aggregate != Aggregate.Sum ? 2 : 0); + if (argsLength > 0) + { + values = new RedisValue[argsLength]; + AddWeightsAggregationAndScore(values, weights, aggregate); + } + return new SortedSetCombineAndStoreCommandMessage(Database, flags, command, destination, keys, values); } private Message GetSortedSetCombineCommandMessage(SetOperation operation, RedisKey[] keys, double[]? weights, Aggregate aggregate, bool withScores, CommandFlags flags) @@ -3215,50 +3229,66 @@ private Message GetSortedSetCombineCommandMessage(SetOperation operation, RedisK { SetOperation.Intersect => RedisCommand.ZINTER, SetOperation.Union => RedisCommand.ZUNION, - SetOperation.Difference => RedisCommand.ZDIFF, + SetOperation.Difference => + // Difference can't have weights or aggregation + weights != null || aggregate != Aggregate.Sum + ? throw new ArgumentException("ZDIFF cannot be used with weights or aggregation.") + : RedisCommand.ZDIFF, _ => throw new ArgumentOutOfRangeException(nameof(operation)), }; - if (keys == null) throw new ArgumentNullException(nameof(keys)); - - var values = new List( - keys.Length + - (weights != null ? 1 + weights.Length : 0) + - (aggregate != Aggregate.Sum ? 2 : 0) - ); + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + if (weights != null && keys.Length != weights.Length) + { + throw new ArgumentException("Keys and weights should have the same number of elements.", nameof(weights)); + } - values.Add(keys.Length); - foreach(var key in keys) - values.Add(key.AsRedisValue()); - AddWeightsAndAggregation(ref values, weights, aggregate); - if (withScores) + var i = 0; + var values = new RedisValue[1 + keys.Length + + (weights?.Length > 0 ? 1 + weights.Length : 0) + + (aggregate != Aggregate.Sum ? 2 : 0) + + (withScores ? 1 : 0)]; + values[i++] = keys.Length; + foreach (var key in keys) { - values?.Add(RedisLiterals.WITHSCORES); + values[i++] = key.AsRedisValue(); } - return Message.Create(Database, flags, command, values?.ToArray() ?? RedisValue.EmptyArray); + AddWeightsAggregationAndScore(values.AsSpan(i), weights, aggregate, withScores: withScores); + return Message.Create(Database, flags, command, values ?? RedisValue.EmptyArray); } - private void AddWeightsAndAggregation(ref List? values, double[]? weights, Aggregate aggregate) + private void AddWeightsAggregationAndScore(Span values, double[]? weights, Aggregate aggregate, bool withScores = false) { - if (weights != null && weights.Length != 0) + int i = 0; + if (weights?.Length > 0) { - (values ??= new List()).Add(RedisLiterals.WEIGHTS); + values[i++] = RedisLiterals.WEIGHTS; foreach (var weight in weights) - values.Add(weight); + { + values[i++] = weight; + } } switch (aggregate) { - case Aggregate.Sum: break; // default + case Aggregate.Sum: + break; // add nothing - Redis default case Aggregate.Min: - (values ??= new List()).Add(RedisLiterals.AGGREGATE); - values.Add(RedisLiterals.MIN); + values[i++] = RedisLiterals.AGGREGATE; + values[i++] = RedisLiterals.MIN; break; case Aggregate.Max: - (values ??= new List()).Add(RedisLiterals.AGGREGATE); - values.Add(RedisLiterals.MAX); + values[i++] = RedisLiterals.AGGREGATE; + values[i++] = RedisLiterals.MAX; break; default: throw new ArgumentOutOfRangeException(nameof(aggregate)); } + if (withScores) + { + values[i++] = RedisLiterals.WITHSCORES; + } } private Message GetSortedSetLengthMessage(RedisKey key, double min, double max, Exclude exclude, CommandFlags flags) @@ -3275,16 +3305,18 @@ private Message GetSortedSetIntersectionLengthMessage(RedisKey[] keys, long limi { if (keys == null) throw new ArgumentNullException(nameof(keys)); - var values = new List(1 + keys.Length + (limit > 0 ? 2 : 0)); - values.Add(keys.Length); - foreach(var key in keys) - values.Add(key.AsRedisValue()); - - if (limit > 0) { - values.Add(RedisLiterals.LIMIT); - values.Add(limit); + var i = 0; + var values = new RedisValue[1 + keys.Length + (limit > 0 ? 2 : 0)]; + values[i++] = keys.Length; + foreach (var key in keys) + { + values[i++] = key.AsRedisValue(); + } + if (limit > 0) + { + values[i++] = RedisLiterals.LIMIT; + values[i++] = limit; } - return Message.Create(Database, flags, RedisCommand.ZINTERCARD, values); } diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index 292f58458..01927926c 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -162,17 +162,26 @@ public void SortedSetCombineErrors() db.SortedSetAdd(key2, entriesPow3); // ZDIFF can't be used with weights - Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2 })); + var ex = Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); // ZDIFF can't be used with aggregation - Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[]{ key1, key2 }, aggregate: Aggregate.Max)); - // too many weights - Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); - Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); - Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[]{ key1, key2 }, new double[]{ 1, 2, 3 })); + ex = Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Difference, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); + // Too many weights + ex = Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineWithScores(SetOperation.Union, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); + ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); } [Fact] From ce2bae20fab082319f7a966712178d4f1413b49d Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Mon, 11 Apr 2022 21:43:16 -0400 Subject: [PATCH 07/11] Add release notes --- docs/ReleaseNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 00ef24fb2..d9127f82f 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -9,6 +9,7 @@ - Note: does *not* increment a major version (as these are warnings to consumers), because: they're warnings (errors are opt-in), removing obsolete types with a 3.0 rev _would_ be binary breaking (this isn't), and reving to 3.0 would cause binding redirect pain for consumers. Bumping from 2.5 to 2.6 only for this change. - Adds: Support for `COPY` with `.KeyCopy()`/`.KeyCopyAsync()` ([#2064 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2064)) - Adds: Support for `LMOVE` with `.ListMove()`/`.ListMoveAsync()` ([#2065 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2065)) +- 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/2078)) - Adds: Support for `SINTERCARD` with `.SetIntersectionLength()`/`.SetIntersectionLengthAsync()` ([#2078 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2078)) ## 2.5.61 From 33273d84678ebf5e70f71be256696137976e3210 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Tue, 12 Apr 2022 09:08:24 -0400 Subject: [PATCH 08/11] Fix release notes link --- docs/ReleaseNotes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 0ac8f4cd0..b53a3beb9 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -10,7 +10,7 @@ - Adds: Support for `COPY` with `.KeyCopy()`/`.KeyCopyAsync()` ([#2064 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2064)) - Adds: Support for `LMOVE` with `.ListMove()`/`.ListMoveAsync()` ([#2065 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2065)) - 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/2078)) +- 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)) ## 2.5.61 From 7cdae8fdf123b5f34e95d7a9fce3105a725cc8f3 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Tue, 12 Apr 2022 09:13:36 -0400 Subject: [PATCH 09/11] Revert test diffs --- tests/StackExchange.Redis.Tests/SortedSets.cs | 207 +++++++++--------- 1 file changed, 108 insertions(+), 99 deletions(-) diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index 01927926c..b86d2f43a 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -210,116 +210,126 @@ public void SortedSetIntersectionLength() [Fact] public void SortedSetPopMulti_Multi() { - using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v5_0_0); + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v5_0_0); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var first = db.SortedSetPop(key, Order.Ascending); - Assert.True(first.HasValue); - Assert.Equal(entries[0], first.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var first = db.SortedSetPop(key, Order.Ascending); + Assert.True(first.HasValue); + Assert.Equal(entries[0], first.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var lasts = db.SortedSetPop(key, 2, Order.Descending); - Assert.Equal(2, lasts.Length); - Assert.Equal(entries[9], lasts[0]); - Assert.Equal(entries[8], lasts[1]); - Assert.Equal(7, db.SortedSetLength(key)); + var lasts = db.SortedSetPop(key, 2, Order.Descending); + Assert.Equal(2, lasts.Length); + Assert.Equal(entries[9], lasts[0]); + Assert.Equal(entries[8], lasts[1]); + Assert.Equal(7, db.SortedSetLength(key)); + } } [Fact] public void SortedSetPopMulti_Single() { - using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v5_0_0); + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v5_0_0); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var last = db.SortedSetPop(key, Order.Descending); - Assert.True(last.HasValue); - Assert.Equal(entries[9], last.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var last = db.SortedSetPop(key, Order.Descending); + Assert.True(last.HasValue); + Assert.Equal(entries[9], last.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var firsts = db.SortedSetPop(key, 1, Order.Ascending); - Assert.Single(firsts); - Assert.Equal(entries[0], firsts[0]); - Assert.Equal(8, db.SortedSetLength(key)); + var firsts = db.SortedSetPop(key, 1, Order.Ascending); + Assert.Single(firsts); + Assert.Equal(entries[0], firsts[0]); + Assert.Equal(8, db.SortedSetLength(key)); + } } [Fact] public async Task SortedSetPopMulti_Multi_Async() { - using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v5_0_0); + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v5_0_0); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var last = await db.SortedSetPopAsync(key, Order.Descending).ForAwait(); - Assert.True(last.HasValue); - Assert.Equal(entries[9], last.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var last = await db.SortedSetPopAsync(key, Order.Descending).ForAwait(); + Assert.True(last.HasValue); + Assert.Equal(entries[9], last.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var moreLasts = await db.SortedSetPopAsync(key, 2, Order.Descending).ForAwait(); - Assert.Equal(2, moreLasts.Length); - Assert.Equal(entries[8], moreLasts[0]); - Assert.Equal(entries[7], moreLasts[1]); - Assert.Equal(7, db.SortedSetLength(key)); + var moreLasts = await db.SortedSetPopAsync(key, 2, Order.Descending).ForAwait(); + Assert.Equal(2, moreLasts.Length); + Assert.Equal(entries[8], moreLasts[0]); + Assert.Equal(entries[7], moreLasts[1]); + Assert.Equal(7, db.SortedSetLength(key)); + } } [Fact] public async Task SortedSetPopMulti_Single_Async() { - using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v5_0_0); + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v5_0_0); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var first = await db.SortedSetPopAsync(key).ForAwait(); - Assert.True(first.HasValue); - Assert.Equal(entries[0], first.Value); - Assert.Equal(9, db.SortedSetLength(key)); + var first = await db.SortedSetPopAsync(key).ForAwait(); + Assert.True(first.HasValue); + Assert.Equal(entries[0], first.Value); + Assert.Equal(9, db.SortedSetLength(key)); - var moreFirsts = await db.SortedSetPopAsync(key, 1).ForAwait(); - Assert.Single(moreFirsts); - Assert.Equal(entries[1], moreFirsts[0]); - Assert.Equal(8, db.SortedSetLength(key)); + var moreFirsts = await db.SortedSetPopAsync(key, 1).ForAwait(); + Assert.Single(moreFirsts); + Assert.Equal(entries[1], moreFirsts[0]); + Assert.Equal(8, db.SortedSetLength(key)); + } } [Fact] public async Task SortedSetPopMulti_Zero_Async() { - using var conn = Create(); - Skip.IfBelow(conn, RedisFeatures.v5_0_0); + using (var conn = Create()) + { + Skip.IfBelow(conn, RedisFeatures.v5_0_0); - var db = conn.GetDatabase(); - var key = Me(); + var db = conn.GetDatabase(); + var key = Me(); - db.KeyDelete(key, CommandFlags.FireAndForget); - db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + db.KeyDelete(key, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); - var t = db.SortedSetPopAsync(key, count: 0); - Assert.True(t.IsCompleted); // sync - var arr = await t; - Assert.NotNull(arr); - Assert.Empty(arr); + var t = db.SortedSetPopAsync(key, count: 0); + Assert.True(t.IsCompleted); // sync + var arr = await t; + Assert.NotNull(arr); + Assert.Empty(arr); - Assert.Equal(10, db.SortedSetLength(key)); + Assert.Equal(10, db.SortedSetLength(key)); + } } [Fact] @@ -327,13 +337,12 @@ public async Task SortedSetRangeStoreByRankAsync() { using var conn = Create(); Skip.IfBelow(conn, RedisFeatures.v6_2_0); - var db = conn.GetDatabase(); var me = Me(); var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 0, -1); Assert.Equal(entries.Length, res); @@ -350,7 +359,7 @@ public async Task SortedSetRangeStoreByRankLimitedAsync() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 1, 4); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -372,7 +381,7 @@ public async Task SortedSetRangeStoreByScoreAsync() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 64, 128, SortedSetOrder.ByScore); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -394,7 +403,7 @@ public async Task SortedSetRangeStoreByScoreAsyncDefault() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -416,7 +425,7 @@ public async Task SortedSetRangeStoreByScoreAsyncLimited() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, double.NegativeInfinity, double.PositiveInfinity, SortedSetOrder.ByScore, skip: 1, take: 6); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -438,7 +447,7 @@ public async Task SortedSetRangeStoreByScoreAsyncExclusiveRange() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, 32, 256, SortedSetOrder.ByScore, exclude: Exclude.Both); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -460,7 +469,7 @@ public async Task SortedSetRangeStoreByScoreAsyncReverse() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, entriesPow2, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, start: double.PositiveInfinity, double.NegativeInfinity, SortedSetOrder.ByScore, order: Order.Descending); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); @@ -482,12 +491,12 @@ public async Task SortedSetRangeStoreByLexAsync() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); await db.SortedSetAddAsync(sourceKey, lexEntries, CommandFlags.FireAndForget); var res = await db.SortedSetRangeAndStoreAsync(sourceKey, destinationKey, "a", "j", SortedSetOrder.ByLex); var range = await db.SortedSetRangeByRankWithScoresAsync(destinationKey); Assert.Equal(10, res); - for (var i = 0; i < lexEntries.Length; i++) + for (var i = 0; i (()=>db.SortedSetRangeAndStore(sourceKey, destinationKey, 0, -1, take:5)); + var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey,0,-1, take:5)); Assert.Equal("take", exception.ParamName); } @@ -780,9 +789,9 @@ public void SortedSetRangeStoreFailExclude() var sourceKey = $"{me}:ZSetSource"; var destinationKey = $"{me}:ZSetDestination"; - db.KeyDelete(new RedisKey[] { sourceKey, destinationKey }, CommandFlags.FireAndForget); + db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); - var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey, 0, -1, exclude: Exclude.Both)); + var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey,0,-1, exclude: Exclude.Both)); Assert.Equal("exclude", exception.ParamName); } } From 9f98e5d8a5e3ef6dc2963bb4f83596affcf363ff Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Tue, 12 Apr 2022 09:19:05 -0400 Subject: [PATCH 10/11] Add async tests --- tests/StackExchange.Redis.Tests/SortedSets.cs | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index b86d2f43a..fec0d82ac 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -89,6 +89,35 @@ public void SortedSetCombine() Assert.Equal("a", union[0]); } + + [Fact] + public async Task SortedSetCombineAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = await db.SortedSetCombineAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }); + Assert.Equal(5, diff.Length); + Assert.Equal("b", diff[0]); + + var inter = await db.SortedSetCombineAsync(SetOperation.Intersect, new RedisKey[] { key1, key2 }); + Assert.Equal(5, inter.Length); + Assert.Equal("a", inter[0]); + + var union = await db.SortedSetCombineAsync(SetOperation.Union, new RedisKey[] { key1, key2 }); + Assert.Equal(10, union.Length); + Assert.Equal("a", union[0]); + } + [Fact] public void SortedSetCombineWithScores() { @@ -117,6 +146,35 @@ public void SortedSetCombineWithScores() Assert.Equal(new SortedSetEntry("a", 2), union[0]); } + + [Fact] + public async Task SortedSetCombineWithScoresAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = await db.SortedSetCombineWithScoresAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }); + Assert.Equal(5, diff.Length); + Assert.Equal(new SortedSetEntry("b", 2), diff[0]); + + var inter = await db.SortedSetCombineWithScoresAsync(SetOperation.Intersect, new RedisKey[] { key1, key2 }); + Assert.Equal(5, inter.Length); + Assert.Equal(new SortedSetEntry("a", 2), inter[0]); + + var union = await db.SortedSetCombineWithScoresAsync(SetOperation.Union, new RedisKey[] { key1, key2 }); + Assert.Equal(10, union.Length); + Assert.Equal(new SortedSetEntry("a", 2), union[0]); + } + [Fact] public void SortedSetCombineAndStore() { @@ -144,8 +202,36 @@ public void SortedSetCombineAndStore() Assert.Equal(10, union); } + [Fact] - public void SortedSetCombineErrors() + public async Task SortedSetCombineAndStoreAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + var destination = Me() + "dest"; + db.KeyDelete(destination, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var diff = await db.SortedSetCombineAndStoreAsync(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }); + Assert.Equal(5, diff); + + var inter = await db.SortedSetCombineAndStoreAsync(SetOperation.Intersect, destination, new RedisKey[] { key1, key2 }); + Assert.Equal(5, inter); + + var union = await db.SortedSetCombineAndStoreAsync(SetOperation.Union, destination, new RedisKey[] { key1, key2 }); + Assert.Equal(10, union); + } + + [Fact] + public async Task SortedSetCombineErrors() { using var conn = Create(); Skip.IfBelow(conn, RedisFeatures.v6_2_0); @@ -168,6 +254,14 @@ public void SortedSetCombineErrors() Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); + // and Async... + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineWithScoresAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAndStoreAsync(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2 })); + Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); + // ZDIFF can't be used with aggregation ex = Assert.Throws(() => db.SortedSetCombine(SetOperation.Difference, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); @@ -175,6 +269,14 @@ public void SortedSetCombineErrors() Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); + // and Async... + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineWithScoresAsync(SetOperation.Difference, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFF cannot be used with weights or aggregation.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAndStoreAsync(SetOperation.Difference, destination, new RedisKey[] { key1, key2 }, aggregate: Aggregate.Max)); + Assert.Equal("ZDIFFSTORE cannot be used with weights or aggregation.", ex.Message); + // Too many weights ex = Assert.Throws(() => db.SortedSetCombine(SetOperation.Union, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); @@ -182,6 +284,13 @@ public void SortedSetCombineErrors() Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); ex = Assert.Throws(() => db.SortedSetCombineAndStore(SetOperation.Union, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); + // and Async... + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAsync(SetOperation.Union, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineWithScoresAsync(SetOperation.Union, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); + ex = await Assert.ThrowsAsync(() => db.SortedSetCombineAndStoreAsync(SetOperation.Union, destination, new RedisKey[] { key1, key2 }, new double[] { 1, 2, 3 })); + Assert.StartsWith("Keys and weights should have the same number of elements.", ex.Message); } [Fact] @@ -207,6 +316,29 @@ public void SortedSetIntersectionLength() Assert.Equal(3, inter); } + [Fact] + public async Task SortedSetIntersectionLengthAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v7_0_0_rc1); + + var db = conn.GetDatabase(); + var key1 = Me(); + db.KeyDelete(key1, CommandFlags.FireAndForget); + var key2 = Me() + "2"; + db.KeyDelete(key2, CommandFlags.FireAndForget); + + db.SortedSetAdd(key1, entries); + db.SortedSetAdd(key2, entriesPow3); + + var inter = await db.SortedSetIntersectionLengthAsync(new RedisKey[] { key1, key2 }); + Assert.Equal(5, inter); + + // with limit + inter = await db.SortedSetIntersectionLengthAsync(new RedisKey[] { key1, key2 }, 3); + Assert.Equal(3, inter); + } + [Fact] public void SortedSetPopMulti_Multi() { From c6003396eeac2ed604d99862b3ea3abb74aca3a9 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Tue, 12 Apr 2022 10:38:16 -0400 Subject: [PATCH 11/11] More code sharing! --- src/StackExchange.Redis/Enums/SetOperation.cs | 18 ++++++++++- src/StackExchange.Redis/RedisDatabase.cs | 32 ++++++------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/StackExchange.Redis/Enums/SetOperation.cs b/src/StackExchange.Redis/Enums/SetOperation.cs index b88b0c8f0..f7cf4f1df 100644 --- a/src/StackExchange.Redis/Enums/SetOperation.cs +++ b/src/StackExchange.Redis/Enums/SetOperation.cs @@ -1,4 +1,6 @@ -namespace StackExchange.Redis +using System; + +namespace StackExchange.Redis { /// /// Describes an algebraic set operation that can be performed to combine multiple sets. @@ -18,4 +20,18 @@ public enum SetOperation /// 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)), + }; + } } diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index db4b5cc09..269211ee1 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3204,21 +3204,15 @@ private Message GetSortedSetAddMessage(RedisKey destination, RedisKey key, long private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights, Aggregate aggregate, CommandFlags flags) { - var command = operation switch - { - SetOperation.Intersect => RedisCommand.ZINTERSTORE, - SetOperation.Union => RedisCommand.ZUNIONSTORE, - SetOperation.Difference => - // Difference can't have weights or aggregation - weights != null || aggregate != Aggregate.Sum - ? throw new ArgumentException("ZDIFFSTORE cannot be used with weights or aggregation.") - : RedisCommand.ZDIFFSTORE, - _ => throw new ArgumentOutOfRangeException(nameof(operation)), - }; + var command = operation.ToCommand(store: true); if (keys == null) { throw new ArgumentNullException(nameof(keys)); } + if (command == RedisCommand.ZDIFFSTORE && (weights != null || aggregate != Aggregate.Sum)) + { + throw new ArgumentException("ZDIFFSTORE cannot be used with weights or aggregation."); + } if (weights != null && keys.Length != weights.Length) { throw new ArgumentException("Keys and weights should have the same number of elements.", nameof(weights)); @@ -3237,21 +3231,15 @@ private Message GetSortedSetCombineAndStoreCommandMessage(SetOperation operation private Message GetSortedSetCombineCommandMessage(SetOperation operation, RedisKey[] keys, double[]? weights, Aggregate aggregate, bool withScores, CommandFlags flags) { - var command = operation switch - { - SetOperation.Intersect => RedisCommand.ZINTER, - SetOperation.Union => RedisCommand.ZUNION, - SetOperation.Difference => - // Difference can't have weights or aggregation - weights != null || aggregate != Aggregate.Sum - ? throw new ArgumentException("ZDIFF cannot be used with weights or aggregation.") - : RedisCommand.ZDIFF, - _ => throw new ArgumentOutOfRangeException(nameof(operation)), - }; + var command = operation.ToCommand(store: false); if (keys == null) { throw new ArgumentNullException(nameof(keys)); } + if (command == RedisCommand.ZDIFF && (weights != null || aggregate != Aggregate.Sum)) + { + throw new ArgumentException("ZDIFF cannot be used with weights or aggregation."); + } if (weights != null && keys.Length != weights.Length) { throw new ArgumentException("Keys and weights should have the same number of elements.", nameof(weights));