Skip to content

Commit

Permalink
Python: add ZREMRANGEBYLEX command (#1306)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-congo authored Apr 19, 2024
1 parent 1c50792 commit 369aff5
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Python: Added GEOADD command ([#1259](https://github.com/aws/glide-for-redis/pull/1259))
* Python: Added GEOHASH command ([#1281](https://github.com/aws/glide-for-redis/pull/1281))
* Python: Added ZLEXCOUNT command ([#1305](https://github.com/aws/glide-for-redis/pull/1305))
* Python: Added ZREMRANGEBYLEX command ([#1306](https://github.com/aws/glide-for-redis/pull/1306))

#### Fixes
* Python: Fix typing error "‘type’ object is not subscriptable" ([#1203](https://github.com/aws/glide-for-redis/pull/1203))
Expand Down
52 changes: 49 additions & 3 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,52 @@ async def zremrangebyscore(
),
)

async def zremrangebylex(
self,
key: str,
min_lex: Union[InfBound, LexBoundary],
max_lex: Union[InfBound, LexBoundary],
) -> int:
"""
Removes all elements in the sorted set stored at `key` with a lexicographical order between `min_lex` and
`max_lex`.
See https://redis.io/commands/zremrangebylex/ for more details.
Args:
key (str): The key of the sorted set.
min_lex (Union[InfBound, LexBoundary]): The minimum bound of the lexicographical range.
Can be an instance of `InfBound` representing positive/negative infinity, or `LexBoundary`
representing a specific lex and inclusivity.
max_lex (Union[InfBound, LexBoundary]): The maximum bound of the lexicographical range.
Can be an instance of `InfBound` representing positive/negative infinity, or `LexBoundary`
representing a specific lex and inclusivity.
Returns:
int: The number of members that were removed from the sorted set.
If `key` does not exist, it is treated as an empty sorted set, and the command returns `0`.
If `min_lex` is greater than `max_lex`, `0` is returned.
Examples:
>>> await client.zremrangebylex("my_sorted_set", LexBoundary("a", is_inclusive=False), LexBoundary("e"))
4 # Indicates that 4 members, with lexicographical values ranging from "a" (exclusive) to "e" (inclusive), have been removed from "my_sorted_set".
>>> await client.zremrangebylex("non_existing_sorted_set", InfBound.NEG_INF, LexBoundary("e"))
0 # Indicates that no members were removed as the sorted set "non_existing_sorted_set" does not exist.
"""
min_lex_arg = (
min_lex.value["lex_arg"] if type(min_lex) == InfBound else min_lex.value
)
max_lex_arg = (
max_lex.value["lex_arg"] if type(max_lex) == InfBound else max_lex.value
)

return cast(
int,
await self._execute_command(
RequestType.ZRemRangeByLex, [key, min_lex_arg, max_lex_arg]
),
)

async def zlexcount(
self,
key: str,
Expand Down Expand Up @@ -2111,17 +2157,17 @@ async def zlexcount(
>>> await client.zlexcount("my_sorted_set", LexBoundary("c" , is_inclusive=True), LexBoundary("k" , is_inclusive=False))
1 # Indicates that there is one member with LexBoundary "c" <= lexicographical value < "k" in the sorted set "my_sorted_set".
"""
min_lex_str = (
min_lex_arg = (
min_lex.value["lex_arg"] if type(min_lex) == InfBound else min_lex.value
)
max_lex_str = (
max_lex_arg = (
max_lex.value["lex_arg"] if type(max_lex) == InfBound else max_lex.value
)

return cast(
int,
await self._execute_command(
RequestType.ZLexCount, [key, min_lex_str, max_lex_str]
RequestType.ZLexCount, [key, min_lex_arg, max_lex_arg]
),
)

Expand Down
43 changes: 40 additions & 3 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,43 @@ def zremrangebyscore(
RequestType.ZRemRangeByScore, [key, score_min, score_max]
)

def zremrangebylex(
self: TTransaction,
key: str,
min_lex: Union[InfBound, LexBoundary],
max_lex: Union[InfBound, LexBoundary],
) -> TTransaction:
"""
Removes all elements in the sorted set stored at `key` with a lexicographical order between `min_lex` and
`max_lex`.
See https://redis.io/commands/zremrangebylex/ for more details.
Args:
key (str): The key of the sorted set.
min_lex (Union[InfBound, LexBoundary]): The minimum bound of the lexicographical range.
Can be an instance of `InfBound` representing positive/negative infinity, or `LexBoundary`
representing a specific lex and inclusivity.
max_lex (Union[InfBound, LexBoundary]): The maximum bound of the lexicographical range.
Can be an instance of `InfBound` representing positive/negative infinity, or `LexBoundary`
representing a specific lex and inclusivity.
Command response:
int: The number of members that were removed from the sorted set.
If `key` does not exist, it is treated as an empty sorted set, and the command returns `0`.
If `min_lex` is greater than `max_lex`, `0` is returned.
"""
min_lex_arg = (
min_lex.value["lex_arg"] if type(min_lex) == InfBound else min_lex.value
)
max_lex_arg = (
max_lex.value["lex_arg"] if type(max_lex) == InfBound else max_lex.value
)

return self.append_command(
RequestType.ZRemRangeByLex, [key, min_lex_arg, max_lex_arg]
)

def zlexcount(
self: TTransaction,
key: str,
Expand All @@ -1610,15 +1647,15 @@ def zlexcount(
If `key` does not exist, it is treated as an empty sorted set, and the command returns `0`.
If `max_lex < min_lex`, `0` is returned.
"""
min_lex_str = (
min_lex_arg = (
min_lex.value["lex_arg"] if type(min_lex) == InfBound else min_lex.value
)
max_lex_str = (
max_lex_arg = (
max_lex.value["lex_arg"] if type(max_lex) == InfBound else max_lex.value
)

return self.append_command(
RequestType.ZLexCount, [key, min_lex_str, max_lex_str]
RequestType.ZLexCount, [key, min_lex_arg, max_lex_arg]
)

def zscore(self: TTransaction, key: str, member: str) -> TTransaction:
Expand Down
44 changes: 44 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,50 @@ async def test_zremrangebyscore(self, redis_client: TRedisClient):
with pytest.raises(RequestError):
await redis_client.zremrangebyscore(key, InfBound.NEG_INF, InfBound.POS_INF)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_zremrangebylex(self, redis_client: TRedisClient):
key1 = get_random_string(10)
key2 = get_random_string(10)
range = RangeByIndex(0, -1)
members_scores = {"a": 1, "b": 2, "c": 3, "d": 4}
assert await redis_client.zadd(key1, members_scores) == 4

assert (
await redis_client.zremrangebylex(
key1, LexBoundary("a", False), LexBoundary("c")
)
== 2
)
assert await redis_client.zrange_withscores(key1, range) == {"a": 1.0, "d": 4.0}

assert (
await redis_client.zremrangebylex(key1, LexBoundary("d"), InfBound.POS_INF)
== 1
)
assert await redis_client.zrange_withscores(key1, range) == {"a": 1.0}

# min_lex > max_lex
assert (
await redis_client.zremrangebylex(key1, LexBoundary("a"), InfBound.NEG_INF)
== 0
)
assert await redis_client.zrange_withscores(key1, range) == {"a": 1.0}

assert (
await redis_client.zremrangebylex(
"non_existing_key", InfBound.NEG_INF, InfBound.POS_INF
)
== 0
)

# key exists, but it is not a sorted set
assert await redis_client.set(key2, "value") == OK
with pytest.raises(RequestError):
await redis_client.zremrangebylex(
key2, LexBoundary("a", False), LexBoundary("c")
)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_zlexcount(self, redis_client: TRedisClient):
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 @@ -206,6 +206,8 @@ async def transaction_test(
args.append({"four": 4})
transaction.zremrangebyscore(key8, InfBound.NEG_INF, InfBound.POS_INF)
args.append(1)
transaction.zremrangebylex(key8, InfBound.NEG_INF, InfBound.POS_INF)
args.append(0)

transaction.geoadd(
key9,
Expand Down

0 comments on commit 369aff5

Please sign in to comment.