Signed-off-by: iko1 <me@remotecpp.dev>
This commit is contained in:
parent
b616b1e1fd
commit
bfc073b59d
|
@ -518,8 +518,119 @@ OpResult<vector<StringVec>> OpObjKeys(const OpArgs& op_args, string_view key,
|
|||
return vec;
|
||||
}
|
||||
|
||||
// Retruns array of string lengths after a successful operation.
|
||||
OpResult<vector<OptSizeT>> OpStrAppend(const OpArgs& op_args, string_view key, string_view path,
|
||||
const vector<string_view>& strs) {
|
||||
OpResult<json> result = GetJson(op_args, key);
|
||||
if (!result) {
|
||||
return result.status();
|
||||
}
|
||||
|
||||
vector<OptSizeT> vec;
|
||||
auto cb = [&](const string& path, json& val) {
|
||||
if (val.is_string()) {
|
||||
string new_val = val.as_string();
|
||||
for (auto& str : strs) {
|
||||
new_val += str;
|
||||
}
|
||||
|
||||
val = new_val;
|
||||
vec.emplace_back(new_val.size());
|
||||
} else {
|
||||
vec.emplace_back(nullopt);
|
||||
}
|
||||
};
|
||||
|
||||
json j = result.value();
|
||||
error_code ec = JsonReplace(j, path, cb);
|
||||
if (ec) {
|
||||
VLOG(1) << "Failed to evaluate expression on json with error: " << ec.message();
|
||||
return OpStatus::SYNTAX_ERR;
|
||||
}
|
||||
|
||||
SetString(op_args, key, j.as_string());
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Returns the numbers of values cleared.
|
||||
// Clears containers(arrays or objects) and zeroing numbers.
|
||||
OpResult<long> OpClear(const OpArgs& op_args, string_view key, string_view path) {
|
||||
OpResult<json> result = GetJson(op_args, key);
|
||||
if (!result) {
|
||||
return result.status();
|
||||
}
|
||||
|
||||
long clear_items = 0;
|
||||
auto cb = [&clear_items](const string& path, json& val) {
|
||||
if (!(val.is_object() || val.is_array() || val.is_number())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (val.is_object()) {
|
||||
val.erase(val.object_range().begin(), val.object_range().end());
|
||||
} else if (val.is_array()) {
|
||||
val.erase(val.array_range().begin(), val.array_range().end());
|
||||
} else if (val.is_number()) {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
clear_items += 1;
|
||||
};
|
||||
|
||||
json j = result.value();
|
||||
error_code ec = JsonReplace(j, path, cb);
|
||||
if (ec) {
|
||||
VLOG(1) << "Failed to evaluate expression on json with error: " << ec.message();
|
||||
return OpStatus::SYNTAX_ERR;
|
||||
}
|
||||
|
||||
SetString(op_args, key, j.as_string());
|
||||
return clear_items;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void JsonFamily::Clear(CmdArgList args, ConnectionContext* cntx) {
|
||||
string_view key = ArgS(args, 1);
|
||||
string_view path = ArgS(args, 2);
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpClear(t->GetOpArgs(shard), key, path);
|
||||
};
|
||||
|
||||
Transaction* trans = cntx->transaction;
|
||||
OpResult<long> result = trans->ScheduleSingleHopT(move(cb));
|
||||
|
||||
if (result) {
|
||||
(*cntx)->SendLong(*result);
|
||||
} else {
|
||||
(*cntx)->SendError(result.status());
|
||||
}
|
||||
}
|
||||
|
||||
void JsonFamily::StrAppend(CmdArgList args, ConnectionContext* cntx) {
|
||||
string_view key = ArgS(args, 1);
|
||||
string_view path = ArgS(args, 2);
|
||||
|
||||
vector<string_view> strs;
|
||||
for (size_t i = 3; i < args.size(); ++i) {
|
||||
strs.emplace_back(ArgS(args, i));
|
||||
}
|
||||
|
||||
auto cb = [&](Transaction* t, EngineShard* shard) {
|
||||
return OpStrAppend(t->GetOpArgs(shard), key, path, strs);
|
||||
};
|
||||
|
||||
Transaction* trans = cntx->transaction;
|
||||
OpResult<vector<OptSizeT>> result = trans->ScheduleSingleHopT(move(cb));
|
||||
|
||||
if (result) {
|
||||
PrintOptVec(cntx, result);
|
||||
} else {
|
||||
(*cntx)->SendError(result.status());
|
||||
}
|
||||
}
|
||||
|
||||
void JsonFamily::ObjKeys(CmdArgList args, ConnectionContext* cntx) {
|
||||
string_view key = ArgS(args, 1);
|
||||
string_view path = ArgS(args, 2);
|
||||
|
@ -803,7 +914,11 @@ void JsonFamily::Register(CommandRegistry* registry) {
|
|||
*registry << CI{"JSON.NUMMULTBY", CO::WRITE | CO::DENYOOM | CO::FAST, 4, 1, 1, 1}.HFUNC(
|
||||
NumMultBy);
|
||||
*registry << CI{"JSON.DEL", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del);
|
||||
*registry << CI{"JSON.FORGET", CO::WRITE, -2, 1, 1, 1}.HFUNC(Del); // An alias of JSON.DEL.
|
||||
*registry << CI{"JSON.OBJKEYS", CO::READONLY | CO::FAST, 3, 1, 1, 1}.HFUNC(ObjKeys);
|
||||
*registry << CI{"JSON.STRAPPEND", CO::WRITE | CO::DENYOOM | CO::FAST, -4, 1, 1, 1}.HFUNC(
|
||||
StrAppend);
|
||||
*registry << CI{"JSON.CLEAR", CO::WRITE | CO::DENYOOM | CO::FAST, 3, 1, 1, 1}.HFUNC(Clear);
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -30,6 +30,8 @@ class JsonFamily {
|
|||
static void NumMultBy(CmdArgList args, ConnectionContext* cntx);
|
||||
static void Del(CmdArgList args, ConnectionContext* cntx);
|
||||
static void ObjKeys(CmdArgList args, ConnectionContext* cntx);
|
||||
static void StrAppend(CmdArgList args, ConnectionContext* cntx);
|
||||
static void Clear(CmdArgList args, ConnectionContext* cntx);
|
||||
};
|
||||
|
||||
} // namespace dfly
|
||||
|
|
|
@ -571,4 +571,119 @@ TEST_F(JsonFamilyTest, ObjKeys) {
|
|||
EXPECT_THAT(arr2[4], ArgType(RespExpr::NIL_ARRAY));
|
||||
}
|
||||
|
||||
TEST_F(JsonFamilyTest, StrAppend) {
|
||||
string json = R"(
|
||||
{"a":{"a":"a"}, "b":{"a":"a", "b":1}, "c":{"a":"a", "b":"bb"}, "d":{"a":1, "b":"b", "c":3}}
|
||||
)";
|
||||
|
||||
auto resp = Run({"set", "json", json});
|
||||
ASSERT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.a.a", "a", "b"});
|
||||
EXPECT_THAT(resp, IntArg(3));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aab"},"b":{"a":"a","b":1},"c":{"a":"a","b":"bb"},"d":{"a":1,"b":"b","c":3}})");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.a.*", "a"});
|
||||
EXPECT_THAT(resp, IntArg(4));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aaba"},"b":{"a":"a","b":1},"c":{"a":"a","b":"bb"},"d":{"a":1,"b":"b","c":3}})");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.b.*", "a"});
|
||||
ASSERT_EQ(RespExpr::ARRAY, resp.type);
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre(IntArg(2), ArgType(RespExpr::NIL)));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aaba"},"b":{"a":"aa","b":1},"c":{"a":"a","b":"bb"},"d":{"a":1,"b":"b","c":3}})");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.c.*", "a"});
|
||||
ASSERT_EQ(RespExpr::ARRAY, resp.type);
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre(IntArg(2), IntArg(3)));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aaba"},"b":{"a":"aa","b":1},"c":{"a":"aa","b":"bba"},"d":{"a":1,"b":"b","c":3}})");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.c.b", "a"});
|
||||
EXPECT_THAT(resp, IntArg(4));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aaba"},"b":{"a":"aa","b":1},"c":{"a":"aa","b":"bbaa"},"d":{"a":1,"b":"b","c":3}})");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$.d.*", "a"});
|
||||
ASSERT_EQ(RespExpr::ARRAY, resp.type);
|
||||
EXPECT_THAT(resp.GetVec(),
|
||||
ElementsAre(ArgType(RespExpr::NIL), IntArg(2), ArgType(RespExpr::NIL)));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(
|
||||
resp,
|
||||
R"({"a":{"a":"aaba"},"b":{"a":"aa","b":1},"c":{"a":"aa","b":"bbaa"},"d":{"a":1,"b":"ba","c":3}})");
|
||||
|
||||
json = R"(
|
||||
{"a":"foo", "inner": {"a": "bye"}, "inner1": {"a": 7}}
|
||||
)";
|
||||
|
||||
resp = Run({"set", "json", json});
|
||||
ASSERT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"JSON.STRAPPEND", "json", "$..a", "bar"});
|
||||
ASSERT_EQ(RespExpr::ARRAY, resp.type);
|
||||
EXPECT_THAT(resp.GetVec(), ElementsAre(IntArg(6), IntArg(6), ArgType(RespExpr::NIL)));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(resp, R"({"a":"foobar","inner":{"a":"byebar"},"inner1":{"a":7}})");
|
||||
}
|
||||
|
||||
TEST_F(JsonFamilyTest, Clear) {
|
||||
string json = R"(
|
||||
[[], [0], [0,1], [0,1,2], 1, true, null, "d"]
|
||||
)";
|
||||
|
||||
auto resp = Run({"set", "json", json});
|
||||
ASSERT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"JSON.CLEAR", "json", "$[*]"});
|
||||
EXPECT_THAT(resp, IntArg(5));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(resp, R"([[],[],[],[],0,true,null,"d"])");
|
||||
|
||||
resp = Run({"JSON.CLEAR", "json", "$"});
|
||||
EXPECT_THAT(resp, IntArg(1));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(resp, R"([])");
|
||||
|
||||
json = R"(
|
||||
{"children": ["Yossi", "Rafi", "Benni", "Avraham", "Yehoshua", "Moshe"]}
|
||||
)";
|
||||
|
||||
resp = Run({"set", "json", json});
|
||||
ASSERT_THAT(resp, "OK");
|
||||
|
||||
resp = Run({"JSON.CLEAR", "json", "$.children"});
|
||||
EXPECT_THAT(resp, IntArg(1));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(resp, R"({"children":[]})");
|
||||
|
||||
resp = Run({"JSON.CLEAR", "json", "$"});
|
||||
EXPECT_THAT(resp, IntArg(1));
|
||||
|
||||
resp = Run({"GET", "json"});
|
||||
EXPECT_EQ(resp, R"({})");
|
||||
}
|
||||
|
||||
} // namespace dfly
|
||||
|
|
Loading…
Reference in New Issue