diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index b3bc9845e..38d9abf19 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 `ZRANDMEMBER` with `.SortedSetRandomMember()`/`.SortedSetRandomMemberAsync()`, `.SortedSetRandomMembers()`/`.SortedSetRandomMembersAsync()`, and `.SortedSetRandomMembersWithScores()`/`.SortedSetRandomMembersWithScoresAsync()` ([#2076 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2076)) - Adds: Support for `SMISMEMBER` with `.SetContains()`/`.SetContainsAsync()` ([#2077 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2077)) - Adds: Support for `SINTERCARD` with `.SetIntersectionLength()`/`.SetIntersectionLengthAsync()` ([#2078 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2078)) diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index b4d3c5fd7..f0020cda4 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -201,6 +201,7 @@ internal enum RedisCommand ZLEXCOUNT, ZPOPMAX, ZPOPMIN, + ZRANDMEMBER, ZRANGE, ZRANGEBYLEX, ZRANGEBYSCORE, diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index c6887608f..3eef153e4 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1199,11 +1199,11 @@ public interface IDatabase : IRedis, IDatabaseAsync RedisValue[] SetPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None); /// - /// Return a random element from the set value stored at key. + /// Return a random element from the set value stored at . /// /// The key of the set. /// The flags to use for this operation. - /// The randomly selected element, or nil when key does not exist. + /// The randomly selected element, or when does not exist. /// https://redis.io/commands/srandmember RedisValue SetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None); @@ -1215,7 +1215,7 @@ public interface IDatabase : IRedis, IDatabaseAsync /// The key of the set. /// The count of members to get. /// The flags to use for this operation. - /// An array of elements, or an empty array when key does not exist. + /// An array of elements, or an empty array when does not exist. /// https://redis.io/commands/srandmember RedisValue[] SetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None); @@ -1433,6 +1433,53 @@ public interface IDatabase : IRedis, IDatabaseAsync /// https://redis.io/commands/zlexcount long SortedSetLengthByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None); + /// + /// Returns a random element from the sorted set value stored at . + /// + /// The key of the sorted set. + /// The flags to use for this operation. + /// The randomly selected element, or when does not exist. + /// https://redis.io/commands/zrandmember + RedisValue SortedSetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None); + + /// + /// Returns an array of random elements from the sorted set value stored at . + /// + /// The key of the sorted set. + /// + /// + /// If the provided count argument is positive, returns an array of distinct elements. + /// The array's length is either or the sorted set's cardinality (ZCARD), whichever is lower. + /// + /// + /// If called with a negative count, the behavior changes and the command is allowed to return the same element multiple times. + /// In this case, the number of returned elements is the absolute value of the specified count. + /// + /// + /// The flags to use for this operation. + /// The randomly selected elements, or an empty array when does not exist. + /// https://redis.io/commands/zrandmember + RedisValue[] SortedSetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + + /// + /// Returns an array of random elements from the sorted set value stored at . + /// + /// The key of the sorted set. + /// + /// + /// If the provided count argument is positive, returns an array of distinct elements. + /// The array's length is either or the sorted set's cardinality (ZCARD), whichever is lower. + /// + /// + /// If called with a negative count, the behavior changes and the command is allowed to return the same element multiple times. + /// In this case, the number of returned elements is the absolute value of the specified count. + /// + /// + /// The flags to use for this operation. + /// The randomly selected elements with scores, or an empty array when does not exist. + /// https://redis.io/commands/zrandmember + SortedSetEntry[] SortedSetRandomMembersWithScores(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Returns the specified range of elements in the sorted set stored at key. /// By default the elements are considered to be ordered from the lowest to the highest score. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 45074dd6d..1ec8d7e30 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -1397,6 +1397,53 @@ public interface IDatabaseAsync : IRedisAsync /// https://redis.io/commands/zlexcount Task SortedSetLengthByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None); + /// + /// Returns a random element from the sorted set value stored at . + /// + /// The key of the sorted set. + /// The flags to use for this operation. + /// The randomly selected element, or when does not exist. + /// https://redis.io/commands/zrandmember + Task SortedSetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None); + + /// + /// Returns an array of random elements from the sorted set value stored at . + /// + /// The key of the sorted set. + /// + /// + /// If the provided count argument is positive, returns an array of distinct elements. + /// The array's length is either or the sorted set's cardinality (ZCARD), whichever is lower. + /// + /// + /// If called with a negative count, the behavior changes and the command is allowed to return the same element multiple times. + /// In this case, the number of returned elements is the absolute value of the specified count. + /// + /// + /// The flags to use for this operation. + /// The randomly selected elements, or an empty array when does not exist. + /// https://redis.io/commands/zrandmember + Task SortedSetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + + /// + /// Returns an array of random elements from the sorted set value stored at . + /// + /// The key of the sorted set. + /// + /// + /// If the provided count argument is positive, returns an array of distinct elements. + /// The array's length is either or the sorted set's cardinality (ZCARD), whichever is lower. + /// + /// + /// If called with a negative count, the behavior changes and the command is allowed to return the same element multiple times. + /// In this case, the number of returned elements is the absolute value of the specified count. + /// + /// + /// The flags to use for this operation. + /// The randomly selected elements with scores, or an empty array when does not exist. + /// https://redis.io/commands/zrandmember + Task SortedSetRandomMembersWithScoresAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Returns the specified range of elements in the sorted set stored at key. /// By default the elements are considered to be ordered from the lowest to the highest score. diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index 9563d4d13..c56209de1 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -363,6 +363,15 @@ public long SortedSetLength(RedisKey key, double min = -1.0 / 0.0, double max = public long SortedSetLengthByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) => Inner.SortedSetLengthByValue(ToInner(key), min, max, exclude, flags); + public RedisValue SortedSetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMember(ToInner(key), flags); + + public RedisValue[] SortedSetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMembers(ToInner(key), count, flags); + + public SortedSetEntry[] SortedSetRandomMembersWithScores(RedisKey key, long count, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMembersWithScores(ToInner(key), count, flags); + public RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) => Inner.SortedSetRangeByRank(ToInner(key), start, stop, order, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 87f40b608..969637abd 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -376,6 +376,15 @@ public Task SortedSetLengthAsync(RedisKey key, double min = -1.0 / 0.0, do public Task SortedSetLengthByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None) => Inner.SortedSetLengthByValueAsync(ToInner(key), min, max, exclude, flags); + public Task SortedSetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMemberAsync(ToInner(key), flags); + + public Task SortedSetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMembersAsync(ToInner(key), count, flags); + + public Task SortedSetRandomMembersWithScoresAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetRandomMembersWithScoresAsync(ToInner(key), count, flags); + public Task SortedSetRangeAndStoreAsync( RedisKey sourceKey, RedisKey destinationKey, diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 4f07e98e2..b31fa1a9e 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -600,6 +600,9 @@ StackExchange.Redis.IDatabase.SortedSetLength(StackExchange.Redis.RedisKey key, 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[]! StackExchange.Redis.IDatabase.SortedSetPop(StackExchange.Redis.RedisKey key, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.SortedSetEntry? +StackExchange.Redis.IDatabase.SortedSetRandomMember(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue +StackExchange.Redis.IDatabase.SortedSetRandomMembers(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue[]! +StackExchange.Redis.IDatabase.SortedSetRandomMembersWithScores(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.SortedSetEntry[]! StackExchange.Redis.IDatabase.SortedSetRangeByRank(StackExchange.Redis.RedisKey key, long start = 0, long stop = -1, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue[]! StackExchange.Redis.IDatabase.SortedSetRangeAndStore(StackExchange.Redis.RedisKey sourceKey, StackExchange.Redis.RedisKey destinationKey, StackExchange.Redis.RedisValue start, StackExchange.Redis.RedisValue stop, StackExchange.Redis.SortedSetOrder sortedSetOrder = StackExchange.Redis.SortedSetOrder.ByRank, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, long skip = 0, long? take = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.SortedSetRangeByRankWithScores(StackExchange.Redis.RedisKey key, long start = 0, long stop = -1, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.SortedSetEntry[]! @@ -791,6 +794,9 @@ StackExchange.Redis.IDatabaseAsync.SortedSetLengthAsync(StackExchange.Redis.Redi 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! StackExchange.Redis.IDatabaseAsync.SortedSetPopAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetRandomMemberAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetRandomMembersAsync(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetRandomMembersWithScoresAsync(StackExchange.Redis.RedisKey key, long count, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetRangeAndStoreAsync(StackExchange.Redis.RedisKey sourceKey, StackExchange.Redis.RedisKey destinationKey, StackExchange.Redis.RedisValue start, StackExchange.Redis.RedisValue stop, StackExchange.Redis.SortedSetOrder sortedSetOrder = StackExchange.Redis.SortedSetOrder.ByRank, StackExchange.Redis.Exclude exclude = StackExchange.Redis.Exclude.None, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, long skip = 0, long? take = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetRangeByRankAsync(StackExchange.Redis.RedisKey key, long start = 0, long stop = -1, StackExchange.Redis.Order order = StackExchange.Redis.Order.Ascending, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.SortedSetRangeByRankWithScoresAsync(StackExchange.Redis.RedisKey key, long start = 0, long stop = -1, 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 54181f982..aaa647e19 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1674,6 +1674,42 @@ public Task SortedSetLengthAsync(RedisKey key, double min = double.Negativ return ExecuteAsync(msg, ResultProcessor.Int64); } + public RedisValue SortedSetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key); + return ExecuteSync(msg, ResultProcessor.RedisValue); + } + + public RedisValue[] SortedSetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key, count); + return ExecuteSync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); + } + + public SortedSetEntry[] SortedSetRandomMembersWithScores(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key, count, RedisLiterals.WITHSCORES); + return ExecuteSync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); + } + + public Task SortedSetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key); + return ExecuteAsync(msg, ResultProcessor.RedisValue); + } + + public Task SortedSetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key, count); + return ExecuteAsync(msg, ResultProcessor.RedisValueArray, defaultValue: Array.Empty()); + } + + public Task SortedSetRandomMembersWithScoresAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZRANDMEMBER, key, count, RedisLiterals.WITHSCORES); + return ExecuteAsync(msg, ResultProcessor.SortedSetWithScores, defaultValue: Array.Empty()); + } + public RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, order == Order.Descending ? RedisCommand.ZREVRANGE : RedisCommand.ZRANGE, key, start, stop); diff --git a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs index 78db3c07a..5b2018858 100644 --- a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs +++ b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs @@ -778,6 +778,27 @@ public void SortedSetLength() mock.Verify(_ => _.SortedSetLength("prefix:key", 1.23, 1.23, Exclude.Start, CommandFlags.None)); } + [Fact] + public void SortedSetRandomMember() + { + wrapper.SortedSetRandomMember("key", CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMember("prefix:key", CommandFlags.None)); + } + + [Fact] + public void SortedSetRandomMembers() + { + wrapper.SortedSetRandomMembers("key", 2, CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMembers("prefix:key", 2, CommandFlags.None)); + } + + [Fact] + public void SortedSetRandomMembersWithScores() + { + wrapper.SortedSetRandomMembersWithScores("key", 2, CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMembersWithScores("prefix:key", 2, CommandFlags.None)); + } + [Fact] public void SortedSetLengthByValue() { diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index e7cdcc59c..f15779d78 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -177,6 +177,95 @@ public async Task SortedSetPopMulti_Zero_Async() } } + [Fact] + public void SortedSetRandomMembers() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var key0 = Me() + "non-existing"; + + db.KeyDelete(key, CommandFlags.FireAndForget); + db.KeyDelete(key0, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + + // single member + var randMember = db.SortedSetRandomMember(key); + Assert.True(Array.Exists(entries, element => element.Element.Equals(randMember))); + + // with count + var randMemberArray = db.SortedSetRandomMembers(key, 5); + Assert.Equal(5, randMemberArray.Length); + randMemberArray = db.SortedSetRandomMembers(key, 15); + Assert.Equal(10, randMemberArray.Length); + randMemberArray = db.SortedSetRandomMembers(key, -5); + Assert.Equal(5, randMemberArray.Length); + randMemberArray = db.SortedSetRandomMembers(key, -15); + Assert.Equal(15, randMemberArray.Length); + + // with scores + var randMemberArray2 = db.SortedSetRandomMembersWithScores(key, 2); + Assert.Equal(2, randMemberArray2.Length); + foreach (var member in randMemberArray2) + { + Assert.Contains(member, entries); + } + + // check missing key case + randMember = db.SortedSetRandomMember(key0); + Assert.True(randMember.IsNull); + randMemberArray = db.SortedSetRandomMembers(key0, 2); + Assert.True(randMemberArray.Length == 0); + randMemberArray2 = db.SortedSetRandomMembersWithScores(key0, 2); + Assert.True(randMemberArray2.Length == 0); + } + + [Fact] + public async Task SortedSetRandomMembersAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var key0 = Me() + "non-existing"; + + db.KeyDelete(key, CommandFlags.FireAndForget); + db.KeyDelete(key0, CommandFlags.FireAndForget); + db.SortedSetAdd(key, entries, CommandFlags.FireAndForget); + + var randMember = await db.SortedSetRandomMemberAsync(key); + Assert.True(Array.Exists(entries, element => element.Element.Equals(randMember))); + + // with count + var randMemberArray = await db.SortedSetRandomMembersAsync(key, 5); + Assert.Equal(5, randMemberArray.Length); + randMemberArray = await db.SortedSetRandomMembersAsync(key, 15); + Assert.Equal(10, randMemberArray.Length); + randMemberArray = await db.SortedSetRandomMembersAsync(key, -5); + Assert.Equal(5, randMemberArray.Length); + randMemberArray = await db.SortedSetRandomMembersAsync(key, -15); + Assert.Equal(15, randMemberArray.Length); + + // with scores + var randMemberArray2 = await db.SortedSetRandomMembersWithScoresAsync(key, 2); + Assert.Equal(2, randMemberArray2.Length); + foreach (var member in randMemberArray2) + { + Assert.Contains(member, entries); + } + + // check missing key case + randMember = await db.SortedSetRandomMemberAsync(key0); + Assert.True(randMember.IsNull); + randMemberArray = await db.SortedSetRandomMembersAsync(key0, 2); + Assert.True(randMemberArray.Length == 0); + randMemberArray2 = await db.SortedSetRandomMembersWithScoresAsync(key0, 2); + Assert.True(randMemberArray2.Length == 0); + } + [Fact] public async Task SortedSetRangeStoreByRankAsync() { diff --git a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs index 322f71a3f..1f293266d 100644 --- a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs +++ b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs @@ -731,6 +731,27 @@ public void SortedSetLengthByValueAsync() mock.Verify(_ => _.SortedSetLengthByValueAsync("prefix:key", "min", "max", Exclude.Start, CommandFlags.None)); } + [Fact] + public void SortedSetRandomMemberAsync() + { + wrapper.SortedSetRandomMemberAsync("key", CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMemberAsync("prefix:key", CommandFlags.None)); + } + + [Fact] + public void SortedSetRandomMembersAsync() + { + wrapper.SortedSetRandomMembersAsync("key", 2, CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMembersAsync("prefix:key", 2, CommandFlags.None)); + } + + [Fact] + public void SortedSetRandomMemberWithScoresAsync() + { + wrapper.SortedSetRandomMembersWithScoresAsync("key", 2, CommandFlags.None); + mock.Verify(_ => _.SortedSetRandomMembersWithScoresAsync("prefix:key", 2, CommandFlags.None)); + } + [Fact] public void SortedSetRangeByRankAsync() {