From ca8c895d9e6f56cabb33fedec43cd769041a7bb8 Mon Sep 17 00:00:00 2001 From: Shoham Elias Date: Mon, 15 Apr 2024 10:21:02 +0000 Subject: [PATCH] Python: adds GEOHASH command --- CHANGELOG.md | 1 + glide-core/src/protobuf/redis_request.proto | 1 + glide-core/src/request_type.rs | 3 ++ python/python/glide/async_commands/core.py | 25 +++++++++++++++ .../glide/async_commands/transaction.py | 16 ++++++++++ python/python/tests/test_async_client.py | 31 +++++++++++++++++++ python/python/tests/test_transaction.py | 2 ++ 7 files changed, 79 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ca64be91..71ed943a5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Node: Added ZRANGE command ([#1115](https://github.com/aws/glide-for-redis/pull/1115)) * Python: Added RENAME command ([#1252](https://github.com/aws/glide-for-redis/pull/1252)) * 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)) #### Fixes * Python: Fix typing error "‘type’ object is not subscriptable" ([#1203](https://github.com/aws/glide-for-redis/pull/1203)) diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 79a11ef2fe..dacf2b6f61 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -151,6 +151,7 @@ enum RequestType { ZRemRangeByLex = 108; ZLexCount = 109; GeoAdd = 110; + GeoHash = 112; } message Command { diff --git a/glide-core/src/request_type.rs b/glide-core/src/request_type.rs index 07efbbdda9..1a5aea6d09 100644 --- a/glide-core/src/request_type.rs +++ b/glide-core/src/request_type.rs @@ -119,6 +119,7 @@ pub enum RequestType { ZRemRangeByLex = 108, ZLexCount = 109, GeoAdd = 110, + GeoHash = 112, } fn get_two_word_command(first: &str, second: &str) -> Cmd { @@ -241,6 +242,7 @@ impl From<::protobuf::EnumOrUnknown> for RequestType { ProtobufRequestType::ZRemRangeByLex => RequestType::ZRemRangeByLex, ProtobufRequestType::ZLexCount => RequestType::ZLexCount, ProtobufRequestType::GeoAdd => RequestType::GeoAdd, + ProtobufRequestType::GeoHash => RequestType::GeoHash, } } } @@ -359,6 +361,7 @@ impl RequestType { RequestType::ZRemRangeByLex => Some(cmd("ZREMRANGEBYLEX")), RequestType::ZLexCount => Some(cmd("ZLEXCOUNT")), RequestType::GeoAdd => Some(cmd("GEOADD")), + RequestType::GeoHash => Some(cmd("GEOHASH")), } } } diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py index eb77431396..820197c4c6 100644 --- a/python/python/glide/async_commands/core.py +++ b/python/python/glide/async_commands/core.py @@ -1566,6 +1566,31 @@ async def geoadd( await self._execute_command(RequestType.GeoAdd, args), ) + async def geohash(self, key: str, members: List[str]) -> List[Optional[str]]: + """ + Returns the GeoHash strings representing the positions of all the specified members in the sorted set stored at + `key`. If a member does not exist in the sorted set, a None value is returned for that member. + + See https://redis.io/commands/geohash for more details. + + Args: + key (str): The key of the sorted set. + members (List[str]): The list of members whose GeoHash strings are to be retrieved. + + Returns: + List[Optional[str]]: A list of GeoHash strings representing the positions of the specified members stored at `key`. + + Examples: + >>> await client.geoadd("my_geo_sorted_set", {"Palermo": Coordinate(13.361389, 38.115556), "Catania": Coordinate(15.087269, 37.502669)}) + 2 # Indicates that two elements have been added to the sorted set "my_geo_sorted_set". + >>> await client.geohash("my_geo_sorted_set", ["Palermo", "Catania", "some city]) + ["sqc8b49rny0", "sqdtr74hyu0", None] # Indicates the GeoHash strings for the specified members. + """ + return cast( + List[Optional[str]], + await self._execute_command(RequestType.GeoHash, [key] + members), + ) + async def zadd( self, key: str, diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 08892f4c64..4396e0a72c 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -1191,6 +1191,22 @@ def geoadd( return self.append_command(RequestType.GeoAdd, args) + def geohash(self: TTransaction, key: str, members: List[str]) -> TTransaction: + """ + Returns the GeoHash strings representing the positions of all the specified members in the sorted set stored at + `key`. If a member does not exist in the sorted set, a None value is returned for that member. + + See https://redis.io/commands/geohash for more details. + + Args: + key (str): The key of the sorted set. + members (List[str]): The list of members whose GeoHash strings are to be retrieved. + + Commands response: + List[Optional[str]]: A list of GeoHash strings representing the positions of the specified members stored at `key`. + """ + return self.append_command(RequestType.GeoHash, [key] + members) + def zadd( self: TTransaction, key: str, diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index eb71255772..4604f9a06e 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -1272,6 +1272,37 @@ async def test_geoadd_invalid_coordinates(self, redis_client: TRedisClient): with pytest.raises(RequestError): await redis_client.geoadd(key, {"Place": Coordinate(0, -86)}) + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_geohash(self, redis_client: TRedisClient): + key = get_random_string(10) + members_coordinates = { + "Palermo": Coordinate(13.361389, 38.115556), + "Catania": Coordinate(15.087269, 37.502669), + } + assert await redis_client.geoadd(key, members_coordinates) == 2 + assert await redis_client.geohash(key, ["Palermo", "Catania", "Place"]) == [ + "sqc8b49rny0", + "sqdtr74hyu0", + None, + ] + + assert ( + await redis_client.geohash( + "non_existing_key", ["Palermo", "Catania", "Place"] + ) + == [None] * 3 + ) + + # Neccessary to check since we are enforcing the user to pass a list of members while redis don't + # But when running the command with key only (and no members) the returned value will always be an empty list + # So in case of any changes, this test will fail and inform us that we should allow not passing any members. + assert await redis_client.geohash(key, []) == [] + + assert await redis_client.set(key, "value") == OK + with pytest.raises(RequestError): + await redis_client.geohash(key, ["Palermo", "Catania"]) + @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) async def test_zadd_zaddincr(self, redis_client: TRedisClient): diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index 12c3197e61..714da214c9 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -206,6 +206,8 @@ async def transaction_test( }, ) args.append(2) + transaction.geohash(key9, ["Palermo", "Catania", "Place"]) + args.append(["sqc8b49rny0", "sqdtr74hyu0", None]) return args