From a882b1f4e96e6951e88bde746c589fc5d986fa90 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Mon, 28 Oct 2024 14:28:45 -0700 Subject: [PATCH] Java: `JSON.DEBUG`. (#2520) * `JSON.DEBUG`. Signed-off-by: Yury-Fridlyand --- CHANGELOG.md | 1 + .../api/commands/servermodules/Json.java | 228 ++++++++++++++++++ .../test/java/glide/modules/JsonTests.java | 25 ++ 3 files changed, 254 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8f7e3bea..cb98c5549b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ * Java: Added `FT.PROFILE` ([#2473](https://github.com/valkey-io/valkey-glide/pull/2473)) * Java: Added `JSON.SET` and `JSON.GET` ([#2462](https://github.com/valkey-io/valkey-glide/pull/2462)) * Node: Added `FT.CREATE` ([#2501](https://github.com/valkey-io/valkey-glide/pull/2501)) +* Java: Added `JSON.DEBUG` ([#2520](https://github.com/valkey-io/valkey-glide/pull/2520)) * Java: Added `JSON.ARRINSERT` and `JSON.ARRLEN` ([#2476](https://github.com/valkey-io/valkey-glide/pull/2476)) * Java: Added `JSON.ARRPOP` ([#2486](https://github.com/valkey-io/valkey-glide/pull/2486)) * Java: Added `JSON.OBJLEN` and `JSON.OBJKEYS` ([#2492](https://github.com/valkey-io/valkey-glide/pull/2492)) diff --git a/java/client/src/main/java/glide/api/commands/servermodules/Json.java b/java/client/src/main/java/glide/api/commands/servermodules/Json.java index 5fd39aba1e..efd0af082b 100644 --- a/java/client/src/main/java/glide/api/commands/servermodules/Json.java +++ b/java/client/src/main/java/glide/api/commands/servermodules/Json.java @@ -25,6 +25,8 @@ public class Json { 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"; + private static final String[] JSON_DEBUG_MEMORY = new String[] {JSON_PREFIX + "DEBUG", "MEMORY"}; + private static final String[] JSON_DEBUG_FIELDS = new String[] {JSON_PREFIX + "DEBUG", "FIELDS"}; private static final String JSON_ARRPOP = JSON_PREFIX + "ARRPOP"; private static final String JSON_ARRTRIM = JSON_PREFIX + "ARRTRIM"; private static final String JSON_OBJLEN = JSON_PREFIX + "OBJLEN"; @@ -716,6 +718,232 @@ public static CompletableFuture arrlen( return executeCommand(client, new GlideString[] {gs(JSON_ARRLEN), key}); } + /** + * Reports memory usage in bytes of a JSON object at the specified path within the + * JSON document stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns an Object[] with a list of numbers for every possible path, + * indicating the memory usage. If path does not exist, an empty array will + * be returned. + *
  • For legacy path (path doesn't start with $):
    + * Returns an integer representing the memory usage. If multiple paths are matched, + * returns the data of the first matching object. If path doesn't exist, an + * error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugMemory(client, "doc", "..").get();
+     * assert res == 258L;
+     * }
+ */ + public static CompletableFuture debugMemory( + @NonNull BaseClient client, @NonNull String key, @NonNull String path) { + return executeCommand(client, concatenateArrays(JSON_DEBUG_MEMORY, new String[] {key, path})); + } + + /** + * Reports the number of fields at the specified path within the JSON document stored + * at key.
+ * Each non-container JSON value counts as one field. Objects and arrays recursively count one + * field for each of their containing JSON values. Each container value, except the root + * container, counts as one additional field. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns an Object[] with a list of numbers for every possible path, + * indicating the number of fields. If path does not exist, an empty array + * will be returned. + *
  • For legacy path (path doesn't start with $):
    + * Returns an integer representing the number of fields. If multiple paths are matched, + * returns the data of the first matching object. If path doesn't exist, an + * error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugFields(client, "doc", "$[*]").get();
+     * assert Arrays.equals((Object[]) res, new Object[] {1, 1, 1, 1, 1, 0, 0, 2, 3});
+     * }
+ */ + public static CompletableFuture debugFields( + @NonNull BaseClient client, @NonNull String key, @NonNull String path) { + return executeCommand(client, concatenateArrays(JSON_DEBUG_FIELDS, new String[] {key, path})); + } + + /** + * Reports memory usage in bytes of a JSON object at the specified path within the + * JSON document stored at key. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns an Object[] with a list of numbers for every possible path, + * indicating the memory usage. If path does not exist, an empty array will + * be returned. + *
  • For legacy path (path doesn't start with $):
    + * Returns an integer representing the memory usage. If multiple paths are matched, + * returns the data of the first matching object. If path doesn't exist, an + * error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugMemory(client, gs("doc"), gs("..")).get();
+     * assert res == 258L;
+     * }
+ */ + public static CompletableFuture debugMemory( + @NonNull BaseClient client, @NonNull GlideString key, @NonNull GlideString path) { + return executeCommand( + client, new ArgsBuilder().add(JSON_DEBUG_MEMORY).add(key).add(path).toArray()); + } + + /** + * Reports the number of fields at the specified path within the JSON document stored + * at key.
+ * Each non-container JSON value counts as one field. Objects and arrays recursively count one + * field for each of their containing JSON values. Each container value, except the root + * container, counts as one additional field. + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @param path The path within the JSON document. + * @return + *
    + *
  • For JSONPath (path starts with $):
    + * Returns an Object[] with a list of numbers for every possible path, + * indicating the number of fields. If path does not exist, an empty array + * will be returned. + *
  • For legacy path (path doesn't start with $):
    + * Returns an integer representing the number of fields. If multiple paths are matched, + * returns the data of the first matching object. If path doesn't exist, an + * error is raised. + *
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugFields(client, gs("doc"), gs("$[*]")).get();
+     * assert Arrays.equals((Object[]) res, new Object[] {1, 1, 1, 1, 1, 0, 0, 2, 3});
+     * }
+ */ + public static CompletableFuture debugFields( + @NonNull BaseClient client, @NonNull GlideString key, @NonNull GlideString path) { + return executeCommand( + client, new ArgsBuilder().add(JSON_DEBUG_FIELDS).add(key).add(path).toArray()); + } + + /** + * Reports memory usage in bytes of a JSON object at the specified path within the + * JSON document stored at key.
+ * Equivalent to {@link #debugMemory(BaseClient, String, String)} with path set to + * "..". + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return The total memory usage in bytes of the entire JSON document.
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugMemory(client, "doc").get();
+     * assert res == 258L;
+     * }
+ */ + public static CompletableFuture debugMemory( + @NonNull BaseClient client, @NonNull String key) { + return executeCommand(client, concatenateArrays(JSON_DEBUG_MEMORY, new String[] {key})); + } + + /** + * Reports the number of fields at the specified path within the JSON document stored + * at key.
+ * Each non-container JSON value counts as one field. Objects and arrays recursively count one + * field for each of their containing JSON values. Each container value, except the root + * container, counts as one additional field.
+ * Equivalent to {@link #debugFields(BaseClient, String, String)} with path set to + * "..". + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return The total number of fields in the entire JSON document.
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugFields(client, "doc").get();
+     * assert res == 14L;
+     * }
+ */ + public static CompletableFuture debugFields( + @NonNull BaseClient client, @NonNull String key) { + return executeCommand(client, concatenateArrays(JSON_DEBUG_FIELDS, new String[] {key})); + } + + /** + * Reports memory usage in bytes of a JSON object at the specified path within the + * JSON document stored at key.
+ * Equivalent to {@link #debugMemory(BaseClient, GlideString, GlideString)} with path + * set to gs(".."). + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return The total memory usage in bytes of the entire JSON document.
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugMemory(client, gs("doc")).get();
+     * assert res == 258L;
+     * }
+ */ + public static CompletableFuture debugMemory( + @NonNull BaseClient client, @NonNull GlideString key) { + return executeCommand(client, new ArgsBuilder().add(JSON_DEBUG_MEMORY).add(key).toArray()); + } + + /** + * Reports the number of fields at the specified path within the JSON document stored + * at key.
+ * Each non-container JSON value counts as one field. Objects and arrays recursively count one + * field for each of their containing JSON values. Each container value, except the root + * container, counts as one additional field.
+ * Equivalent to {@link #debugFields(BaseClient, GlideString, GlideString)} with path + * set to gs(".."). + * + * @param client The client to execute the command. + * @param key The key of the JSON document. + * @return The total number of fields in the entire JSON document.
+ * If key doesn't exist, returns null. + * @example + *
{@code
+     * Json.set(client, "doc", "$", "[1, 2.3, \"foo\", true, null, {}, [], {\"a\":1, \"b\":2}, [1, 2, 3]]").get();
+     * var res = Json.debugFields(client, gs("doc")).get();
+     * assert res == 14L;
+     * }
+ */ + public static CompletableFuture debugFields( + @NonNull BaseClient client, @NonNull GlideString key) { + return executeCommand(client, new ArgsBuilder().add(JSON_DEBUG_FIELDS).add(key).toArray()); + } + /** * Pops the last element from the array stored in the root of the JSON document stored at * key. Equivalent to {@link #arrpop(BaseClient, String, String)} with diff --git a/java/integTest/src/test/java/glide/modules/JsonTests.java b/java/integTest/src/test/java/glide/modules/JsonTests.java index ed5336844a..2b07f9d44e 100644 --- a/java/integTest/src/test/java/glide/modules/JsonTests.java +++ b/java/integTest/src/test/java/glide/modules/JsonTests.java @@ -291,6 +291,31 @@ public void arrinsert() { assertEquals(JsonParser.parseString(expected), JsonParser.parseString(doc)); } + @Test + @SneakyThrows + public void debug() { + String key = UUID.randomUUID().toString(); + + var doc = + "{ \"key1\": 1, \"key2\": 3.5, \"key3\": {\"nested_key\": {\"key1\": [4, 5]}}, \"key4\":" + + " [1, 2, 3], \"key5\": 0, \"key6\": \"hello\", \"key7\": null, \"key8\":" + + " {\"nested_key\": {\"key1\": 3.5953862697246314e307}}, \"key9\":" + + " 3.5953862697246314e307, \"key10\": true }"; + assertEquals("OK", Json.set(client, key, "$", doc).get()); + + assertArrayEquals(new Object[] {1L}, (Object[]) Json.debugFields(client, key, "$.key1").get()); + + assertEquals(2L, Json.debugFields(client, gs(key), gs(".key3.nested_key.key1")).get()); + + assertArrayEquals( + new Object[] {16L}, (Object[]) Json.debugMemory(client, key, "$.key4[2]").get()); + + assertEquals(16L, Json.debugMemory(client, gs(key), gs(".key6")).get()); + + assertEquals(504L, Json.debugMemory(client, key).get()); + assertEquals(19L, Json.debugFields(client, gs(key)).get()); + } + @Test @SneakyThrows public void arrlen() {