22package glide .api .commands .servermodules ;
33
44import static glide .api .models .GlideString .gs ;
5+ import static glide .utils .ArrayTransformUtils .castArray ;
56import static glide .utils .ArrayTransformUtils .concatenateArrays ;
67
78import 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>.
0 commit comments