Skip to content

Commit

Permalink
Python: adds ZPOPMAX command (#996)
Browse files Browse the repository at this point in the history
  • Loading branch information
shohamazon authored Feb 21, 2024
1 parent 4ae2285 commit 89f9970
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Python: Added ZPOPMIN command ([#975](https:/aws/glide-for-redis/pull/975))
* Node: Added STRLEN command ([#993](https:/aws/glide-for-redis/pull/993))
* Node: Added LINDEX command ([#999](https:/aws/glide-for-redis/pull/999))
* Python: Added ZPOPMAX command ([#996](https:/aws/glide-for-redis/pull/996))

#### Features
* Python, Node: Added support in Lua Scripts ([#775](https:/aws/glide-for-redis/pull/775), [#860](https:/aws/glide-for-redis/pull/860))
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ enum RequestType {
ZPopMin = 71;
Strlen = 72;
Lindex = 73;
ZPopMax = 74;
}

message Command {
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/socket_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ fn get_command(request: &Command) -> Option<Cmd> {
RequestType::ZPopMin => Some(cmd("ZPOPMIN")),
RequestType::Strlen => Some(cmd("STRLEN")),
RequestType::Lindex => Some(cmd("LINDEX")),
RequestType::ZPopMax => Some(cmd("ZPOPMAX")),
}
}

Expand Down
36 changes: 34 additions & 2 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,38 @@ async def zcount(
),
)

async def zpopmax(
self, key: str, count: Optional[int] = None
) -> Mapping[str, float]:
"""
Removes and returns the members with the highest scores from the sorted set stored at `key`.
If `count` is provided, up to `count` members with the highest scores are removed and returned.
Otherwise, only one member with the highest score is removed and returned.
See https://redis.io/commands/zpopmax for more details.
Args:
key (str): The key of the sorted set.
count (Optional[int]): Specifies the quantity of members to pop. If not specified, pops one member.
If `count` is higher than the sorted set's cardinality, returns all members and their scores, ordered from highest to lowest.
Returns:
Mapping[str, float]: A map of the removed members and their scores, ordered from the one with the highest score to the one with the lowest.
If `key` doesn't exist, it will be treated as an empy sorted set and the command returns an empty map.
Examples:
>>> await client.zpopmax("my_sorted_set")
{'member1': 10.0} # Indicates that 'member1' with a score of 10.0 has been removed from the sorted set.
>>> await client.zpopmax("my_sorted_set", 2)
{'member2': 8.0, 'member3': 7.5} # Indicates that 'member2' with a score of 8.0 and 'member3' with a score of 7.5 have been removed from the sorted set.
"""
return cast(
Mapping[str, float],
await self._execute_command(
RequestType.ZPopMax, [key, str(count)] if count else [key]
),
)

async def zpopmin(
self, key: str, count: Optional[int] = None
) -> Mapping[str, float]:
Expand All @@ -1363,9 +1395,9 @@ async def zpopmin(
Examples:
>>> await client.zpopmin("my_sorted_set")
{'member1': 10.0} # Indicates that 'member1' with a score of 10.0 has been removed from the sorted set.
{'member1': 5.0} # Indicates that 'member1' with a score of 5.0 has been removed from the sorted set.
>>> await client.zpopmin("my_sorted_set", 2)
{'member2': 8.0, 'member3': 7.5} # Indicates that 'member2' with a score of 8.0 and 'member3' with a score of 7.5 have been removed from the sorted set.
{'member3': 7.5 , 'member2': 8.0} # Indicates that 'member3' with a score of 7.5 and 'member2' with a score of 8.0 have been removed from the sorted set.
"""
return cast(
Mapping[str, float],
Expand Down
23 changes: 23 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,29 @@ def zcount(
RequestType.Zcount, [key, min_score.value, max_score.value]
)

def zpopmax(
self: TTransaction, key: str, count: Optional[int] = None
) -> TTransaction:
"""
Removes and returns the members with the highest scores from the sorted set stored at `key`.
If `count` is provided, up to `count` members with the highest scores are removed and returned.
Otherwise, only one member with the highest score is removed and returned.
See https://redis.io/commands/zpopmax for more details.
Args:
key (str): The key of the sorted set.
count (Optional[int]): Specifies the quantity of members to pop. If not specified, pops one member.
If `count` is higher than the sorted set's cardinality, returns all members and their scores, ordered from highest to lowest.
Commands response:
Mapping[str, float]: A map of the removed members and their scores, ordered from the one with the highest score to the one with the lowest.
If `key` doesn't exist, it will be treated as an empy sorted set and the command returns an empty map.
"""
return self.append_command(
RequestType.ZPopMax, [key, str(count)] if count else [key]
)

def zpopmin(
self: TTransaction, key: str, count: Optional[int] = None
) -> TTransaction:
Expand Down
15 changes: 15 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,21 @@ async def test_zpopmin(self, redis_client: TRedisClient):

assert await redis_client.zpopmin("non_exisitng_key") == {}

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_zpopmax(self, redis_client: TRedisClient):
key = get_random_string(10)
members_scores = {"a": 1.0, "b": 2.0, "c": 3.0}
assert await redis_client.zadd(key, members_scores) == 3
assert await redis_client.zpopmax(key) == {"c": 3.0}
assert await redis_client.zpopmax(key, 3) == {"b": 2.0, "a": 1.0}
assert await redis_client.zpopmax(key) == {}
assert await redis_client.set(key, "value") == OK
with pytest.raises(RequestError):
await redis_client.zpopmax(key)

assert await redis_client.zpopmax("non_exisitng_key") == {}

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_type(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 @@ -149,6 +149,8 @@ def transaction_test(
args.append(2.0)
transaction.zpopmin(key8)
args.append({"two": 2.0})
transaction.zpopmax(key8)
args.append({"three": 3})
return args


Expand Down

0 comments on commit 89f9970

Please sign in to comment.