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

hulk 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 0bd1f948 Add the support of JSON.TYPE command (#1847)
0bd1f948 is described below

commit 0bd1f948e1cd12df6e5658ac81e94631a656069a
Author: hulk <[email protected]>
AuthorDate: Sat Oct 21 22:01:28 2023 +0800

    Add the support of JSON.TYPE command (#1847)
    
    Co-authored-by: Twice <[email protected]>
---
 src/commands/cmd_json.cc                 | 26 ++++++++++++++++++++++
 src/types/json.h                         | 38 ++++++++++++++++++++++++++++++++
 src/types/redis_json.cc                  | 12 ++++++++++
 src/types/redis_json.h                   |  1 +
 tests/gocase/unit/type/json/json_test.go | 33 +++++++++++++++++++++++++++
 5 files changed, 110 insertions(+)

diff --git a/src/commands/cmd_json.cc b/src/commands/cmd_json.cc
index 1a014d28..34cc6415 100644
--- a/src/commands/cmd_json.cc
+++ b/src/commands/cmd_json.cc
@@ -123,8 +123,34 @@ class CommandJsonArrAppend : public Commander {
   }
 };
 
+class CommandJsonType : public Commander {
+ public:
+  Status Execute(Server *svr, Connection *conn, std::string *output) override {
+    redis::Json json(svr->storage, conn->GetNamespace());
+
+    std::vector<std::string> types;
+
+    std::string path = "$";
+    if (args_.size() == 3) {
+      path = args_[2];
+    } else if (args_.size() > 3) {
+      return {Status::RedisExecErr, "The number of arguments is more than 
expected"};
+    }
+    auto s = json.Type(args_[1], path, &types);
+    if (!s.ok() && !s.IsNotFound()) return {Status::RedisExecErr, 
s.ToString()};
+    if (s.IsNotFound()) {
+      *output = redis::NilString();
+      return Status::OK();
+    }
+
+    *output = redis::MultiBulkString(types);
+    return Status::OK();
+  }
+};
+
 REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandJsonSet>("json.set", -3, "write", 
1, 1, 1),
                         MakeCmdAttr<CommandJsonGet>("json.get", -2, 
"read-only", 1, 1, 1),
+                        MakeCmdAttr<CommandJsonType>("json.type", -2, 
"read-only", 1, 1, 1),
                         MakeCmdAttr<CommandJsonArrAppend>("json.arrappend", 
-4, "write", 1, 1, 1), );
 
 }  // namespace redis
diff --git a/src/types/json.h b/src/types/json.h
index eb5d7878..667ce51b 100644
--- a/src/types/json.h
+++ b/src/types/json.h
@@ -132,6 +132,44 @@ struct JsonValue {
     return Status::OK();
   }
 
+  Status Type(std::string_view path, std::vector<std::string> *types) const {
+    types->clear();
+    try {
+      jsoncons::jsonpath::json_query(value, path, [&types](const std::string & 
/*path*/, const jsoncons::json &val) {
+        switch (val.type()) {
+          case jsoncons::json_type::null_value:
+            types->emplace_back("null");
+            break;
+          case jsoncons::json_type::bool_value:
+            types->emplace_back("boolean");
+            break;
+          case jsoncons::json_type::int64_value:
+          case jsoncons::json_type::uint64_value:
+            types->emplace_back("integer");
+            break;
+          case jsoncons::json_type::double_value:
+            types->emplace_back("number");
+            break;
+          case jsoncons::json_type::string_value:
+            types->emplace_back("string");
+            break;
+          case jsoncons::json_type::array_value:
+            types->emplace_back("array");
+            break;
+          case jsoncons::json_type::object_value:
+            types->emplace_back("object");
+            break;
+          default:
+            types->emplace_back("unknown");
+            break;
+        }
+      });
+    } catch (const jsoncons::jsonpath::jsonpath_error &e) {
+      return {Status::NotOK, e.what()};
+    }
+    return Status::OK();
+  }
+
   JsonValue(const JsonValue &) = default;
   JsonValue(JsonValue &&) = default;
 
