Skip to content

Commit 569d46f

Browse files
aaron-congocyip10
authored andcommitted
Python: add PFMERGE command (valkey-io#1497)
Python: add PFMERGE command (#322)
1 parent 4d8ad2f commit 569d46f

File tree

5 files changed

+91
-0
lines changed

5 files changed

+91
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Python: Added OBJECT REFCOUNT command ([#1485](https://github.com/aws/glide-for-redis/pull/1485))
77
* Python: Added RENAMENX command ([#1492](https://github.com/aws/glide-for-redis/pull/1492))
88
* Python: Added PFCOUNT command ([#1493](https://github.com/aws/glide-for-redis/pull/1493))
9+
* Python: Added PFMERGE command ([#1497](https://github.com/aws/glide-for-redis/pull/1497))
910

1011
## 0.4.0 (2024-05-26)
1112

python/python/glide/async_commands/core.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3574,6 +3574,38 @@ async def pfcount(self, keys: List[str]) -> int:
35743574
await self._execute_command(RequestType.PfCount, keys),
35753575
)
35763576

3577+
async def pfmerge(self, destination: str, source_keys: List[str]) -> TOK:
3578+
"""
3579+
Merges multiple HyperLogLog values into a unique value. If the destination variable exists, it is treated as one
3580+
of the source HyperLogLog data sets, otherwise a new HyperLogLog is created.
3581+
3582+
See https://valkey.io/commands/pfmerge for more details.
3583+
3584+
Note:
3585+
When in Cluster mode, all keys in `source_keys` and `destination` must map to the same hash slot.
3586+
3587+
Args:
3588+
destination (str): The key of the destination HyperLogLog where the merged data sets will be stored.
3589+
source_keys (List[str]): The keys of the HyperLogLog structures to be merged.
3590+
3591+
Returns:
3592+
OK: A simple OK response.
3593+
3594+
Examples:
3595+
>>> await client.pfadd("hll1", ["a", "b"])
3596+
>>> await client.pfadd("hll2", ["b", "c"])
3597+
>>> await client.pfmerge("new_hll", ["hll1", "hll2"])
3598+
OK # The value of "hll1" merged with "hll2" was stored in "new_hll".
3599+
>>> await client.pfcount(["new_hll"])
3600+
3 # The approximated cardinality of "new_hll" is 3.
3601+
"""
3602+
return cast(
3603+
TOK,
3604+
await self._execute_command(
3605+
RequestType.PfMerge, [destination] + source_keys
3606+
),
3607+
)
3608+
35773609
async def object_encoding(self, key: str) -> Optional[str]:
35783610
"""
35793611
Returns the internal encoding for the Redis object stored at `key`.

python/python/glide/async_commands/transaction.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,24 @@ def pfcount(self: TTransaction, keys: List[str]) -> TTransaction:
24912491
"""
24922492
return self.append_command(RequestType.PfCount, keys)
24932493

2494+
def pfmerge(
2495+
self: TTransaction, destination: str, source_keys: List[str]
2496+
) -> TTransaction:
2497+
"""
2498+
Merges multiple HyperLogLog values into a unique value. If the destination variable exists, it is treated as one
2499+
of the source HyperLogLog data sets, otherwise a new HyperLogLog is created.
2500+
2501+
See https://valkey.io/commands/pfmerge for more details.
2502+
2503+
Args:
2504+
destination (str): The key of the destination HyperLogLog where the merged data sets will be stored.
2505+
source_keys (List[str]): The keys of the HyperLogLog structures to be merged.
2506+
2507+
Command response:
2508+
OK: A simple OK response.
2509+
"""
2510+
return self.append_command(RequestType.PfMerge, [destination] + source_keys)
2511+
24942512
def object_encoding(self: TTransaction, key: str) -> TTransaction:
24952513
"""
24962514
Returns the internal encoding for the Redis object stored at `key`.

python/python/tests/test_async_client.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3372,6 +3372,43 @@ async def test_pfcount(self, redis_client: TRedisClient):
33723372
with pytest.raises(RequestError):
33733373
await redis_client.pfcount([string_key])
33743374

3375+
@pytest.mark.parametrize("cluster_mode", [True, False])
3376+
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
3377+
async def test_pfmerge(self, redis_client: TRedisClient):
3378+
key1 = f"{{testKey}}:1-{get_random_string(10)}"
3379+
key2 = f"{{testKey}}:2-{get_random_string(10)}"
3380+
key3 = f"{{testKey}}:3-{get_random_string(10)}"
3381+
string_key = f"{{testKey}}:4-{get_random_string(10)}"
3382+
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"
3383+
3384+
assert await redis_client.pfadd(key1, ["a", "b", "c"]) == 1
3385+
assert await redis_client.pfadd(key2, ["b", "c", "d"]) == 1
3386+
3387+
# merge into new HyperLogLog data set
3388+
assert await redis_client.pfmerge(key3, [key1, key2]) == OK
3389+
assert await redis_client.pfcount([key3]) == 4
3390+
3391+
# merge into existing HyperLogLog data set
3392+
assert await redis_client.pfmerge(key1, [key2]) == OK
3393+
assert await redis_client.pfcount([key1]) == 4
3394+
3395+
# non-existing source key
3396+
assert await redis_client.pfmerge(key2, [key1, non_existing_key]) == OK
3397+
assert await redis_client.pfcount([key2]) == 4
3398+
3399+
# empty source key list
3400+
assert await redis_client.pfmerge(key1, []) == OK
3401+
assert await redis_client.pfcount([key1]) == 4
3402+
3403+
# source key exists, but it is not a HyperLogLog
3404+
assert await redis_client.set(string_key, "foo")
3405+
with pytest.raises(RequestError):
3406+
assert await redis_client.pfmerge(key3, [string_key])
3407+
3408+
# destination key exists, but it is not a HyperLogLog
3409+
with pytest.raises(RequestError):
3410+
assert await redis_client.pfmerge(string_key, [key3])
3411+
33753412
@pytest.mark.parametrize("cluster_mode", [True, False])
33763413
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
33773414
async def test_object_encoding(self, redis_client: TRedisClient):
@@ -3516,6 +3553,7 @@ async def test_multi_key_command_returns_cross_slot_error(
35163553
redis_client.sdiffstore("abc", ["def", "ghi"]),
35173554
redis_client.renamenx("abc", "def"),
35183555
redis_client.pfcount(["def", "ghi"]),
3556+
redis_client.pfmerge("abc", ["def", "ghi"]),
35193557
]
35203558

35213559
if not await check_if_server_version_lt(redis_client, "7.0.0"):

python/python/tests/test_transaction.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ async def transaction_test(
304304

305305
transaction.pfadd(key10, ["a", "b", "c"])
306306
args.append(1)
307+
transaction.pfmerge(key10, [])
308+
args.append(OK)
307309
transaction.pfcount([key10])
308310
args.append(3)
309311

0 commit comments

Comments
 (0)