Skip to content

Commit

Permalink
Python: Adds JSON.NUMMULTBY Command (valkey-io#2458)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Muhammad Awawdi <Mawawdi@amazon.com>
Co-authored-by: Shoham Elias <116083498+shohamazon@users.noreply.github.com>
  • Loading branch information
2 people authored and avifenesh committed Oct 21, 2024
1 parent 174600d commit 6bcad22
Show file tree
Hide file tree
Showing 3 changed files with 181 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 @@
* Python: Add JSON.CLEAR command ([#2418](https://github.com/valkey-io/valkey-glide/pull/2418))
* Python: Add JSON.TYPE command ([#2409](https://github.com/valkey-io/valkey-glide/pull/2409))
* Python: Add JSON.NUMINCRBY command ([#2448](https://github.com/valkey-io/valkey-glide/pull/2448))
* Python: Add JSON.NUMMULTBY command ([#2458](https://github.com/valkey-io/valkey-glide/pull/2458))
* Java: Added `FT.CREATE` ([#2414](https://github.com/valkey-io/valkey-glide/pull/2414))
* Java: Added `FT.DROPINDEX` ([#2440](https://github.com/valkey-io/valkey-glide/pull/2440))
* Core: Update routing for commands from server modules ([#2461](https://github.com/valkey-io/valkey-glide/pull/2461))
Expand Down
42 changes: 42 additions & 0 deletions python/python/glide/async_commands/server_modules/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,48 @@ async def numincrby(
return cast(Optional[bytes], await client.custom_command(args))


async def nummultby(
client: TGlideClient,
key: TEncodable,
path: TEncodable,
number: Union[int, float],
) -> Optional[bytes]:
"""
Multiplies the JSON value(s) at the specified `path` by `number` 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 (TEncodable): The path within the JSON document.
number (Union[int, float]): The number to multiply by.
Returns:
Optional[bytes]:
For JSONPath (`path` starts with `$`):
Returns a bytes string representation of an array of bulk strings, indicating the new values after multiplication for each matched `path`.
If a value is not a number, its corresponding return value will be `null`.
If `path` doesn't exist, a byte string representation of an empty array will be returned.
For legacy path (`path` doesn't start with `$`):
Returns a bytes string representation of the resulting value after multiplication.
If multiple paths match, the result of the last updated value is returned.
If the value at the `path` is not a number or `path` doesn't exist, an error is raised.
If `key` does not exist, an error is raised.
If the result is out of the range of 64-bit IEEE double, an error is raised.
Examples:
>>> from glide import json
>>> await json.set(client, "doc", "$", '{"a": [], "b": [1], "c": [1, 2], "d": [1, 2, 3]}')
'OK'
>>> await json.nummultby(client, "doc", "$.d[*]", 2)
b'[2,4,6]' # Multiplies each element in the `d` array by 2.
>>> await json.nummultby(client, "doc", ".c[1]", 2)
b'4' # Multiplies the second element in the `c` array by 2.
"""
args = ["JSON.NUMMULTBY", key, path, str(number)]

return cast(Optional[bytes], await client.custom_command(args))


async def toggle(
client: TGlideClient,
key: TEncodable,
Expand Down
138 changes: 138 additions & 0 deletions python/python/tests/tests_server_modules/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,141 @@ async def test_json_numincrby(self, glide_client: TGlideClient):
# Check for Overflow in legacy
with pytest.raises(RequestError):
await json.numincrby(glide_client, key, ".key9", 1.7976931348623157e308)

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

json_value = {
"key1": 1,
"key2": 3.5,
"key3": {"nested_key": {"key1": [4, 5]}},
"key4": [1, 2, 3],
"key5": 0,
"key6": "hello",
"key7": None,
"key8": {"nested_key": {"key1": 69}},
"key9": 3.5953862697246314e307,
}

# Set the initial JSON document at the key
assert await json.set(glide_client, key, "$", OuterJson.dumps(json_value)) == OK

# Test JSONPath
# Multiply integer value (key1) by 5
result = await json.nummultby(glide_client, key, "$.key1", 5)
assert result == b"[5]" # Expect 1 * 5 = 5

# Multiply float value (key2) by 2.5
result = await json.nummultby(glide_client, key, "$.key2", 2.5)
assert result == b"[8.75]" # Expect 3.5 * 2.5 = 8.75

# Multiply nested object (key3.nested_key.key1[1]) by 7
result = await json.nummultby(glide_client, key, "$.key3.nested_key.key1[1]", 7)
assert result == b"[35]" # Expect 5 * 7 = 35

# Multiply array element (key4[1]) by 1
result = await json.nummultby(glide_client, key, "$.key4[1]", 1)
assert result == b"[2]" # Expect 2 * 1 = 2

# Multiply zero value (key5) by 10.23 (float number)
result = await json.nummultby(glide_client, key, "$.key5", 10.23)
assert result == b"[0]" # Expect 0 * 10.23 = 0

# Multiply a string value (key6) by a number
result = await json.nummultby(glide_client, key, "$.key6", 99)
assert result == b"[null]" # Expect null

# Multiply a None value (key7) by a number
result = await json.nummultby(glide_client, key, "$.key7", 51)
assert result == b"[null]" # Expect null

# Check multiplication for all numbers in the document using JSON Path
# key1: 5 * 5 = 25
# key2: 8.75 * 5 = 43.75
# key3.nested_key.key1[0]: 4 * 5 = 20
# key3.nested_key.key1[1]: 35 * 5 = 175
# key4[0]: 1 * 5 = 5
# key4[1]: 2 * 5 = 10
# key4[2]: 3 * 5 = 15
# key5: 0 * 5 = 0
# key8.nested_key.key1: 69 * 5 = 345
# key9: 3.5953862697246314e307 * 5 = 1.7976931348623157e308
result = await json.nummultby(glide_client, key, "$..*", 5)
assert (
result
== b"[25,43.75,null,null,0,null,null,null,1.7976931348623157e+308,null,null,20,175,5,10,15,null,345]"
)

# Check for multiple path matches in JSONPath
# key1: 25 * 2 = 50
# key8.nested_key.key1: 345 * 2 = 690
result = await json.nummultby(glide_client, key, "$..key1", 2)
assert result == b"[50,null,690]" # After previous multiplications

# Check for non-existent path in JSONPath
result = await json.nummultby(glide_client, key, "$.key10", 51)
assert result == b"[]" # Expect Empty Array

# Check for non-existent key in JSONPath
with pytest.raises(RequestError):
await json.nummultby(glide_client, "non_existent_key", "$.key10", 51)

# Check for Overflow in JSONPath
with pytest.raises(RequestError):
await json.nummultby(glide_client, key, "$.key9", 1.7976931348623157e308)

# Multiply integer value (key1) by -12
result = await json.nummultby(glide_client, key, "$.key1", -12)
assert result == b"[-600]" # Expect 50 * -12 = -600

# Multiply integer value (key1) by -0.5
result = await json.nummultby(glide_client, key, "$.key1", -0.5)
assert result == b"[300]" # Expect -600 * -0.5 = 300

# Test Legacy Path
# Multiply int value (key1) by 5 (integer)
result = await json.nummultby(glide_client, key, "key1", 5)
assert result == b"1500" # Expect 300 * 5 = -1500

# Multiply int value (key1) by -5.5 (float number)
result = await json.nummultby(glide_client, key, "key1", -5.5)
assert result == b"-8250" # Expect -150 * -5.5 = -8250

# Multiply int float (key2) by 2.5 (a float number)
result = await json.nummultby(glide_client, key, "key2", 2.5)
assert result == b"109.375" # Expect 43.75 * 2.5 = 109.375

# Multiply nested value (key3.nested_key.key1[0]) by 7
result = await json.nummultby(glide_client, key, "key3.nested_key.key1[0]", 7)
assert result == b"140" # Expect 20 * 7 = 140

# Multiply array element (key4[1]) by 1
result = await json.nummultby(glide_client, key, "key4[1]", 1)
assert result == b"10" # Expect 10 * 1 = 10

# Multiply a float value (key5) by 10.2 (a float number)
result = await json.nummultby(glide_client, key, "key5", 10.2)
assert result == b"0" # Expect 0 * 10.2 = 0

# Check for multiple path matches in legacy and assure that the result of the last updated value is returned
# last updated value is key8.nested_key.key1: 690 * 2 = 1380
result = await json.nummultby(glide_client, key, "..key1", 2)
assert result == b"1380" # Expect the last updated key1 value multiplied by 2

# Check if the rest of the key1 path matches were updated and not only the last value
result = await json.get(glide_client, key, "$..key1")
assert result == b"[-16500,[140,175],1380]"

# Check for non-existent path in legacy
with pytest.raises(RequestError):
await json.nummultby(glide_client, key, ".key10", 51)

# Check for non-existent key in legacy
with pytest.raises(RequestError):
await json.nummultby(glide_client, "non_existent_key", ".key10", 51)

# Check for Overflow in legacy
with pytest.raises(RequestError):
await json.nummultby(glide_client, key, ".key9", 1.7976931348623157e308)

0 comments on commit 6bcad22

Please sign in to comment.