Skip to content

Commit d052c4c

Browse files
Yury-Fridlyandjamesx-improvingacarbonetto
authored
Java: JSON.MGET. (#2514)
* `JSON.MGET`. Signed-off-by: Yury-Fridlyand <[email protected]> Co-authored-by: James Xin <[email protected]> Co-authored-by: Andrew Carbonetto <[email protected]>
1 parent 08936ce commit d052c4c

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* Java: Added `FT.AGGREGATE` ([#2466](https://github.com/valkey-io/valkey-glide/pull/2466))
2828
* Java: Added `FT.PROFILE` ([#2473](https://github.com/valkey-io/valkey-glide/pull/2473))
2929
* Java: Added `JSON.SET` and `JSON.GET` ([#2462](https://github.com/valkey-io/valkey-glide/pull/2462))
30+
* Java: Added `JSON.MGET` ([#2514](https://github.com/valkey-io/valkey-glide/pull/2514))
3031
* Node: Added `FT.CREATE` ([#2501](https://github.com/valkey-io/valkey-glide/pull/2501))
3132
* Node: Added `FT.INFO` ([#2540](https://github.com/valkey-io/valkey-glide/pull/2540))
3233
* Node: Added `FT.AGGREGATE` ([#2554](https://github.com/valkey-io/valkey-glide/pull/2554))

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
@@ -2,6 +2,7 @@
22
package glide.api.commands.servermodules;
33

44
import static glide.api.models.GlideString.gs;
5+
import static glide.utils.ArrayTransformUtils.castArray;
56
import static glide.utils.ArrayTransformUtils.concatenateArrays;
67

78
import glide.api.BaseClient;
@@ -23,6 +24,7 @@ public class Json {
2324
private static final String JSON_PREFIX = "JSON.";
2425
private static final String JSON_SET = JSON_PREFIX + "SET";
2526
private static final String JSON_GET = JSON_PREFIX + "GET";
27+
private static final String JSON_MGET = JSON_PREFIX + "MGET";
2628
private static final String JSON_NUMINCRBY = JSON_PREFIX + "NUMINCRBY";
2729
private static final String JSON_NUMMULTBY = JSON_PREFIX + "NUMMULTBY";
2830
private static final String JSON_ARRAPPEND = JSON_PREFIX + "ARRAPPEND";
@@ -413,6 +415,85 @@ public static CompletableFuture<GlideString> get(
413415
new ArgsBuilder().add(gs(JSON_GET)).add(key).add(options.toArgs()).add(paths).toArray());
414416
}
415417

418+
/**
419+
* Retrieves the JSON values at the specified <code>path</code> stored at multiple <code>keys
420+
* </code>.
421+
*
422+
* @apiNote When in cluster mode, if keys in <code>keys</code> map to different hash slots, the
423+
* command will be split across these slots and executed separately for each. This means the
424+
* command is atomic only at the slot level. If one or more slot-specific requests fail, the
425+
* entire call will return the first encountered error, even though some requests may have
426+
* succeeded while others did not. If this behavior impacts your application logic, consider
427+
* splitting the request into sub-requests per slot to ensure atomicity.
428+
* @param client The client to execute the command.
429+
* @param keys The keys of the JSON documents.
430+
* @param path The path within the JSON documents.
431+
* @return An array with requested values for each key.
432+
* <ul>
433+
* <li>For JSONPath (path starts with <code>$</code>): Returns a stringified JSON list
434+
* replies for every possible path, or a string representation of an empty array, if
435+
* path doesn't exist.
436+
* <li>For legacy path (path doesn't start with <code>$</code>): Returns a string
437+
* representation of the value in <code>path</code>. If <code>path</code> doesn't exist,
438+
* the corresponding array element will be <code>null</code>.
439+
* </ul>
440+
* If a <code>key</code> doesn't exist, the corresponding array element will be <code>null
441+
* </code>.
442+
* @example
443+
* <pre>{@code
444+
* Json.set(client, "doc1", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
445+
* Json.set(client, "doc2", "$", "{\"a\": 1, \"c\": false}").get();
446+
* var res = Json.mget(client, new String[] { "doc1", "doc2", "non_existing" }, "$.c").get();
447+
* assert Arrays.equals(res, new String[] { "[]", "[false]", null });
448+
* }</pre>
449+
*/
450+
public static CompletableFuture<String[]> mget(
451+
@NonNull BaseClient client, @NonNull String[] keys, @NonNull String path) {
452+
return Json.<Object[]>executeCommand(
453+
client, concatenateArrays(new String[] {JSON_MGET}, keys, new String[] {path}))
454+
.thenApply(res -> castArray(res, String.class));
455+
}
456+
457+
/**
458+
* Retrieves the JSON values at the specified <code>path</code> stored at multiple <code>keys
459+
* </code>.
460+
*
461+
* @apiNote When in cluster mode, if keys in <code>keys</code> map to different hash slots, the
462+
* command will be split across these slots and executed separately for each. This means the
463+
* command is atomic only at the slot level. If one or more slot-specific requests fail, the
464+
* entire call will return the first encountered error, even though some requests may have
465+
* succeeded while others did not. If this behavior impacts your application logic, consider
466+
* splitting the request into sub-requests per slot to ensure atomicity.
467+
* @param client The client to execute the command.
468+
* @param keys The keys of the JSON documents.
469+
* @param path The path within the JSON documents.
470+
* @return An array with requested values for each key.
471+
* <ul>
472+
* <li>For JSONPath (path starts with <code>$</code>): Returns a stringified JSON list
473+
* replies for every possible path, or a string representation of an empty array, if
474+
* path doesn't exist.
475+
* <li>For legacy path (path doesn't start with <code>$</code>): Returns a string
476+
* representation of the value in <code>path</code>. If <code>path</code> doesn't exist,
477+
* the corresponding array element will be <code>null</code>.
478+
* </ul>
479+
* If a <code>key</code> doesn't exist, the corresponding array element will be <code>null
480+
* </code>.
481+
* @example
482+
* <pre>{@code
483+
* Json.set(client, "doc1", "$", "{\"a\": 1, \"b\": [\"one\", \"two\"]}").get();
484+
* Json.set(client, "doc2", "$", "{\"a\": 1, \"c\": false}").get();
485+
* var res = Json.mget(client, new GlideString[] { gs("doc1"), gs("doc2"), gs("doc3") }, gs("$.c")).get();
486+
* assert Arrays.equals(res, new GlideString[] { gs("[]"), gs("[false]"), null });
487+
* }</pre>
488+
*/
489+
public static CompletableFuture<GlideString[]> mget(
490+
@NonNull BaseClient client, @NonNull GlideString[] keys, @NonNull GlideString path) {
491+
return Json.<Object[]>executeCommand(
492+
client,
493+
concatenateArrays(new GlideString[] {gs(JSON_MGET)}, keys, new GlideString[] {path}))
494+
.thenApply(res -> castArray(res, GlideString.class));
495+
}
496+
416497
/**
417498
* Appends one or more <code>values</code> to the JSON array at the specified <code>path</code>
418499
* within the JSON document stored at <code>key</code>.

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import glide.api.models.commands.InfoOptions.Section;
2323
import glide.api.models.commands.json.JsonArrindexOptions;
2424
import glide.api.models.commands.json.JsonGetOptions;
25+
import java.util.Map;
2526
import java.util.UUID;
2627
import java.util.concurrent.ExecutionException;
2728
import lombok.SneakyThrows;
@@ -940,6 +941,28 @@ public void objkeys() {
940941
assertArrayEquals(new Object[] {gs("a"), gs("b")}, res);
941942
}
942943

944+
@Test
945+
@SneakyThrows
946+
public void mget() {
947+
String key1 = UUID.randomUUID().toString();
948+
String key2 = UUID.randomUUID().toString();
949+
var data =
950+
Map.of(
951+
key1, "{\"a\": 1, \"b\": [\"one\", \"two\"]}",
952+
key2, "{\"a\": 1, \"c\": false}");
953+
954+
for (var entry : data.entrySet()) {
955+
assertEquals("OK", Json.set(client, entry.getKey(), "$", entry.getValue()).get());
956+
}
957+
958+
var res1 =
959+
Json.mget(client, new String[] {key1, key2, UUID.randomUUID().toString()}, "$.c").get();
960+
assertArrayEquals(new String[] {"[]", "[false]", null}, res1);
961+
962+
var res2 = Json.mget(client, new GlideString[] {gs(key1), gs(key2)}, gs(".b[*]")).get();
963+
assertArrayEquals(new GlideString[] {gs("\"one\""), null}, res2);
964+
}
965+
943966
@Test
944967
@SneakyThrows
945968
public void json_forget() {

0 commit comments

Comments
 (0)