diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 8a87fd3a1..5a7cb8b5b 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -14,6 +14,7 @@ - Adds: Support for `ZDIFF`, `ZDIFFSTORE`, `ZINTER`, `ZINTERCARD`, and `ZUNION` with `.SortedSetCombine()`/`.SortedSetCombineAsync()`, `.SortedSetCombineWithScores()`/`.SortedSetCombineWithScoresAsync()`, and `.SortedSetIntersectionLength()`/`.SortedSetIntersectionLengthAsync()` ([#2075 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2075)) - Adds: Support for `SINTERCARD` with `.SetIntersectionLength()`/`.SetIntersectionLengthAsync()` ([#2078 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2078)) - Adds: Support for `LPOS` with `.ListPosition()`/`.ListPositionAsync()` and `.ListPositions()`/`.ListPositionsAsync()` ([#2080 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2080)) +- Adds: Support for `ZMSCORE` with `.SortedSetScores()`/.`SortedSetScoresAsync()` ([#2082 by ttingen](https://github.com/StackExchange/StackExchange.Redis/pull/2082)) - Fix: For streams, properly hash `XACK`, `XCLAIM`, and `XPENDING` in cluster scenarios to eliminate `MOVED` retries ([#2085 by nielsderdaele](https://github.com/StackExchange/StackExchange.Redis/pull/2085)) - Adds: Support for `OBJECT REFCOUNT` with `.KeyRefCount()`/`.KeyRefCountAsync()` ([#2087 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2087)) - Adds: Support for `OBJECT ENCODIND` with `.KeyEncoding()`/`.KeyEncodingAsync()` ([#2088 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2088)) diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index f40d3ff49..e61281123 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -204,6 +204,7 @@ internal enum RedisCommand ZINTERCARD, ZINTERSTORE, ZLEXCOUNT, + ZMSCORE, ZPOPMAX, ZPOPMIN, ZRANDMEMBER, diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 627ab3d81..feb961ebd 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -1847,6 +1847,20 @@ IEnumerable SortedSetScan(RedisKey key, /// https://redis.io/commands/zscore double? SortedSetScore(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None); + /// + /// Returns the scores of members in the sorted set at . + /// If a member does not exist in the sorted set, or key does not exist, is returned. + /// + /// The key of the sorted set. + /// The members to get a score for. + /// The flags to use for this operation. + /// + /// The scores of the members in the same order as the array. + /// If a member does not exist in the set, is returned. + /// + /// https://redis.io/commands/zmscore + double?[] SortedSetScores(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None); + /// /// Removes and returns the first element from the sorted set stored at key, by default with the scores ordered from low to high. /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 00e1e5a8b..ccb34c83d 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -1799,6 +1799,20 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// https://redis.io/commands/zscore Task SortedSetScoreAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None); + /// + /// Returns the scores of members in the sorted set at . + /// If a member does not exist in the sorted set, or key does not exist, is returned. + /// + /// The key of the sorted set. + /// The members to get a score for. + /// The flags to use for this operation. + /// + /// The scores of the members in the same order as the array. + /// If a member does not exist in the set, is returned. + /// + /// https://redis.io/commands/zmscore + Task SortedSetScoresAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None); + /// /// Removes and returns the first element from the sorted set stored at key, by default with the scores ordered from low to high. /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index df54dd8fd..713464293 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -445,6 +445,9 @@ public long SortedSetRemoveRangeByValue(RedisKey key, RedisValue min, RedisValue public double? SortedSetScore(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) => Inner.SortedSetScore(ToInner(key), member, flags); + public double?[] SortedSetScores(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetScores(ToInner(key), members, flags); + public SortedSetEntry? SortedSetPop(RedisKey key, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) => Inner.SortedSetPop(ToInner(key), order, flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 0821c9a2b..fba0b347c 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -458,6 +458,9 @@ public Task SortedSetRemoveRangeByValueAsync(RedisKey key, RedisValue min, public Task SortedSetScoreAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) => Inner.SortedSetScoreAsync(ToInner(key), member, flags); + public Task SortedSetScoresAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) => + Inner.SortedSetScoresAsync(ToInner(key), members, flags); + public IAsyncEnumerable SortedSetScanAsync(RedisKey key, RedisValue pattern, int pageSize, long cursor, int pageOffset, CommandFlags flags) => Inner.SortedSetScanAsync(ToInner(key), pattern, pageSize, cursor, pageOffset, flags); diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index d7f806828..83c1ae8e3 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -626,6 +626,7 @@ StackExchange.Redis.IDatabase.SortedSetRemoveRangeByValue(StackExchange.Redis.Re StackExchange.Redis.IDatabase.SortedSetScan(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pattern = default(StackExchange.Redis.RedisValue), int pageSize = 250, long cursor = 0, int pageOffset = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Collections.Generic.IEnumerable! StackExchange.Redis.IDatabase.SortedSetScan(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pattern, int pageSize, StackExchange.Redis.CommandFlags flags) -> System.Collections.Generic.IEnumerable! StackExchange.Redis.IDatabase.SortedSetScore(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double? +StackExchange.Redis.IDatabase.SortedSetScores(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! members, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double?[]! StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAcknowledge(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StreamAdd(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue @@ -826,6 +827,7 @@ StackExchange.Redis.IDatabaseAsync.SortedSetRemoveRangeByScoreAsync(StackExchang StackExchange.Redis.IDatabaseAsync.SortedSetRemoveRangeByValueAsync(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.SortedSetScanAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue pattern = default(StackExchange.Redis.RedisValue), int pageSize = 250, long cursor = 0, int pageOffset = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Collections.Generic.IAsyncEnumerable! StackExchange.Redis.IDatabaseAsync.SortedSetScoreAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.SortedSetScoresAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! members, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue messageId, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAcknowledgeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue groupName, StackExchange.Redis.RedisValue[]! messageIds, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StreamAddAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.NameValueEntry[]! streamPairs, StackExchange.Redis.RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/RawResult.cs b/src/StackExchange.Redis/RawResult.cs index ef449c101..919054411 100644 --- a/src/StackExchange.Redis/RawResult.cs +++ b/src/StackExchange.Redis/RawResult.cs @@ -253,6 +253,9 @@ internal bool GetBoolean() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Sequence GetItems() => _items.Cast(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal double?[]? GetItemsAsDoubles() => this.ToArray((in RawResult x) => x.TryGetDouble(out double val) ? val : null); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal RedisKey[]? GetItemsAsKeys() => this.ToArray((in RawResult x) => x.AsRedisKey()); diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index d243e4a30..4b28d8c91 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1959,12 +1959,24 @@ private CursorEnumerable SortedSetScanAsync(RedisKey key, RedisV return ExecuteSync(msg, ResultProcessor.NullableDouble); } + public double?[] SortedSetScores(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZMSCORE, key, members); + return ExecuteSync(msg, ResultProcessor.NullableDoubleArray, defaultValue: Array.Empty()); + } + public Task SortedSetScoreAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.ZSCORE, key, member); return ExecuteAsync(msg, ResultProcessor.NullableDouble); } + public Task SortedSetScoresAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.ZMSCORE, key, members); + return ExecuteAsync(msg, ResultProcessor.NullableDoubleArray, defaultValue: Array.Empty()); + } + public SortedSetEntry? SortedSetPop(RedisKey key, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, order == Order.Descending ? RedisCommand.ZPOPMAX : RedisCommand.ZPOPMIN, key); diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index 9db002a0c..4f611354d 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -56,6 +56,10 @@ public static readonly ResultProcessor public static readonly ResultProcessor NullableDouble = new NullableDoubleProcessor(); + + public static readonly ResultProcessor + NullableDoubleArray = new NullableDoubleArrayProcessor(); + public static readonly ResultProcessor NullableInt64 = new NullableInt64Processor(); @@ -1112,6 +1116,20 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } + private sealed class NullableDoubleArrayProcessor : ResultProcessor + { + protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) + { + if (result.Type == ResultType.MultiBulk && !result.IsNull) + { + var arr = result.GetItemsAsDoubles()!; + SetResult(message, arr); + return true; + } + return false; + } + } + private sealed class NullableDoubleProcessor : ResultProcessor { protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) diff --git a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs index 64cf898b5..0c6e41b97 100644 --- a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs +++ b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs @@ -950,6 +950,13 @@ public void SortedSetScore() mock.Verify(_ => _.SortedSetScore("prefix:key", "member", CommandFlags.None)); } + [Fact] + public void SortedSetScore_Multiple() + { + wrapper.SortedSetScores("key", new RedisValue[] { "member1", "member2" }, CommandFlags.None); + mock.Verify(_ => _.SortedSetScores("prefix:key", new RedisValue[] { "member1", "member2" }, CommandFlags.None)); + } + [Fact] public void StreamAcknowledge_1() { diff --git a/tests/StackExchange.Redis.Tests/SortedSets.cs b/tests/StackExchange.Redis.Tests/SortedSets.cs index c20464240..a6b29e64f 100644 --- a/tests/StackExchange.Redis.Tests/SortedSets.cs +++ b/tests/StackExchange.Redis.Tests/SortedSets.cs @@ -1015,5 +1015,265 @@ public void SortedSetRangeStoreFailExclude() var exception = Assert.Throws(()=>db.SortedSetRangeAndStore(sourceKey, destinationKey,0,-1, exclude: Exclude.Both)); Assert.Equal("exclude", exception.ParamName); } + + [Fact] + public void SortedSetScoresSingle() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + var memberName = "member"; + + db.KeyDelete(key); + db.SortedSetAdd(key, memberName, 1.5); + + var score = db.SortedSetScore(key, memberName); + + Assert.NotNull(score); + Assert.Equal((double)1.5, score.Value); + } + + [Fact] + public async Task SortedSetScoresSingleAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + var memberName = "member"; + + await db.KeyDeleteAsync(key); + await db.SortedSetAddAsync(key, memberName, 1.5); + + var score = await db.SortedSetScoreAsync(key, memberName); + + Assert.NotNull(score); + Assert.Equal((double)1.5, score.Value); + } + + [Fact] + public void SortedSetScoresSingle_MissingSetStillReturnsNull() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + + db.KeyDelete(key); + + // Attempt to retrieve score for a missing set, should still return null. + var score = db.SortedSetScore(key, "bogusMemberName"); + + Assert.Null(score); + } + + [Fact] + public async Task SortedSetScoresSingle_MissingSetStillReturnsNullAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + + await db.KeyDeleteAsync(key); + + // Attempt to retrieve score for a missing set, should still return null. + var score = await db.SortedSetScoreAsync(key, "bogusMemberName"); + + Assert.Null(score); + } + + [Fact] + public void SortedSetScoresSingle_ReturnsNullForMissingMember() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + + db.KeyDelete(key); + db.SortedSetAdd(key, "member1", 1.5); + + // Attempt to retrieve score for a missing member, should return null. + var score = db.SortedSetScore(key, "bogusMemberName"); + + Assert.Null(score); + } + + [Fact] + public async Task SortedSetScoresSingle_ReturnsNullForMissingMemberAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v2_1_0); + + var db = conn.GetDatabase(); + var key = Me(); + + await db.KeyDeleteAsync(key); + await db.SortedSetAddAsync(key, "member1", 1.5); + + // Attempt to retrieve score for a missing member, should return null. + var score = await db.SortedSetScoreAsync(key, "bogusMemberName"); + + Assert.Null(score); + } + + [Fact] + public void SortedSetScoresMultiple() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var member1 = "member1"; + var member2 = "member2"; + var member3 = "member3"; + + db.KeyDelete(key); + db.SortedSetAdd(key, member1, 1.5); + db.SortedSetAdd(key, member2, 1.75); + db.SortedSetAdd(key, member3, 2); + + var scores = db.SortedSetScores(key, new RedisValue[] { member1, member2, member3 }); + + Assert.NotNull(scores); + Assert.Equal(3, scores.Length); + Assert.Equal((double)1.5, scores[0]); + Assert.Equal((double)1.75, scores[1]); + Assert.Equal(2, scores[2]); + } + + [Fact] + public async Task SortedSetScoresMultipleAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var member1 = "member1"; + var member2 = "member2"; + var member3 = "member3"; + + await db.KeyDeleteAsync(key); + await db.SortedSetAddAsync(key, member1, 1.5); + await db.SortedSetAddAsync(key, member2, 1.75); + await db.SortedSetAddAsync(key, member3, 2); + + var scores = await db.SortedSetScoresAsync(key, new RedisValue[] { member1, member2, member3 }); + + Assert.NotNull(scores); + Assert.Equal(3, scores.Length); + Assert.Equal((double)1.5, scores[0]); + Assert.Equal((double)1.75, scores[1]); + Assert.Equal(2, scores[2]); + } + + [Fact] + public void SortedSetScoresMultiple_ReturnsNullItemsForMissingSet() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + + db.KeyDelete(key); + + // Missing set but should still return an array of nulls. + var scores = db.SortedSetScores(key, new RedisValue[] { "bogus1", "bogus2", "bogus3" }); + + Assert.NotNull(scores); + Assert.Equal(3, scores.Length); + Assert.Null(scores[0]); + Assert.Null(scores[1]); + Assert.Null(scores[2]); + } + + [Fact] + public async Task SortedSetScoresMultiple_ReturnsNullItemsForMissingSetAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + + await db.KeyDeleteAsync(key); + + // Missing set but should still return an array of nulls. + var scores = await db.SortedSetScoresAsync(key, new RedisValue[] { "bogus1", "bogus2", "bogus3" }); + + Assert.NotNull(scores); + Assert.Equal(3, scores.Length); + Assert.Null(scores[0]); + Assert.Null(scores[1]); + Assert.Null(scores[2]); + } + + [Fact] + public void SortedSetScoresMultiple_ReturnsScoresAndNullItems() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var member1 = "member1"; + var member2 = "member2"; + var member3 = "member3"; + var bogusMember = "bogusMember"; + + db.KeyDelete(key); + + db.SortedSetAdd(key, member1, 1.5); + db.SortedSetAdd(key, member2, 1.75); + db.SortedSetAdd(key, member3, 2); + + var scores = db.SortedSetScores(key, new RedisValue[] { member1, bogusMember, member2, member3 }); + + Assert.NotNull(scores); + Assert.Equal(4, scores.Length); + Assert.Null(scores[1]); + Assert.Equal((double)1.5, scores[0]); + Assert.Equal((double)1.75, scores[2]); + Assert.Equal(2, scores[3]); + } + + [Fact] + public async Task SortedSetScoresMultiple_ReturnsScoresAndNullItemsAsync() + { + using var conn = Create(); + Skip.IfBelow(conn, RedisFeatures.v6_2_0); + + var db = conn.GetDatabase(); + var key = Me(); + var member1 = "member1"; + var member2 = "member2"; + var member3 = "member3"; + var bogusMember = "bogusMember"; + + await db.KeyDeleteAsync(key); + + await db.SortedSetAddAsync(key, member1, 1.5); + await db.SortedSetAddAsync(key, member2, 1.75); + await db.SortedSetAddAsync(key, member3, 2); + + var scores = await db.SortedSetScoresAsync(key, new RedisValue[] { member1, bogusMember, member2, member3 }); + + Assert.NotNull(scores); + Assert.Equal(4, scores.Length); + Assert.Null(scores[1]); + Assert.Equal((double)1.5, scores[0]); + Assert.Equal((double)1.75, scores[2]); + Assert.Equal(2, scores[3]); + } } } diff --git a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs index 471b6f184..b6204de3c 100644 --- a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs +++ b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs @@ -883,6 +883,13 @@ public void SortedSetScoreAsync() mock.Verify(_ => _.SortedSetScoreAsync("prefix:key", "member", CommandFlags.None)); } + [Fact] + public void SortedSetScoreAsync_Multiple() + { + wrapper.SortedSetScoresAsync("key", new RedisValue[] { "member1", "member2" }, CommandFlags.None); + mock.Verify(_ => _.SortedSetScoresAsync("prefix:key", new RedisValue[] { "member1", "member2" }, CommandFlags.None)); + } + [Fact] public void StreamAcknowledgeAsync_1() {