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())
+ })
}