This is an automated email from the ASF dual-hosted git repository.

twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 3557ad3f feat(json): add support of the command JSON.DEBUG MEMORY 
(#2323)
3557ad3f is described below

commit 3557ad3f9f862ea421f5a94ec6d026b3517487e0
Author: lizhenglei <[email protected]>
AuthorDate: Fri May 31 22:24:54 2024 +0800

    feat(json): add support of the command JSON.DEBUG MEMORY (#2323)
    
    Co-authored-by: 80597928 <[email protected]>
    Co-authored-by: Twice <[email protected]>
---
 src/commands/cmd_json.cc                 | 45 +++++++++++++++++++++++++++++++-
 src/types/json.h                         | 24 +++++++++++++++++
 src/types/redis_json.cc                  | 20 ++++++++++++++
 src/types/redis_json.h                   |  1 +
 tests/gocase/unit/type/json/json_test.go | 23 ++++++++++++++++
 5 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc
index 887cd6c6..8f788953 100644
--- a/src/commands/cmd_json.cc
+++ b/src/commands/cmd_json.cc
@@ -45,6 +45,14 @@ std::string OptionalsToString(const Connection *conn, 
Optionals<T> &opts) {
   return str;
 }
 
+std::string SizeToString(const std::vector<std::size_t> &elems) {
+  std::string result = MultiLen(elems.size());
+  for (const auto &elem : elems) {
+    result += redis::Integer(elem);
+  }
+  return result;
+}
+
 class CommandJsonSet : public Commander {
  public:
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
@@ -632,6 +640,40 @@ class CommandJsonMSet : public Commander {
   }
 };
 
+class CommandJsonDebug : public Commander {
+ public:
+  Status Execute(Server *svr, Connection *conn, std::string *output) override {
+    redis::Json json(svr->storage, conn->GetNamespace());
+
+    std::string path = "$";
+
+    if (!util::EqualICase(args_[1], "memory")) {
+      return {Status::RedisExecErr, "ERR wrong number of arguments for 
'json.debug' command"};
+    }
+
+    if (args_.size() == 4) {
+      path = args_[3];
+    } else if (args_.size() > 4) {
+      return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
+    }
+
+    std::vector<std::size_t> results;
+    auto s = json.DebugMemory(args_[2], path, &results);
+
+    if (s.IsNotFound()) {
+      if (args_.size() == 3) {
+        *output = redis::Integer(0);
+      } else {
+        *output = SizeToString(results);
+      }
+      return Status::OK();
+    }
+    if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
+
+    *output = SizeToString(results);
+    return Status::OK();
+  }
+};
 REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandJsonSet>("json.set", 4, "write", 1, 
1, 1),
                         MakeCmdAttr<CommandJsonGet>("json.get", -2, 
"read-only", 1, 1, 1),
                         MakeCmdAttr<CommandJsonInfo>("json.info", 2, 
"read-only", 1, 1, 1),
@@ -655,6 +697,7 @@ 
REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandJsonSet>("json.set", 4, "write", 1, 1
                         MakeCmdAttr<CommandJsonStrAppend>("json.strappend", 
-3, "write", 1, 1, 1),
                         MakeCmdAttr<CommandJsonStrLen>("json.strlen", -2, 
"read-only", 1, 1, 1),
                         MakeCmdAttr<CommandJsonMGet>("json.mget", -3, 
"read-only", 1, -2, 1),
-                        MakeCmdAttr<CommandJsonMSet>("json.mset", -4, "write", 
1, -3, 3), );
+                        MakeCmdAttr<CommandJsonMSet>("json.mset", -4, "write", 
1, -3, 3),
+                        MakeCmdAttr<CommandJsonDebug>("json.debug", -3, 
"read-only", 2, 2, 1));
 
 }  // namespace redis
diff --git a/src/types/json.h b/src/types/json.h
index 2f5c0c45..fe651ccc 100644
--- a/src/types/json.h
+++ b/src/types/json.h
@@ -217,6 +217,30 @@ struct JsonValue {
     return results;
   }
 
+  StatusOr<std::vector<size_t>> GetBytes(std::string_view path, 
JsonStorageFormat format,
+                                         int max_nesting_depth = 
std::numeric_limits<int>::max()) const {
+    std::vector<size_t> results;
+    Status s;
+    try {
+      jsoncons::jsonpath::json_query(value, path, [&](const std::string & 
/*path*/, const jsoncons::json &origin) {
+        if (!s) return;
+        std::string buffer;
+        JsonValue query_value(origin);
+        if (format == JsonStorageFormat::JSON) {
+          s = query_value.Dump(&buffer, max_nesting_depth);
+        } else if (format == JsonStorageFormat::CBOR) {
+          s = query_value.DumpCBOR(&buffer, max_nesting_depth);
+        }
+        results.emplace_back(buffer.size());
+      });
+    } catch (const jsoncons::jsonpath::jsonpath_error &e) {
+      return {Status::NotOK, e.what()};
+    }
+    if (!s) return {Status::NotOK, s.Msg()};
+
+    return results;
+  }
+
   StatusOr<JsonValue> Get(std::string_view path) const {
     try {
       return jsoncons::jsonpath::json_query(value, path);
diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc
index ca879981..9929b22c 100644
--- a/src/types/redis_json.cc
+++ b/src/types/redis_json.cc
@@ -627,4 +627,24 @@ std::vector<rocksdb::Status> Json::readMulti(const 
std::vector<Slice> &ns_keys,
   return statuses;
 }
 
+rocksdb::Status Json::DebugMemory(const std::string &user_key, const 
std::string &path, std::vector<size_t> *results) {
+  auto ns_key = AppendNamespacePrefix(user_key);
+  JsonMetadata metadata;
+  if (path == "$") {
+    std::string bytes;
+    Slice rest;
+    auto s = GetMetadata(GetOptions{}, {kRedisJson}, ns_key, &bytes, 
&metadata, &rest);
+    if (!s.ok()) return s;
+    results->emplace_back(rest.size());
+  } else {
+    JsonValue json_val;
+    auto s = read(ns_key, &metadata, &json_val);
+    if (!s.ok()) return s;
+    auto str_bytes = json_val.GetBytes(path, metadata.format, 
storage_->GetConfig()->json_max_nesting_depth);
+    if (!str_bytes) return rocksdb::Status::InvalidArgument(str_bytes.Msg());
+    *results = std::move(*str_bytes);
+  }
+  return rocksdb::Status::OK();
+}
+
 }  // namespace redis
diff --git a/src/types/redis_json.h b/src/types/redis_json.h
index f72b2c00..8dc21235 100644
--- a/src/types/redis_json.h
+++ b/src/types/redis_json.h
@@ -68,6 +68,7 @@ class Json : public Database {
                                     std::vector<JsonValue> &results);
   rocksdb::Status MSet(const std::vector<std::string> &user_keys, const 
std::vector<std::string> &paths,
                        const std::vector<std::string> &values);
+  rocksdb::Status DebugMemory(const std::string &user_key, const std::string 
&path, std::vector<size_t> *results);
 
  private:
   rocksdb::Status write(Slice ns_key, JsonMetadata *metadata, const JsonValue 
&json_val);
diff --git a/tests/gocase/unit/type/json/json_test.go 
b/tests/gocase/unit/type/json/json_test.go
index 0f2bbf8f..e609d14f 100644
--- a/tests/gocase/unit/type/json/json_test.go
+++ b/tests/gocase/unit/type/json/json_test.go
@@ -631,6 +631,29 @@ func TestJson(t *testing.T) {
                EqualJSON(t, `[{"a": 4, "b": 5, "nested": {"a": 6}, "c": 
null}]`, rdb.Do(ctx, "JSON.GET", "a1", "$").Val())
                EqualJSON(t, `[4]`, rdb.Do(ctx, "JSON.GET", "a1", "$.a").Val())
        })
+
+       t.Run("JSON.DEBUG MEMORY basics", func(t *testing.T) {
+               require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", 
`{"b":true,"x":1, "y":1.2, "z": {"x":[1,2,3], "y": null}, 
"v":{"x":"y"},"f":{"x":[]}}`).Err())
+               //object
+               var result1 = make([]interface{}, 0)
+               result1 = append(result1, int64(43))
+               require.Equal(t, result1, rdb.Do(ctx, "JSON.DEBUG", "MEMORY", 
"a", "$").Val())
+               //integer string array empty_array
+               var result2 = make([]interface{}, 0)
+               result2 = append(result2, int64(1), int64(1), int64(2), 
int64(4))
+               require.Equal(t, result2, rdb.Do(ctx, "JSON.DEBUG", "MEMORY", 
"a", "$..x").Val())
+               //null object
+               var result3 = make([]interface{}, 0)
+               result3 = append(result3, int64(9), int64(1))
+               require.Equal(t, result3, rdb.Do(ctx, "JSON.DEBUG", "MEMORY", 
"a", "$..y").Val())
+               //no no_exists
+               require.Equal(t, []interface{}{}, rdb.Do(ctx, "JSON.DEBUG", 
"MEMORY", "a", "$..no_exists").Val())
+               //no key no path
+               require.Equal(t, rdb.Do(ctx, "JSON.DEBUG", "MEMORY", 
"not_exists").Val(), int64(0))
+               //no key have path
+               require.Equal(t, []interface{}{}, rdb.Do(ctx, "JSON.DEBUG", 
"MEMORY", "not_exists", "$").Val())
+
+       })
 }
 
 func EqualJSON(t *testing.T, expected string, actual interface{}) {

Reply via email to