Skip to content

Commit

Permalink
Python: adds JSON.ARRLEN command (#2403)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Shoham Elias <[email protected]>
Signed-off-by: Shoham Elias <[email protected]>
  • Loading branch information
shohamazon authored Oct 10, 2024
1 parent 1ce22c6 commit fcc6001
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#### Changes
* Python: Add JSON.ARRLEN command ([#2403](https://github.com/valkey-io/valkey-glide/pull/2403))

#### Breaking Changes

Expand Down
54 changes: 54 additions & 0 deletions python/python/glide/async_commands/server_modules/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,60 @@ async def get(
return cast(bytes, await client.custom_command(args))


async def arrlen(
client: TGlideClient,
key: TEncodable,
path: Optional[TEncodable] = None,
) -> Optional[TJsonResponse[int]]:
"""
Retrieves the length of the array at the specified `path` within the JSON document stored at `key`.
Args:
client (TGlideClient): The client to execute the command.
key (TEncodable): The key of the JSON document.
path (Optional[TEncodable]): The path within the JSON document. Defaults to None.
Returns:
Optional[TJsonResponse[int]]:
For JSONPath (`path` starts with `$`):
Returns a list of integer replies for every possible path, indicating the length of the array,
or None for JSON values matching the path that are not an array.
If `path` doesn't exist, an empty array will be returned.
For legacy path (`path` doesn't starts with `$`):
Returns the length of the array at `path`.
If multiple paths match, the length of the first array match is returned.
If the JSON value at `path` is not a array or if `path` doesn't exist, an error is raised.
If `key` doesn't exist, None is returned.
Examples:
>>> from glide import json
>>> await json.set(client, "doc", "$", '{"a": [1, 2, 3], "b": {"a": [1, 2], "c": {"a": 42}}}')
b'OK' # JSON is successfully set for doc
>>> await json.arrlen(client, "doc", "$")
[None] # No array at the root path.
>>> await json.arrlen(client, "doc", "$.a")
[3] # Retrieves the length of the array at path $.a.
>>> await json.arrlen(client, "doc", "$..a")
[3, 2, None] # Retrieves lengths of arrays found at all levels of the path `..a`.
>>> await json.arrlen(client, "doc", "..a")
3 # Legacy path retrieves the first array match at path `..a`.
>>> await json.arrlen(client, "non_existing_key", "$.a")
None # Returns None because the key does not exist.
>>> await json.set(client, "doc", "$", '[1, 2, 3, 4]')
b'OK' # JSON is successfully set for doc
>>> await json.arrlen(client, "doc")
4 # Retrieves lengths of arrays in root.
"""
args = ["JSON.ARRLEN", key]
if path:
args.append(path)
return cast(
Optional[TJsonResponse[int]],
await client.custom_command(args),
)


async def delete(
client: TGlideClient,
key: TEncodable,
Expand Down
36 changes: 36 additions & 0 deletions python/python/tests/tests_server_modules/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,39 @@ async def test_json_type(self, glide_client: TGlideClient):
# Check for all types in the JSON document using legacy path
result = await json.type(glide_client, key, "[*]")
assert result == b"string" # Expecting only the first type (string for key1)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_json_arrlen(self, glide_client: TGlideClient):
key = get_random_string(5)

json_value = '{"a": [1, 2, 3], "b": {"a": [1, 2], "c": {"a": 42}}}'
assert await json.set(glide_client, key, "$", json_value) == OK

assert await json.arrlen(glide_client, key, "$.a") == [3]

assert await json.arrlen(glide_client, key, "$..a") == [3, 2, None]

# Legacy path retrieves the first array match at ..a
assert await json.arrlen(glide_client, key, "..a") == 3

# Value at path is not an array
assert await json.arrlen(glide_client, key, "$") == [None]
with pytest.raises(RequestError):
assert await json.arrlen(glide_client, key, ".")

# Path doesn't exist
assert await json.arrlen(glide_client, key, "$.non_existing_path") == []
with pytest.raises(RequestError):
assert await json.arrlen(glide_client, key, "non_existing_path")

# Non-existing key
assert await json.arrlen(glide_client, "non_existing_key", "$.a") is None
assert await json.arrlen(glide_client, "non_existing_key", ".a") is None

# No path
with pytest.raises(RequestError):
assert await json.arrlen(glide_client, key)

assert await json.set(glide_client, key, "$", "[1, 2, 3, 4]") == OK
assert await json.arrlen(glide_client, key) == 4

0 comments on commit fcc6001

Please sign in to comment.