Skip to content

Commit

Permalink
Python: add SDIFF command (#1437)
Browse files Browse the repository at this point in the history
Python: add SDIFF command (#295)
  • Loading branch information
aaron-congo authored May 23, 2024
1 parent 4277b97 commit be4dcd4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* Python: Added SMOVE command ([#1421](https://github.com/aws/glide-for-redis/pull/1421))
* Python: Added SUNIONSTORE command ([#1423](https://github.com/aws/glide-for-redis/pull/1423))
* Python: Added SINTER command ([#1434](https://github.com/aws/glide-for-redis/pull/1434))
* Python: Added SDIFF command ([#1437](https://github.com/aws/glide-for-redis/pull/1437))


#### Fixes
Expand Down
2 changes: 1 addition & 1 deletion glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
b"HEXISTS" | b"HSETNX" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT"
| b"SISMEMBER" | b"PERSIST" | b"SMOVE" | b"RENAMENX" => Some(ExpectedReturnType::Boolean),
b"SMISMEMBER" => Some(ExpectedReturnType::ArrayOfBools),
b"SMEMBERS" | b"SINTER" => Some(ExpectedReturnType::Set),
b"SMEMBERS" | b"SINTER" | b"SDIFF" => Some(ExpectedReturnType::Set),
b"ZSCORE" | b"GEODIST" => Some(ExpectedReturnType::DoubleOrNull),
b"ZMSCORE" => Some(ExpectedReturnType::ArrayOfDoubleOrNull),
b"ZPOPMIN" | b"ZPOPMAX" => Some(ExpectedReturnType::MapOfStringToDouble),
Expand Down
27 changes: 27 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,33 @@ async def sinter(self, keys: List[str]) -> Set[str]:
"""
return cast(Set[str], await self._execute_command(RequestType.SInter, keys))

async def sdiff(self, keys: List[str]) -> Set[str]:
"""
Computes the difference between the first set and all the successive sets in `keys`.
See https://valkey.io/commands/sdiff for more details.
Note:
When in cluster mode, all `keys` must map to the same hash slot.
Args:
keys (List[str]): The keys of the sets to diff.
Returns:
Set[str]: A set of elements representing the difference between the sets.
If any of the keys in `keys` do not exist, they are treated as empty sets.
Examples:
>>> await client.sadd("set1", ["member1", "member2"])
>>> await client.sadd("set2", ["member1"])
>>> await client.sdiff("set1", "set2")
{"member2"} # "member2" is in "set1" but not "set2"
"""
return cast(
Set[str],
await self._execute_command(RequestType.SDiff, keys),
)

async def ltrim(self, key: str, start: int, end: int) -> TOK:
"""
Trim an existing list so that it will contain only the specified range of elements specified.
Expand Down
15 changes: 15 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,21 @@ def sinter(self: TTransaction, keys: List[str]) -> TTransaction:
"""
return self.append_command(RequestType.SInter, keys)

def sdiff(self: TTransaction, keys: List[str]) -> TTransaction:
"""
Computes the difference between the first set and all the successive sets in `keys`.
See https://valkey.io/commands/sdiff for more details.
Args:
keys (List[str]): The keys of the sets to diff.
Command response:
Set[str]: A set of elements representing the difference between the sets.
If any of the keys in `keys` do not exist, they are treated as empty sets.
"""
return self.append_command(RequestType.SDiff, keys)

def ltrim(self: TTransaction, key: str, start: int, end: int) -> TTransaction:
"""
Trim an existing list so that it will contain only the specified range of elements specified.
Expand Down
28 changes: 27 additions & 1 deletion python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,6 @@ async def test_smove(self, redis_client: TRedisClient):
key1 = f"{{testKey}}:1-{get_random_string(10)}"
key2 = f"{{testKey}}:2-{get_random_string(10)}"
key3 = f"{{testKey}}:3-{get_random_string(10)}"
key4 = f"{{testKey}}:4-{get_random_string(10)}"
string_key = f"{{testKey}}:4-{get_random_string(10)}"
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"

Expand Down Expand Up @@ -1217,6 +1216,32 @@ async def test_sinter(self, redis_client: TRedisClient):
await redis_client.sinter([key2])
assert "Operation against a key holding the wrong kind of value" in str(e)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_sdiff(self, redis_client: TRedisClient):
key1 = f"{{testKey}}:1-{get_random_string(10)}"
key2 = f"{{testKey}}:2-{get_random_string(10)}"
string_key = f"{{testKey}}:4-{get_random_string(10)}"
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"

assert await redis_client.sadd(key1, ["a", "b", "c"]) == 3
assert await redis_client.sadd(key2, ["c", "d", "e"]) == 3

assert await redis_client.sdiff([key1, key2]) == {"a", "b"}
assert await redis_client.sdiff([key2, key1]) == {"d", "e"}

assert await redis_client.sdiff([key1, non_existing_key]) == {"a", "b", "c"}
assert await redis_client.sdiff([non_existing_key, key1]) == set()

# invalid argument - key list must not be empty
with pytest.raises(RequestError):
await redis_client.sdiff([])

# key exists, but it is not a set
assert await redis_client.set(string_key, "value") == OK
with pytest.raises(RequestError):
await redis_client.sdiff([string_key])

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_ltrim(self, redis_client: TRedisClient):
Expand Down Expand Up @@ -3157,6 +3182,7 @@ async def test_multi_key_command_returns_cross_slot_error(
redis_client.smove("abc", "def", "_"),
redis_client.sunionstore("abc", ["zxy", "lkn"]),
redis_client.sinter(["abc", "zxy", "lkn"]),
redis_client.sdiff(["abc", "zxy", "lkn"]),
]

if not check_if_server_version_lt(redis_client, "7.0.0"):
Expand Down
2 changes: 2 additions & 0 deletions python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ async def transaction_test(
args.append(2)
transaction.sinter([key7, key7])
args.append({"foo", "bar"})
transaction.sdiff([key7, key7])
args.append(set())
transaction.spop_count(key7, 4)
args.append({"foo", "bar"})
transaction.smove(key7, key7, "non_existing_member")
Expand Down

0 comments on commit be4dcd4

Please sign in to comment.