Skip to content

Commit 6e3c580

Browse files
authored
Merge branch 'unstable' into json-strappend
2 parents 16c6bd5 + b889b0e commit 6e3c580

File tree

5 files changed

+93
-0
lines changed

5 files changed

+93
-0
lines changed

src/commands/cmd_json.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,39 @@ class CommandJsonArrPop : public Commander {
389389
int64_t index_ = -1;
390390
};
391391

392+
class CommandJsonObjLen : public Commander {
393+
public:
394+
Status Execute(Server *svr, Connection *conn, std::string *output) override {
395+
redis::Json json(svr->storage, conn->GetNamespace());
396+
397+
std::string path = "$";
398+
if (args_.size() == 3) {
399+
path = args_[2];
400+
} else if (args_.size() > 3) {
401+
return {Status::RedisExecErr, "The number of arguments is more than expected"};
402+
}
403+
404+
std::vector<std::optional<uint64_t>> results;
405+
auto s = json.ObjLen(args_[1], path, results);
406+
if (s.IsNotFound()) {
407+
*output = redis::NilString();
408+
return Status::OK();
409+
}
410+
if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
411+
412+
*output = redis::MultiLen(results.size());
413+
for (const auto &len : results) {
414+
if (len.has_value()) {
415+
*output += redis::Integer(len.value());
416+
} else {
417+
*output += redis::NilString();
418+
}
419+
}
420+
421+
return Status::OK();
422+
}
423+
};
424+
392425
class CommandJsonArrTrim : public Commander {
393426
public:
394427
Status Parse(const std::vector<std::string> &args) override {
@@ -608,6 +641,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandJsonSet>("json.set", 4, "write", 1, 1
608641
MakeCmdAttr<CommandJsonDel>("json.forget", -2, "write", 1, 1, 1),
609642
MakeCmdAttr<CommandJsonNumIncrBy>("json.numincrby", 4, "write", 1, 1, 1),
610643
MakeCmdAttr<CommandJsonNumMultBy>("json.nummultby", 4, "write", 1, 1, 1),
644+
MakeCmdAttr<CommandJsonObjLen>("json.objlen", -2, "read-only", 1, 1, 1),
611645
MakeCmdAttr<CommandJsonStrAppend>("json.strappend", -3, "write", 1, 1, 1),
612646
MakeCmdAttr<CommandJsonStrLen>("json.strlen", -2, "read-only", 1, 1, 1), );
613647

src/types/json.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,22 @@ struct JsonValue {
467467
return Status::OK();
468468
}
469469

470+
Status ObjLen(std::string_view path, std::vector<std::optional<uint64_t>> &obj_lens) const {
471+
try {
472+
jsoncons::jsonpath::json_query(value, path,
473+
[&obj_lens](const std::string & /*path*/, const jsoncons::json &basic_json) {
474+
if (basic_json.is_object()) {
475+
obj_lens.emplace_back(static_cast<uint64_t>(basic_json.size()));
476+
} else {
477+
obj_lens.emplace_back(std::nullopt);
478+
}
479+
});
480+
} catch (const jsoncons::jsonpath::jsonpath_error &e) {
481+
return {Status::NotOK, e.what()};
482+
}
483+
return Status::OK();
484+
}
485+
470486
StatusOr<std::vector<std::optional<JsonValue>>> ArrPop(std::string_view path, int64_t index = -1) {
471487
std::vector<std::optional<JsonValue>> popped_values;
472488

src/types/redis_json.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,4 +501,18 @@ rocksdb::Status Json::StrLen(const std::string &user_key, const std::string &pat
501501
return rocksdb::Status::OK();
502502
}
503503

504+
rocksdb::Status Json::ObjLen(const std::string &user_key, const std::string &path,
505+
std::vector<std::optional<uint64_t>> &obj_lens) {
506+
auto ns_key = AppendNamespacePrefix(user_key);
507+
JsonMetadata metadata;
508+
JsonValue json_val;
509+
auto s = read(ns_key, &metadata, &json_val);
510+
if (!s.ok()) return s;
511+
512+
auto len_res = json_val.ObjLen(path, obj_lens);
513+
if (!len_res) return rocksdb::Status::InvalidArgument(len_res.Msg());
514+
515+
return rocksdb::Status::OK();
516+
}
517+
504518
} // namespace redis

src/types/redis_json.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class Json : public Database {
6464
rocksdb::Status StrAppend(const std::string &user_key, const std::string &path, const std::string &value,
6565
std::vector<uint64_t> &append_cnt);
6666
rocksdb::Status StrLen(const std::string &user_key, const std::string &path, std::vector<uint64_t> &lens);
67+
rocksdb::Status ObjLen(const std::string &user_key, const std::string &path,
68+
std::vector<std::optional<uint64_t>> &obj_lens);
6769

6870
private:
6971
rocksdb::Status write(Slice ns_key, JsonMetadata *metadata, const JsonValue &json_val);

tests/gocase/unit/type/json/json_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,4 +554,31 @@ func TestJson(t *testing.T) {
554554
rdb.Do(ctx, "JSON.GET", "nested_arr_big_num").Val())
555555
})
556556

557+
t.Run("JSON.OBJLEN basics", func(t *testing.T) {
558+
require.NoError(t, rdb.Do(ctx, "JSON.SET", "a", "$", `{"x":[3], "nested": {"x": {"y":2, "z": 1}}}`).Err())
559+
vals, err := rdb.Do(ctx, "JSON.OBJLEN", "a", "$..x").Slice()
560+
require.NoError(t, err)
561+
require.EqualValues(t, 2, len(vals))
562+
// the first `x` path is not an object, so it should return nil
563+
require.EqualValues(t, nil, vals[0])
564+
// the second `x` path is an object with two fields, so it should return 2
565+
require.EqualValues(t, 2, vals[1])
566+
567+
vals, err = rdb.Do(ctx, "JSON.OBJLEN", "a", "$.nested").Slice()
568+
require.NoError(t, err)
569+
require.EqualValues(t, 1, len(vals))
570+
require.EqualValues(t, 1, vals[0])
571+
572+
vals, err = rdb.Do(ctx, "JSON.OBJLEN", "a", "$").Slice()
573+
require.NoError(t, err)
574+
require.EqualValues(t, 1, len(vals))
575+
require.EqualValues(t, 2, vals[0])
576+
577+
vals, err = rdb.Do(ctx, "JSON.OBJLEN", "a", "$.no_exists_path").Slice()
578+
require.NoError(t, err)
579+
require.EqualValues(t, 0, len(vals))
580+
581+
err = rdb.Do(ctx, "JSON.OBJLEN", "no-such-json-key", "$").Err()
582+
require.EqualError(t, err, redis.Nil.Error())
583+
})
557584
}

0 commit comments

Comments
 (0)