Skip to content

Commit 7d351f7

Browse files
yipin-chenmo-amzn
authored andcommitted
Java: add JSON.ARRAPPEND command (valkey-io#2489)
* Java: add JSON.ARRAPPEND command --------- Signed-off-by: Yi-Pin Chen <yi-pin.chen@improving.com>
1 parent dd5ccd7 commit 7d351f7

File tree

3 files changed

+137
-0
lines changed

3 files changed

+137
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* Java: Added `FT.ALIASADD`, `FT.ALIASDEL`, `FT.ALIASUPDATE` ([#2442](https://github.com/valkey-io/valkey-glide/pull/2442))
1919
* Core: Update routing for commands from server modules ([#2461](https://github.com/valkey-io/valkey-glide/pull/2461))
2020
* Node: Added `JSON.SET` and `JSON.GET` ([#2427](https://github.com/valkey-io/valkey-glide/pull/2427))
21+
* Java: Added `JSON.ARRAPPEND` ([#2489](https://github.com/valkey-io/valkey-glide/pull/2489))
2122

2223
#### Breaking Changes
2324

java/client/src/main/java/glide/api/commands/servermodules/Json.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class Json {
2222
private static final String JSON_PREFIX = "JSON.";
2323
public static final String JSON_SET = JSON_PREFIX + "SET";
2424
public static final String JSON_GET = JSON_PREFIX + "GET";
25+
private static final String JSON_ARRAPPEND = JSON_PREFIX + "ARRAPPEND";
2526
private static final String JSON_ARRINSERT = JSON_PREFIX + "ARRINSERT";
2627
private static final String JSON_ARRLEN = JSON_PREFIX + "ARRLEN";
2728

@@ -390,6 +391,86 @@ public static CompletableFuture<GlideString> get(
390391
new ArgsBuilder().add(gs(JSON_GET)).add(key).add(options.toArgs()).add(paths).toArray());
391392
}
392393

394+
/**
395+
* Appends one or more <code>values</code> to the JSON array at the specified <code>path</code>
396+
* within the JSON document stored at <code>key</code>.
397+
*
398+
* @param client The client to execute the command.
399+
* @param key The <code>key</code> of the JSON document.
400+
* @param path Represents the <code>path</code> within the JSON document where the <code>values
401+
* </code> will be appended.
402+
* @param values The <code>values</code> to append to the JSON array at the specified <code>path
403+
* </code>.
404+
* @return
405+
* <ul>
406+
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
407+
* Returns a list of integers for every possible path, indicating the new length of the
408+
* new array after appending <code>values</code>, or <code>null</code> for JSON values
409+
* matching the path that are not an array. If <code>path</code> does not exist, an
410+
* empty array will be returned.
411+
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
412+
* Returns the length of the new array after appending <code>values</code> to the array
413+
* at <code>path</code>. If multiple paths are matched, returns the last updated array.
414+
* If the JSON value at <code>path</code> is not a array or if <code>path</code> doesn't
415+
* exist, an error is raised. If <code>key</code> doesn't exist, an error is raised.
416+
* @example
417+
* <pre>{@code
418+
* Json.set(client, "doc", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
419+
* var res = Json.arrappend(client, "doc", "$.b", new String[] {"\"three\""}).get();
420+
* assert Arrays.equals((Object[]) res, new int[] {3}); // New length of the array after appending
421+
* res = Json.arrappend(client, "doc", ".b", new String[] {"\"four\""}).get();
422+
* assert res.equals(4); // New length of the array after appending
423+
* }</pre>
424+
*/
425+
public static CompletableFuture<Object> arrappend(
426+
@NonNull BaseClient client,
427+
@NonNull String key,
428+
@NonNull String path,
429+
@NonNull String[] values) {
430+
return executeCommand(
431+
client, concatenateArrays(new String[] {JSON_ARRAPPEND, key, path}, values));
432+
}
433+
434+
/**
435+
* Appends one or more <code>values</code> to the JSON array at the specified <code>path</code>
436+
* within the JSON document stored at <code>key</code>.
437+
*
438+
* @param client The client to execute the command.
439+
* @param key The <code>key</code> of the JSON document.
440+
* @param path Represents the <code>path</code> within the JSON document where the <code>values
441+
* </code> will be appended.
442+
* @param values The <code>values</code> to append to the JSON array at the specified <code>path
443+
* </code>.
444+
* @return
445+
* <ul>
446+
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
447+
* Returns a list of integers for every possible path, indicating the new length of the
448+
* new array after appending <code>values</code>, or <code>null</code> for JSON values
449+
* matching the path that are not an array. If <code>path</code> does not exist, an
450+
* empty array will be returned.
451+
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
452+
* Returns the length of the new array after appending <code>values</code> to the array
453+
* at <code>path</code>. If multiple paths are matched, returns the last updated array.
454+
* If the JSON value at <code>path</code> is not a array or if <code>path</code> doesn't
455+
* exist, an error is raised. If <code>key</code> doesn't exist, an error is raised.
456+
* @example
457+
* <pre>{@code
458+
* Json.set(client, "doc", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
459+
* var res = Json.arrappend(client, gs("doc"), gs("$.b"), new GlideString[] {gs("\"three\"")}).get();
460+
* assert Arrays.equals((Object[]) res, new int[] {3}); // New length of the array after appending
461+
* res = Json.arrappend(client, gs("doc"), gs(".b"), new GlideString[] {gs("\"four\"")}).get();
462+
* assert res.equals(4); // New length of the array after appending
463+
* }</pre>
464+
*/
465+
public static CompletableFuture<Object> arrappend(
466+
@NonNull BaseClient client,
467+
@NonNull GlideString key,
468+
@NonNull GlideString path,
469+
@NonNull GlideString[] values) {
470+
return executeCommand(
471+
client, new ArgsBuilder().add(gs(JSON_ARRAPPEND)).add(key).add(path).add(values).toArray());
472+
}
473+
393474
/**
394475
* Inserts one or more values into the array at the specified <code>path</code> within the JSON
395476
* document stored at <code>key</code>, before the given <code>index</code>.

java/integTest/src/test/java/glide/modules/JsonTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
1010
import static org.junit.jupiter.api.Assertions.assertEquals;
1111
import static org.junit.jupiter.api.Assertions.assertNull;
12+
import static org.junit.jupiter.api.Assertions.assertThrows;
1213
import static org.junit.jupiter.api.Assertions.assertTrue;
1314

1415
import com.google.gson.JsonParser;
@@ -20,6 +21,7 @@
2021
import glide.api.models.commands.InfoOptions.Section;
2122
import glide.api.models.commands.json.JsonGetOptions;
2223
import java.util.UUID;
24+
import java.util.concurrent.ExecutionException;
2325
import lombok.SneakyThrows;
2426
import org.junit.jupiter.api.AfterAll;
2527
import org.junit.jupiter.api.BeforeAll;
@@ -157,6 +159,59 @@ public void json_set_get_formatting() {
157159
assertEquals(expectedGetResult2, actualGetResult2);
158160
}
159161

162+
@Test
163+
@SneakyThrows
164+
public void arrappend() {
165+
String key = UUID.randomUUID().toString();
166+
String doc = "{\"a\": 1, \"b\": [\"one\", \"two\"]}";
167+
168+
assertEquals(OK, Json.set(client, key, "$", doc).get());
169+
170+
assertArrayEquals(
171+
new Object[] {3L},
172+
(Object[]) Json.arrappend(client, key, "$.b", new String[] {"\"three\""}).get());
173+
assertEquals(
174+
5L, Json.arrappend(client, key, ".b", new String[] {"\"four\"", "\"five\""}).get());
175+
176+
String getResult = Json.get(client, key, new String[] {"$"}).get();
177+
String expectedGetResult =
178+
"[{\"a\": 1, \"b\": [\"one\", \"two\", \"three\", \"four\", \"five\"]}]";
179+
assertEquals(JsonParser.parseString(expectedGetResult), JsonParser.parseString(getResult));
180+
181+
assertArrayEquals(
182+
new Object[] {null},
183+
(Object[]) Json.arrappend(client, key, "$.a", new String[] {"\"value\""}).get());
184+
185+
// JSONPath, path doesn't exist
186+
assertArrayEquals(
187+
new Object[] {},
188+
(Object[])
189+
Json.arrappend(client, gs(key), gs("$.c"), new GlideString[] {gs("\"value\"")}).get());
190+
191+
// Legacy path, path doesn't exist
192+
var exception =
193+
assertThrows(
194+
ExecutionException.class,
195+
() -> Json.arrappend(client, key, ".c", new String[] {"\"value\""}).get());
196+
197+
// Legacy path, the JSON value at path is not a array
198+
exception =
199+
assertThrows(
200+
ExecutionException.class,
201+
() -> Json.arrappend(client, key, ".a", new String[] {"\"value\""}).get());
202+
203+
exception =
204+
assertThrows(
205+
ExecutionException.class,
206+
() ->
207+
Json.arrappend(client, "non_existing_key", "$.b", new String[] {"\"six\""}).get());
208+
209+
exception =
210+
assertThrows(
211+
ExecutionException.class,
212+
() -> Json.arrappend(client, "non_existing_key", ".b", new String[] {"\"six\""}).get());
213+
}
214+
160215
@Test
161216
@SneakyThrows
162217
public void arrinsert() {

0 commit comments

Comments
 (0)