Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SMISMEMBER #2077

Merged
merged 15 commits into from
Apr 12, 2022
1 change: 1 addition & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ internal enum RedisCommand
SLAVEOF,
SLOWLOG,
SMEMBERS,
SMISMEMBER,
SMOVE,
SORT,
SPOP,
Expand Down
15 changes: 14 additions & 1 deletion src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// Returns if member is a member of the set stored at key.
/// </summary>
/// <param name="key">The key of the set.</param>
/// <param name="value">The value to check for .</param>
/// <param name="value">The value to check for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// <see langword="true"/> if the element is a member of the set.
Expand All @@ -1102,6 +1102,19 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>https://redis.io/commands/sismember</remarks>
bool SetContains(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns whether each member is a member of the set stored at key.
/// </summary>
/// <param name="key">The key of the set.</param>
/// <param name="values">The members to check for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// <see langword="true"/> if the element is a member of the set.
/// <see langword="false"/> if the element is not a member of the set, or if key does not exist.
/// </returns>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case doc should say an array of these true/false, matching position, e.g.

Suggested change
/// <returns>
/// <see langword="true"/> if the element is a member of the set.
/// <see langword="false"/> if the element is not a member of the set, or if key does not exist.
/// </returns>
/// <returns>
/// An array of booleans corresponding to <paramref name="values"/>, for each:
/// <see langword="true"/> if the element is a member of the set.
/// <see langword="false"/> if the element is not a member of the set, or if key does not exist.
/// </returns>

What happens when the key isn't present, all falses?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

/// <remarks>https://redis.io/commands/smismember</remarks>
bool[] SetContains(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the set cardinality (number of elements) of the set stored at key.
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,19 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>https://redis.io/commands/sismember</remarks>
Task<bool> SetContainsAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns whether each member is a member of the set stored at key.
/// </summary>
/// <param name="key">The key of the set.</param>
/// <param name="values">The members to check for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// <see langword="true"/> if the element is a member of the set.
/// <see langword="false"/> if the element is not a member of the set, or if key does not exist.
/// </returns>
/// <remarks>https://redis.io/commands/smismember</remarks>
Task<bool[]> SetContainsAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the set cardinality (number of elements) of the set stored at key.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ public RedisValue[] SetCombine(SetOperation operation, RedisKey first, RedisKey
public bool SetContains(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) =>
Inner.SetContains(ToInner(key), value, flags);

public bool[] SetContains(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) =>
Inner.SetContains(ToInner(key), values, flags);

public long SetLength(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.SetLength(ToInner(key), flags);

Expand Down
5 changes: 4 additions & 1 deletion src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@ public Task<RedisValue[]> SetCombineAsync(SetOperation operation, RedisKey first
public Task<bool> SetContainsAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None) =>
Inner.SetContainsAsync(ToInner(key), value, flags);

public Task<bool[]> SetContainsAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) =>
Inner.SetContainsAsync(ToInner(key), values, flags);

public Task<long> SetLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.SetLengthAsync(ToInner(key), flags);

Expand Down Expand Up @@ -532,7 +535,7 @@ public Task<RedisValue[]> StringGetAsync(RedisKey[] keys, CommandFlags flags = C

public Task<RedisValue> StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.StringGetAsync(ToInner(key), flags);

public Task<RedisValue> StringGetSetExpiryAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) =>
Inner.StringGetSetExpiryAsync(ToInner(key), expiry, flags);

Expand Down
2 changes: 2 additions & 0 deletions src/StackExchange.Redis/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ StackExchange.Redis.IDatabase.SetCombine(StackExchange.Redis.SetOperation operat
StackExchange.Redis.IDatabase.SetCombineAndStore(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.SetCombineAndStore(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.SetContains(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.SetContains(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! values, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool[]!
StackExchange.Redis.IDatabase.SetLength(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.SetMembers(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue[]!
StackExchange.Redis.IDatabase.SetMove(StackExchange.Redis.RedisKey source, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
Expand Down Expand Up @@ -758,6 +759,7 @@ StackExchange.Redis.IDatabaseAsync.SetCombineAndStoreAsync(StackExchange.Redis.S
StackExchange.Redis.IDatabaseAsync.SetCombineAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue[]!>!
StackExchange.Redis.IDatabaseAsync.SetCombineAsync(StackExchange.Redis.SetOperation operation, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue[]!>!
StackExchange.Redis.IDatabaseAsync.SetContainsAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.SetContainsAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue[]! values, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool[]!>!
StackExchange.Redis.IDatabaseAsync.SetLengthAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long>!
StackExchange.Redis.IDatabaseAsync.SetMembersAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue[]!>!
StackExchange.Redis.IDatabaseAsync.SetMoveAsync(StackExchange.Redis.RedisKey source, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/RawResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ internal bool GetBoolean()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal string?[]? GetItemsAsStrings() => this.ToArray<string?>((in RawResult x) => (string?)x.AsRedisValue());

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool[]? GetItemsAsBooleans() => this.ToArray<bool>((in RawResult x) => (bool)x.AsRedisValue());

internal GeoPosition? GetItemsAsGeoPosition()
{
var items = GetItems();
Expand Down
12 changes: 12 additions & 0 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,18 @@ public Task<bool> SetContainsAsync(RedisKey key, RedisValue value, CommandFlags
return ExecuteAsync(msg, ResultProcessor.Boolean);
}

public bool[] SetContains(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.SMISMEMBER, key, values);
return ExecuteSync(msg, ResultProcessor.BooleanArray, defaultValue: Array.Empty<bool>());
}

public Task<bool[]> SetContainsAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.SMISMEMBER, key, values);
return ExecuteAsync(msg, ResultProcessor.BooleanArray, defaultValue: Array.Empty<bool>());
}

public long SetLength(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.SCARD, key);
Expand Down
19 changes: 19 additions & 0 deletions src/StackExchange.Redis/ResultProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public static readonly ResultProcessor<RedisValue[]>
public static readonly ResultProcessor<string?[]>
StringArray = new StringArrayProcessor();

public static readonly ResultProcessor<bool[]>
BooleanArray = new BooleanArrayProcessor();

public static readonly ResultProcessor<GeoPosition?[]>
RedisGeoPositionArray = new RedisValueGeoPositionArrayProcessor();
public static readonly ResultProcessor<GeoPosition?>
Expand Down Expand Up @@ -1262,6 +1265,22 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
}
}

private sealed class BooleanArrayProcessor : ResultProcessor<Boolean[]>
Avital-Fine marked this conversation as resolved.
Show resolved Hide resolved
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result)
{
switch (result.Type)
{
case ResultType.MultiBulk:
var arr = result.GetItemsAsBooleans()!;
Avital-Fine marked this conversation as resolved.
Show resolved Hide resolved

SetResult(message, arr);
return true;
}
return false;
}
}

private sealed class RedisValueGeoPositionProcessor : ResultProcessor<GeoPosition?>
{
protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result)
Expand Down
8 changes: 8 additions & 0 deletions tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,14 @@ public void SetContains()
mock.Verify(_ => _.SetContains("prefix:key", "value", CommandFlags.None));
}

[Fact]
public void SetContains_2()
{
RedisValue[] values = new RedisValue[] { "value1", "value2" };
wrapper.SetContains("key", values, CommandFlags.None);
mock.Verify(_ => _.SetContains("prefix:key", values, CommandFlags.None));
}

[Fact]
public void SetLength()
{
Expand Down
25 changes: 25 additions & 0 deletions tests/StackExchange.Redis.Tests/Sets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ public void SScan()
}
}

[Fact]
public void SetContains()
{
using (var conn = Create())
Avital-Fine marked this conversation as resolved.
Show resolved Hide resolved
{
var server = GetAnyPrimary(conn);

RedisKey key = Me();
var db = conn.GetDatabase();
for (int i = 1; i < 1001; i++)
{
db.SetAdd(key, i, CommandFlags.FireAndForget);
}

// Single member
var isMemeber = db.SetContains(key, 1);
Assert.True(isMemeber);

// Multi members
var areMemebers = db.SetContains(key, new RedisValue[] {1, 2, 3});
Assert.Equal(3, areMemebers.Length);
Assert.True(areMemebers[1]);
Avital-Fine marked this conversation as resolved.
Show resolved Hide resolved
}
}

[Fact]
public async Task SetRemoveArgTests()
{
Expand Down
8 changes: 8 additions & 0 deletions tests/StackExchange.Redis.Tests/WrapperBaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,14 @@ public void SetContainsAsync()
mock.Verify(_ => _.SetContainsAsync("prefix:key", "value", CommandFlags.None));
}

[Fact]
public void SetContainsAsync_2()
{
RedisValue[] values = new RedisValue[] { "value1", "value2" };
wrapper.SetContainsAsync("key", values, CommandFlags.None);
mock.Verify(_ => _.SetContainsAsync("prefix:key", values, CommandFlags.None));
}

[Fact]
public void SetLengthAsync()
{
Expand Down