Skip to content

Commit

Permalink
Support JSON.MERGE Command (#3429)
Browse files Browse the repository at this point in the history
Co-authored-by: M Sazzadul Hoque <7600764+sazzad16@users.noreply.github.com>
  • Loading branch information
shacharPash and sazzad16 authored May 29, 2023
1 parent d0a89d4 commit a6ec2b9
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/main/java/redis/clients/jedis/CommandObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -3401,6 +3401,15 @@ public final CommandObject<String> jsonSet(String key, Path path, Object pojo, J
getJsonObjectMapper().toJson(pojo)).addParams(params), BuilderFactory.STRING);
}

public final CommandObject<String> jsonMerge(String key, Path2 path, Object object) {
return new CommandObject<>(commandArguments(JsonCommand.MERGE).key(key).add(path).add(object), BuilderFactory.STRING);
}

public final CommandObject<String> jsonMerge(String key, Path path, Object pojo) {
return new CommandObject<>(commandArguments(JsonCommand.MERGE).key(key).add(path).add(
getJsonObjectMapper().toJson(pojo)), BuilderFactory.STRING);
}

public final CommandObject<Object> jsonGet(String key) {
return new CommandObject<>(commandArguments(JsonCommand.GET).key(key), new JsonObjectBuilder<>(Object.class));
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/redis/clients/jedis/PipelineBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -3458,6 +3458,16 @@ public Response<String> jsonSet(String key, Path path, Object object, JsonSetPar
return appendCommand(commandObjects.jsonSet(key, path, object, params));
}

@Override
public Response<String> jsonMerge(String key, Path2 path, Object object) {
return appendCommand(commandObjects.jsonMerge(key, path, object));
}

@Override
public Response<String> jsonMerge(String key, Path path, Object object) {
return appendCommand(commandObjects.jsonMerge(key, path, object));
}

@Override
public Response<Object> jsonGet(String key) {
return appendCommand(commandObjects.jsonGet(key));
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/redis/clients/jedis/TransactionBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public abstract class TransactionBase implements PipelineCommands, PipelineBinar

/**
* Creates a new transaction.
*
*
* A MULTI command will be added to be sent to server. WATCH/UNWATCH/MULTI commands must not be
* called with this object.
* @param connection connection
Expand Down Expand Up @@ -3626,6 +3626,16 @@ public Response<String> jsonSet(String key, Path path, Object object, JsonSetPar
return appendCommand(commandObjects.jsonSet(key, path, object, params));
}

@Override
public Response<String> jsonMerge(String key, Path2 path, Object object) {
return appendCommand(commandObjects.jsonMerge(key, path, object));
}

@Override
public Response<String> jsonMerge(String key, Path path, Object object) {
return appendCommand(commandObjects.jsonMerge(key, path, object));
}

@Override
public Response<Object> jsonGet(String key) {
return appendCommand(commandObjects.jsonGet(key));
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/redis/clients/jedis/UnifiedJedis.java
Original file line number Diff line number Diff line change
Expand Up @@ -3902,6 +3902,16 @@ public String jsonSet(String key, Path path, Object pojo, JsonSetParams params)
return executeCommand(commandObjects.jsonSet(key, path, pojo, params));
}

@Override
public String jsonMerge(String key, Path2 path, Object object) {
return executeCommand(commandObjects.jsonMerge(key, path, object));
}

@Override
public String jsonMerge(String key, Path path, Object pojo) {
return executeCommand(commandObjects.jsonMerge(key, path, pojo));
}

@Override
public Object jsonGet(String key) {
return executeCommand(commandObjects.jsonGet(key));
Expand Down
1 change: 1 addition & 0 deletions src/main/java/redis/clients/jedis/json/JsonProtocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public enum JsonCommand implements ProtocolCommand {
DEL("JSON.DEL"),
GET("JSON.GET"),
MGET("JSON.MGET"),
MERGE("JSON.MERGE"),
SET("JSON.SET"),
TYPE("JSON.TYPE"),
STRAPPEND("JSON.STRAPPEND"),
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/redis/clients/jedis/json/RedisJsonCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ default String jsonSetLegacy(String key, Object pojo, JsonSetParams params) {

String jsonSet(String key, Path path, Object pojo, JsonSetParams params);

String jsonMerge(String key, Path2 path, Object object);

String jsonMerge(String key, Path path, Object pojo);

Object jsonGet(String key);

<T> T jsonGet(String key, Class<T> clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ default Response<String> jsonSetLegacy(String key, Object pojo, JsonSetParams pa

Response<String> jsonSet(String key, Path path, Object pojo, JsonSetParams params);

Response<String> jsonMerge(String key, Path2 path, Object object);

Response<String> jsonMerge(String key, Path path, Object pojo);

Response<Object> jsonGet(String key);

<T> Response<T> jsonGet(String key, Class<T> clazz);
Expand Down
43 changes: 42 additions & 1 deletion src/test/java/redis/clients/jedis/modules/json/JsonObjects.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package redis.clients.jedis.modules.json;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class JsonObjects {
Expand Down Expand Up @@ -102,15 +104,54 @@ public boolean equals(Object o) {
}

public static class Person {
public String name;
public int age;
public String address;
public String phone;
public List<String> childrens;

public Person(String name, int age, String address, String phone, List<String> childrens) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
this.childrens = childrens;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
// if (getClass() != o.getClass()) {
// return false;
// }
Person other = (Person) o;

return Objects.equals(name, other.name)
&& Objects.equals(age, other.age)
&& Objects.equals(address, other.address)
&& Objects.equals(phone, other.phone)
&& Objects.equals(childrens, other.childrens);
}
}

public static class Tick {
private final String id;
private final Instant created;
public Person(String id, Instant created) {

public Tick(String id, Instant created) {
this.id = id;
this.created = created;
}

public String getId() {
return id;
}

public Instant getCreated() {
return created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.google.gson.JsonObject;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -186,6 +187,25 @@ public void typeChecksShouldSucceed() {
assertNull(client.jsonType("foobar", Path.of(".fooErr")));
}

@Test
public void testJsonMerge() {
// create data
List<String> childrens = new ArrayList<>();
childrens.add("Child 1");
Person person = new Person("John Doe", 25, "123 Main Street", "123-456-7890", childrens);
assertEquals("OK", client.jsonSet("test_merge", ROOT_PATH, person));

// After 5 years:
person.age = 30;
person.childrens.add("Child 2");
person.childrens.add("Child 3");

// merge the new data
assertEquals("OK", client.jsonMerge("test_merge", Path.of((".childrens")), person.childrens));
assertEquals("OK", client.jsonMerge("test_merge", Path.of((".age")), person.age));
assertEquals(person, client.jsonGet("test_merge", Person.class));
}

@Test
public void mgetWithPathWithAllKeysExist() {
Baz baz1 = new Baz("quuz1", "grault1", "waldo1");
Expand Down Expand Up @@ -501,7 +521,7 @@ public void resp() {

@Test
public void testJsonGsonParser() {
Person person = new Person("foo", Instant.now());
Tick person = new Tick("foo", Instant.now());

// setting the custom json gson parser
client.setJsonObjectMapper(JsonObjectMapperTestUtil.getCustomGsonObjectMapper());
Expand All @@ -514,7 +534,7 @@ public void testJsonGsonParser() {

@Test
public void testDefaultJsonGsonParserStringsMustBeDifferent() {
Person person = new Person("foo", Instant.now());
Tick person = new Tick("foo", Instant.now());

// using the default json gson parser which is automatically configured

Expand All @@ -526,7 +546,7 @@ public void testDefaultJsonGsonParserStringsMustBeDifferent() {

@Test
public void testJsonJacksonParser() {
Person person = new Person("foo", Instant.now());
Tick person = new Tick("foo", Instant.now());

// setting the custom json jackson parser
client.setJsonObjectMapper(JsonObjectMapperTestUtil.getCustomJacksonObjectMapper());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,57 @@ public void typeChecksShouldSucceed() {
assertEquals(Collections.emptyList(), client.jsonType("foobar", Path2.of(".fooErr")));
}

@Test
public void testJsonMerge() {
// Test with root path
JSONObject json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":25,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}");
assertEquals("OK", client.jsonSet("test_merge", json));

json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}");
assertEquals("OK", client.jsonMerge("test_merge", Path2.of("$"), "{\"person\":{\"age\":30}}"));

assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge", Path2.of("$")));

// Test with root path path $.a.b
assertEquals("OK", client.jsonMerge("test_merge", Path2.of("$.person.address"), "{\"work\":\"Redis office\"}"));
json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}");
assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge", Path2.of("$")));

// Test with null value to delete a value
assertEquals("OK", client.jsonMerge("test_merge", Path2.of("$.person"), "{\"age\":null}"));
json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}");
assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge", Path2.of("$")));

// cleanup
assertEquals(1L, client.del("test_merge"));
}

@Test
public void testJsonMergeArray()
{
// Test merge on an array
JSONObject json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"d\",\"e\"]}}}");
assertEquals("OK", (client.jsonSet("test_merge_array", Path2.of("$"), json)));
assertEquals("OK", (client.jsonMerge("test_merge_array", Path2.of("$.a.b.c"), "[\"f\"]")));

json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"f\"]}}}");
assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge_array", Path2.of("$")));

// assertEquals("{{a={b={c=[f]}}}", client.jsonGet("test_merge_array", Path2.of("$")));

// Test merge an array on a value
assertEquals("OK", (client.jsonSet("test_merge_array", Path2.of("$"), "{\"a\":{\"b\":{\"c\":\"d\"}}}")));
assertEquals("OK", (client.jsonMerge("test_merge_array", Path2.of("$.a.b.c"), "[\"f\"]")));
json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"f\"]}}}");
assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge_array", Path2.of("$")));

// Test with null value to delete an array value
assertEquals("OK", (client.jsonSet("test_merge_array", Path2.of("$"), "{\"a\":{\"b\":{\"c\":[\"d\",\"e\"]}}}")));
assertEquals("OK", (client.jsonMerge("test_merge_array", Path2.of("$.a.b"), "{\"c\":null}")));
json = new JSONObject("{\"a\":{\"b\":{}}}");
assertJsonArrayEquals(jsonArray(json), client.jsonGet("test_merge_array", Path2.of("$")));
}

@Test
public void mgetWithPathWithAllKeysExist() {
Baz baz1 = new Baz("quuz1", "grault1", "waldo1");
Expand Down

0 comments on commit a6ec2b9

Please sign in to comment.