diff --git a/src/types/redis_json.cc b/src/types/redis_json.cc
index cc4fc768..e16fc161 100644
--- a/src/types/redis_json.cc
+++ b/src/types/redis_json.cc
@@ -150,4 +150,16 @@ rocksdb::Status Json::ArrAppend(const std::string 
&user_key, const std::string &
   return write(ns_key, &metadata, value);
 }
 
+rocksdb::Status Json::Type(const std::string &user_key, const std::string 
&path, std::vector<std::string> *results) {
+  auto ns_key = AppendNamespacePrefix(user_key);
+
+  JsonMetadata metadata;
+  JsonValue json_val;
+  auto s = read(ns_key, &metadata, &json_val);
+  if (!s.ok()) return s;
+
+  auto res = json_val.Type(path, results);
+  return rocksdb::Status::OK();
+}
+
 }  // namespace redis
diff --git a/src/types/redis_json.h b/src/types/redis_json.h
index 04895be5..d932d53d 100644
--- a/src/types/redis_json.h
+++ b/src/types/redis_json.h
@@ -35,6 +35,7 @@ class Json : public Database {
 
   rocksdb::Status Set(const std::string &user_key, const std::string &path, 
const std::string &value);
   rocksdb::Status Get(const std::string &user_key, const 
std::vector<std::string> &paths, JsonValue *result);
+  rocksdb::Status Type(const std::string &user_key, const std::string &path, 
std::vector<std::string> *results);
   rocksdb::Status ArrAppend(const std::string &user_key, const std::string 
&path,
                             const std::vector<std::string> &values, 
std::vector<uint64_t> *result_count);
 
diff --git a/tests/gocase/unit/type/json/json_test.go 
b/tests/gocase/unit/type/json/json_test.go
index fe6a8787..452dd250 100644
--- a/tests/gocase/unit/type/json/json_test.go
+++ b/tests/gocase/unit/type/json/json_test.go
@@ -23,6 +23,8 @@ import (
        "context"
        "testing"
 
+       "github.com/redis/go-redis/v9"
+
        "github.com/apache/kvrocks/tests/gocase/util"
        "github.com/stretchr/testify/require"
 )
@@ -95,4 +97,35 @@ func TestJson(t *testing.T) {
                require.Equal(t, `[]`, rdb.Do(ctx, "JSON.GET", "a", 
"$.x.x").Val())
                require.Equal(t, 
`[{"x":[1,1,2,3,1,2],"y":[{"x":[1,2],"y":[]}]}]`, rdb.Do(ctx, "JSON.GET", "a", 
"$").Val())
        })
+
+       t.Run("JSON.TYPE 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"}}`).Err())
+
+               types, err := rdb.Do(ctx, "JSON.TYPE", "a").StringSlice()
+               require.NoError(t, err)
+               require.Equal(t, []string{"object"}, types)
+
+               types, err = rdb.Do(ctx, "JSON.TYPE", "a", "$").StringSlice()
+               require.NoError(t, err)
+               require.Equal(t, []string{"object"}, types)
+
+               types, err = rdb.Do(ctx, "JSON.TYPE", "a", "$..x").StringSlice()
+               require.NoError(t, err)
+               require.EqualValues(t, []string{"integer", "string", "array"}, 
types)
+
+               types, err = rdb.Do(ctx, "JSON.TYPE", "a", "$..y").StringSlice()
+               require.NoError(t, err)
+               require.EqualValues(t, []string{"number", "null"}, types)
+
+               types, err = rdb.Do(ctx, "JSON.TYPE", "a", "$.b").StringSlice()
+               require.NoError(t, err)
+               require.EqualValues(t, []string{"boolean"}, types)
+
+               types, err = rdb.Do(ctx, "JSON.TYPE", "a", 
"$.no_exists").StringSlice()
+               require.NoError(t, err)
+               require.EqualValues(t, []string{}, types)
+
+               _, err = rdb.Do(ctx, "JSON.TYPE", "not_exists", 
"$").StringSlice()
+               require.EqualError(t, err, redis.Nil.Error())
+       })
 }

Reply via email to