Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Java: Added `FT.ALIASADD`, `FT.ALIASDEL`, `FT.ALIASUPDATE` ([#2442](https://github.com/valkey-io/valkey-glide/pull/2442))
* Core: Update routing for commands from server modules ([#2461](https://github.com/valkey-io/valkey-glide/pull/2461))
* Node: Added `JSON.SET` and `JSON.GET` ([#2427](https://github.com/valkey-io/valkey-glide/pull/2427))
* Java: Added `JSON.ARRAPPEND` ([#2489](https://github.com/valkey-io/valkey-glide/pull/2489))

#### Breaking Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Json {
private static final String JSON_PREFIX = "JSON.";
public static final String JSON_SET = JSON_PREFIX + "SET";
public static final String JSON_GET = JSON_PREFIX + "GET";
private static final String JSON_ARRAPPEND = JSON_PREFIX + "ARRAPPEND";
private static final String JSON_ARRINSERT = JSON_PREFIX + "ARRINSERT";
private static final String JSON_ARRLEN = JSON_PREFIX + "ARRLEN";

Expand Down Expand Up @@ -391,8 +392,88 @@ public static CompletableFuture<GlideString> get(
}

/**
* Inserts one or more values into the array at the specified <code>path</code> within the JSON
* document stored at <code>key</code>, before the given <code>index</code>.
* Appends one or more <code>values</code> to the JSON array at the specified <code>path</code>
* within the JSON document stored at <code>key</code>.
*
* @param client The client to execute the command.
* @param key The <code>key</code> of the JSON document.
* @param path Represents the <code>path</code> within the JSON document where the <code>values
* </code> will be appended.
* @param values The <code>values</code> to append to the JSON array at the specified <code>path
* </code>.
* @return
* <ul>
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
* Returns a list of integers for every possible path, indicating the new length of the
* new array after appending <code>values</code>, or <code>null</code> for JSON values
* matching the path that are not an array. If <code>path</code> does not exist, an
* empty array will be returned.
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
* Returns the length of the new array after appending <code>values</code> to the array
* at <code>path</code>. If multiple paths are matched, returns the last updated array.
* If the JSON value at <code>path</code> is not a array or if <code>path</code> doesn't
* exist, an error is raised. If <code>key</code> doesn't exist, an error is raised.
* @example
* <pre>{@code
* Json.set(client, "doc", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
* var res = Json.arrappend(client, "doc", "$.b", new String[] {"\"three\""}).get();
* assert Arrays.equals((Object[]) res, new int[] {3}); // New length of the array after appending
* res = Json.arrappend(client, "doc", ".b", new String[] {"\"four\""}).get();
* assert res.equals(4); // New length of the array after appending
* }</pre>
*/
public static CompletableFuture<Object> arrappend(
@NonNull BaseClient client,
@NonNull String key,
@NonNull String path,
@NonNull String[] values) {
return executeCommand(
client, concatenateArrays(new String[] {JSON_ARRAPPEND, key, path}, values));
}

/**
* Appends one or more <code>values</code> to the JSON array at the specified <code>path</code>
* within the JSON document stored at <code>key</code>.
*
* @param client The client to execute the command.
* @param key The <code>key</code> of the JSON document.
* @param path Represents the <code>path</code> within the JSON document where the <code>values
* </code> will be appended.
* @param values The <code>values</code> to append to the JSON array at the specified <code>path
* </code>.
* @return
* <ul>
* <li>For JSONPath (<code>path</code> starts with <code>$</code>):<br>
* Returns a list of integers for every possible path, indicating the new length of the
* new array after appending <code>values</code>, or <code>null</code> for JSON values
* matching the path that are not an array. If <code>path</code> does not exist, an
* empty array will be returned.
* <li>For legacy path (<code>path</code> doesn't start with <code>$</code>):<br>
* Returns the length of the new array after appending <code>values</code> to the array
* at <code>path</code>. If multiple paths are matched, returns the last updated array.
* If the JSON value at <code>path</code> is not a array or if <code>path</code> doesn't
* exist, an error is raised. If <code>key</code> doesn't exist, an error is raised.
* @example
* <pre>{@code
* Json.set(client, "doc", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
* var res = Json.arrappend(client, gs("doc"), gs("$.b"), new GlideString[] {gs("\"three\"")}).get();
* assert Arrays.equals((Object[]) res, new int[] {3}); // New length of the array after appending
* res = Json.arrappend(client, gs("doc"), gs(".b"), new GlideString[] {gs("\"four\"")}).get();
* assert res.equals(4); // New length of the array after appending
* }</pre>
*/
public static CompletableFuture<Object> arrappend(
@NonNull BaseClient client,
@NonNull GlideString key,
@NonNull GlideString path,
@NonNull GlideString[] values) {
return executeCommand(
client, new ArgsBuilder().add(gs(JSON_ARRAPPEND)).add(key).add(path).add(values).toArray());
}

/**
* /** Inserts one or more values into the array at the specified <code>path</code> within the
Comment thread
yipin-chen marked this conversation as resolved.
Outdated
* JSON document stored at <code>key</code>, before the given <code>index</code>.
*
* @param client The client to execute the command.
* @param key The key of the JSON document.
Expand Down
54 changes: 54 additions & 0 deletions java/integTest/src/test/java/glide/modules/JsonTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.google.gson.JsonParser;
Expand All @@ -20,6 +21,7 @@
import glide.api.models.commands.InfoOptions.Section;
import glide.api.models.commands.json.JsonGetOptions;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import lombok.SneakyThrows;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -157,6 +159,58 @@ public void json_set_get_formatting() {
assertEquals(expectedGetResult2, actualGetResult2);
}

@Test
@SneakyThrows
public void arrappend() {
String key = UUID.randomUUID().toString();
String doc = "{\"a\": 1, \"b\": [\"one\", \"two\"]}";

assertEquals(OK, Json.set(client, key, "$", doc).get());

assertArrayEquals(
new Object[] {3L},
(Object[]) Json.arrappend(client, key, "$.b", new String[] {"\"three\""}).get());
assertEquals(
5L, Json.arrappend(client, key, ".b", new String[] {"\"four\"", "\"five\""}).get());

String getResult = Json.get(client, key, new String[] {"$"}).get();
String expectedGetResult =
"[{\"a\": 1, \"b\": [\"one\", \"two\", \"three\", \"four\", \"five\"]}]";
assertEquals(JsonParser.parseString(expectedGetResult), JsonParser.parseString(getResult));

assertArrayEquals(
new Object[] {null},
(Object[]) Json.arrappend(client, key, "$.a", new String[] {"\"value\""}).get());

// JSONPath, path doesn't exist
assertArrayEquals(
new Object[] {},
(Object[]) Json.arrappend(client, key, "$.c", new String[] {"\"value\""}).get());
Comment thread
yipin-chen marked this conversation as resolved.
Outdated

// Legacy path, path doesn't exist
var exception =
assertThrows(
ExecutionException.class,
() -> Json.arrappend(client, key, ".c", new String[] {"\"value\""}).get());

// Legacy path, the JSON value at path is not a array
exception =
assertThrows(
ExecutionException.class,
() -> Json.arrappend(client, key, ".a", new String[] {"\"value\""}).get());

exception =
assertThrows(
ExecutionException.class,
() ->
Json.arrappend(client, "non_existing_key", "$.b", new String[] {"\"six\""}).get());
Comment thread
yipin-chen marked this conversation as resolved.

exception =
assertThrows(
ExecutionException.class,
() -> Json.arrappend(client, "non_existing_key", ".b", new String[] {"\"six\""}).get());
}

@Test
@SneakyThrows
public void arrinsert() {
Expand Down