summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorshacharPash <93581407+shacharPash@users.noreply.github.com>2023-05-16 13:52:09 +0300
committerGitHub <noreply@github.com>2023-05-16 13:52:09 +0300
commit2d9b5ac6fe03fdc572b8ca47f7134082bae2a5e2 (patch)
treea0780069a09c8ce427d9e3676a6f33d07b3d95aa
parent35b7e09a57a1b7e2931d90e4b13858b68cee97cf (diff)
downloadredis-py-master.tar.gz
support JSON.MERGE Command (#2761)HEADmaster
* support JSON.MERGE Command * linters * try with abc instead person * change @skip_ifmodversion_lt to latest ReJSON 2.4.7 * change version * fix test * linters * add async test
-rw-r--r--redis/commands/json/__init__.py1
-rw-r--r--redis/commands/json/commands.py22
-rw-r--r--tests/test_asyncio/test_json.py35
-rw-r--r--tests/test_json.py33
4 files changed, 91 insertions, 0 deletions
diff --git a/redis/commands/json/__init__.py b/redis/commands/json/__init__.py
index 7d55023..77fb21c 100644
--- a/redis/commands/json/__init__.py
+++ b/redis/commands/json/__init__.py
@@ -38,6 +38,7 @@ class JSON(JSONCommands):
"JSON.GET": self._decode,
"JSON.MGET": bulk_of_jsons(self._decode),
"JSON.SET": lambda r: r and nativestr(r) == "OK",
+ "JSON.MERGE": lambda r: r and nativestr(r) == "OK",
"JSON.NUMINCRBY": self._decode,
"JSON.NUMMULTBY": self._decode,
"JSON.TOGGLE": self._decode,
diff --git a/redis/commands/json/commands.py b/redis/commands/json/commands.py
index c02c47a..5da9245 100644
--- a/redis/commands/json/commands.py
+++ b/redis/commands/json/commands.py
@@ -253,6 +253,28 @@ class JSONCommands:
pieces.append("XX")
return self.execute_command("JSON.SET", *pieces)
+ def merge(
+ self,
+ name: str,
+ path: str,
+ obj: JsonType,
+ decode_keys: Optional[bool] = False,
+ ) -> Optional[str]:
+ """
+ Sets or updates the JSON value at a path..
+
+ ``decode_keys`` If set to True, the keys of ``obj`` will be decoded
+ with utf-8.
+
+ For more information see `JSON.MERGE <https://redis.io/commands/json.merge>`_.
+ """
+ if decode_keys:
+ obj = decode_dict_keys(obj)
+
+ pieces = [name, str(path), self._encode(obj)]
+
+ return self.execute_command("JSON.MERGE", *pieces)
+
def set_file(
self,
name: str,
diff --git a/tests/test_asyncio/test_json.py b/tests/test_asyncio/test_json.py
index fc530c6..7334399 100644
--- a/tests/test_asyncio/test_json.py
+++ b/tests/test_asyncio/test_json.py
@@ -40,6 +40,41 @@ async def test_json_get_jset(modclient: redis.Redis):
@pytest.mark.redismod
+@skip_ifmodversion_lt("2.6.0", "ReJSON") # todo: update after the release
+async def test_json_merge(modclient: redis.Redis):
+ # Test with root path $
+ assert await modclient.json().set(
+ "person_data",
+ "$",
+ {"person1": {"personal_data": {"name": "John"}}},
+ )
+ assert await modclient.json().merge(
+ "person_data", "$", {"person1": {"personal_data": {"hobbies": "reading"}}}
+ )
+ assert await modclient.json().get("person_data") == {
+ "person1": {"personal_data": {"name": "John", "hobbies": "reading"}}
+ }
+
+ # Test with root path path $.person1.personal_data
+ assert await modclient.json().merge(
+ "person_data", "$.person1.personal_data", {"country": "Israel"}
+ )
+ assert await modclient.json().get("person_data") == {
+ "person1": {
+ "personal_data": {"name": "John", "hobbies": "reading", "country": "Israel"}
+ }
+ }
+
+ # Test with null value to delete a value
+ assert await modclient.json().merge(
+ "person_data", "$.person1.personal_data", {"name": None}
+ )
+ assert await modclient.json().get("person_data") == {
+ "person1": {"personal_data": {"country": "Israel", "hobbies": "reading"}}
+ }
+
+
+@pytest.mark.redismod
async def test_nonascii_setgetdelete(modclient: redis.Redis):
assert await modclient.json().set("notascii", Path.root_path(), "hyvää-élève")
assert "hyvää-élève" == await modclient.json().get("notascii", no_escape=True)
diff --git a/tests/test_json.py b/tests/test_json.py
index 8e8da05..0a85998 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -48,6 +48,39 @@ def test_json_get_jset(client):
@pytest.mark.redismod
+@skip_ifmodversion_lt("2.6.0", "ReJSON") # todo: update after the release
+def test_json_merge(client):
+ # Test with root path $
+ assert client.json().set(
+ "person_data",
+ "$",
+ {"person1": {"personal_data": {"name": "John"}}},
+ )
+ assert client.json().merge(
+ "person_data", "$", {"person1": {"personal_data": {"hobbies": "reading"}}}
+ )
+ assert client.json().get("person_data") == {
+ "person1": {"personal_data": {"name": "John", "hobbies": "reading"}}
+ }
+
+ # Test with root path path $.person1.personal_data
+ assert client.json().merge(
+ "person_data", "$.person1.personal_data", {"country": "Israel"}
+ )
+ assert client.json().get("person_data") == {
+ "person1": {
+ "personal_data": {"name": "John", "hobbies": "reading", "country": "Israel"}
+ }
+ }
+
+ # Test with null value to delete a value
+ assert client.json().merge("person_data", "$.person1.personal_data", {"name": None})
+ assert client.json().get("person_data") == {
+ "person1": {"personal_data": {"country": "Israel", "hobbies": "reading"}}
+ }
+
+
+@pytest.mark.redismod
def test_nonascii_setgetdelete(client):
assert client.json().set("notascii", Path.root_path(), "hyvää-élève")
assert "hyvää-élève" == client.json().get("notascii", no_escape=True)