Skip to content

Commit

Permalink
Python: add PFCOUNT command (valkey-io#1493)
Browse files Browse the repository at this point in the history
Python: add PFCOUNT command (#321)
  • Loading branch information
aaron-congo authored May 29, 2024
1 parent 7c4c9b7 commit b2be117
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Node: Added RENAMENX command ([#1483](https://github.com/aws/glide-for-redis/pull/1483))
* Python: Added OBJECT REFCOUNT command ([#1485](https://github.com/aws/glide-for-redis/pull/1485))
* Python: Added RENAMENX command ([#1492](https://github.com/aws/glide-for-redis/pull/1492))
* Python: Added PFCOUNT command ([#1493](https://github.com/aws/glide-for-redis/pull/1493))

## 0.4.0 (2024-05-26)

Expand Down
26 changes: 26 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3548,6 +3548,32 @@ async def pfadd(self, key: str, elements: List[str]) -> int:
await self._execute_command(RequestType.PfAdd, [key] + elements),
)

async def pfcount(self, keys: List[str]) -> int:
"""
Estimates the cardinality of the data stored in a HyperLogLog structure for a single key or
calculates the combined cardinality of multiple keys by merging their HyperLogLogs temporarily.
See https://valkey.io/commands/pfcount 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 HyperLogLog data structures to be analyzed.
Returns:
int: The approximated cardinality of given HyperLogLog data structures.
The cardinality of a key that does not exist is 0.
Examples:
>>> await client.pfcount(["hll_1", "hll_2"])
4 # The approximated cardinality of the union of "hll_1" and "hll_2" is 4.
"""
return cast(
int,
await self._execute_command(RequestType.PfCount, keys),
)

async def object_encoding(self, key: str) -> Optional[str]:
"""
Returns the internal encoding for the Redis object stored at `key`.
Expand Down
16 changes: 16 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2475,6 +2475,22 @@ def pfadd(self: TTransaction, key: str, elements: List[str]) -> TTransaction:
"""
return self.append_command(RequestType.PfAdd, [key] + elements)

def pfcount(self: TTransaction, keys: List[str]) -> TTransaction:
"""
Estimates the cardinality of the data stored in a HyperLogLog structure for a single key or
calculates the combined cardinality of multiple keys by merging their HyperLogLogs temporarily.
See https://valkey.io/commands/pfcount for more details.
Args:
keys (List[str]): The keys of the HyperLogLog data structures to be analyzed.
Command response:
int: The approximated cardinality of given HyperLogLog data structures.
The cardinality of a key that does not exist is 0.
"""
return self.append_command(RequestType.PfCount, keys)

def object_encoding(self: TTransaction, key: str) -> TTransaction:
"""
Returns the internal encoding for the Redis object stored at `key`.
Expand Down
29 changes: 29 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3344,6 +3344,34 @@ async def test_pfadd(self, redis_client: TRedisClient):
with pytest.raises(RequestError):
await redis_client.pfadd("foo", [])

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_pfcount(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)}"
string_key = f"{{testKey}}:4-{get_random_string(10)}"
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"

assert await redis_client.pfadd(key1, ["a", "b", "c"]) == 1
assert await redis_client.pfadd(key2, ["b", "c", "d"]) == 1
assert await redis_client.pfcount([key1]) == 3
assert await redis_client.pfcount([key2]) == 3
assert await redis_client.pfcount([key1, key2]) == 4
assert await redis_client.pfcount([key1, key2, non_existing_key]) == 4
# empty HyperLogLog data set
assert await redis_client.pfadd(key3, []) == 1
assert await redis_client.pfcount([key3]) == 0

# incorrect argument - key list cannot be empty
with pytest.raises(RequestError):
await redis_client.pfcount([])

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

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_object_encoding(self, redis_client: TRedisClient):
Expand Down Expand Up @@ -3487,6 +3515,7 @@ async def test_multi_key_command_returns_cross_slot_error(
redis_client.sdiff(["abc", "zxy", "lkn"]),
redis_client.sdiffstore("abc", ["def", "ghi"]),
redis_client.renamenx("abc", "def"),
redis_client.pfcount(["def", "ghi"]),
]

if not await 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 @@ -304,6 +304,8 @@ async def transaction_test(

transaction.pfadd(key10, ["a", "b", "c"])
args.append(1)
transaction.pfcount([key10])
args.append(3)

transaction.geoadd(
key12,
Expand Down

0 comments on commit b2be117

Please sign in to comment.