1
0
Fork 0
mirror of https://github.com/dragonflydb/dragonfly.git synced 2024-12-14 11:58:02 +00:00

fix(json): fix json dot path (#2186)

* fix(json): fix json dot path

Signed-off-by: Vladislav Oleshko <vlad@dragonflydb.io>

---------

Signed-off-by: Vladislav Oleshko <vlad@dragonflydb.io>
This commit is contained in:
Vladislav 2023-11-17 22:32:16 +03:00 committed by GitHub
parent c4cae58fc8
commit 27dcec38ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 67 deletions

View file

@ -19,6 +19,7 @@ extern "C" {
#include "base/logging.h"
#include "core/json_object.h"
#include "facade/cmd_arg_parser.h"
#include "server/acl/acl_commands_def.h"
#include "server/command_registry.h"
#include "server/error.h"
@ -389,9 +390,10 @@ void SendJsonValue(ConnectionContext* cntx, const JsonType& j) {
}
}
OpResult<string> OpGet(const OpArgs& op_args, string_view key,
vector<pair<string_view, JsonExpression>> expressions, bool should_format,
const OptString& indent, const OptString& new_line, const OptString& space) {
OpResult<string> OpJsonGet(const OpArgs& op_args, string_view key,
const vector<pair<string_view, optional<JsonExpression>>>& expressions,
bool should_format, const OptString& indent, const OptString& new_line,
const OptString& space) {
OpResult<JsonType*> result = GetJson(op_args, key);
if (!result) {
return result.status();
@ -427,22 +429,16 @@ OpResult<string> OpGet(const OpArgs& op_args, string_view key,
}
}
if (expressions.size() == 1) {
json out = expressions[0].second.evaluate(json_entry);
if (should_format) {
json_printable jp(out, options, indenting::indent);
std::stringstream ss;
jp.dump(ss);
return ss.str();
}
return out.as<string>();
}
auto eval_wrapped = [&json_entry](const optional<JsonExpression>& expr) {
return expr ? expr->evaluate(json_entry) : json_entry;
};
json out;
for (auto& expr : expressions) {
json eval = expr.second.evaluate(json_entry);
out[expr.first] = eval;
if (expressions.size() == 1) {
out = eval_wrapped(expressions[0].second);
} else {
for (auto& [expr_str, expr] : expressions)
out[expr_str] = eval_wrapped(expr);
}
if (should_format) {
@ -1785,54 +1781,50 @@ void JsonFamily::StrLen(CmdArgList args, ConnectionContext* cntx) {
void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
DCHECK_GE(args.size(), 1U);
string_view key = ArgS(args, 0);
facade::CmdArgParser parser{args};
string_view key = parser.Next();
OptString indent;
OptString new_line;
OptString space;
vector<pair<string_view, JsonExpression>> expressions;
for (size_t i = 1; i < args.size(); ++i) {
string_view param = ArgS(args, i);
if (absl::EqualsIgnoreCase(param, "space")) {
if (++i >= args.size()) {
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
facade::kSyntaxErrType);
} else {
space = ArgS(args, i);
continue;
}
} else if (absl::EqualsIgnoreCase(param, "newline")) {
if (++i >= args.size()) {
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
facade::kSyntaxErrType);
} else {
new_line = ArgS(args, i);
continue;
}
} else if (absl::EqualsIgnoreCase(param, "indent")) {
if (++i >= args.size()) {
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()),
facade::kSyntaxErrType);
} else {
indent = ArgS(args, i);
continue;
vector<pair<string_view, optional<JsonExpression>>> expressions;
while (parser.HasNext()) {
if (parser.Check("SPACE").IgnoreCase().ExpectTail(1)) {
space = parser.Next();
continue;
}
if (parser.Check("NEWLINE").IgnoreCase().ExpectTail(1)) {
new_line = parser.Next();
continue;
}
if (parser.Check("INDENT").IgnoreCase().ExpectTail(1)) {
indent = parser.Next();
continue;
}
optional<JsonExpression> expr;
string_view expr_str = parser.Next();
if (expr_str != ".") {
error_code ec;
expr = ParseJsonPath(expr_str, &ec);
if (ec) {
LOG(WARNING) << "path '" << expr_str << "': Invalid JSONPath syntax: " << ec.message();
return (*cntx)->SendError(kSyntaxErr);
}
}
error_code ec;
JsonExpression expr = ParseJsonPath(param, &ec);
if (ec) {
LOG(WARNING) << "path '" << param << "': Invalid JSONPath syntax: " << ec.message();
return (*cntx)->SendError(kSyntaxErr);
}
expressions.emplace_back(param, move(expr));
expressions.emplace_back(expr_str, move(expr));
}
if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply());
bool should_format = (indent || new_line || space);
auto cb = [&](Transaction* t, EngineShard* shard) {
return OpGet(t->GetOpArgs(shard), key, move(expressions), should_format, indent, new_line,
space);
return OpJsonGet(t->GetOpArgs(shard), key, expressions, should_format, indent, new_line, space);
};
Transaction* trans = cntx->transaction;
@ -1842,8 +1834,7 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
(*cntx)->SendBulkString(*result);
} else {
if (result == facade::OpStatus::KEY_NOTFOUND) {
// Match what Redis returning
(*cntx)->SendNull();
(*cntx)->SendNull(); // Match Redis
} else {
(*cntx)->SendError(result.status());
}

View file

@ -4,6 +4,10 @@
#include "server/json_family.h"
#include <absl/strings/str_replace.h>
#include <jsoncons/json.hpp>
#include "base/gtest.h"
#include "base/logging.h"
#include "facade/facade_test.h"
@ -106,6 +110,14 @@ TEST_F(JsonFamilyTest, SetGetFromPhonebook) {
auto resp = Run({"JSON.SET", "json", ".", PhonebookJson});
ASSERT_THAT(resp, "OK");
auto compact_json = jsoncons::json::parse(PhonebookJson).as_string();
resp = Run({"JSON.GET", "json", "."});
EXPECT_EQ(resp, compact_json);
resp = Run({"JSON.GET", "json", "$"});
EXPECT_EQ(resp, "[" + compact_json + "]");
resp = Run({"JSON.GET", "json", "$.address.*"});
EXPECT_EQ(resp, R"(["New York","NY","21 2nd Street","10021-3100"])");
@ -1038,15 +1050,4 @@ TEST_F(JsonFamilyTest, Set) {
EXPECT_EQ(resp, R"([{"a":2,"b":8,"c":[1,2,3]}])");
}
TEST_F(JsonFamilyTest, LegacyV1) {
string json = R"({"key":[1,2,3,4]})";
auto resp = Run({"JSON.SET", "json1", ".", json});
EXPECT_THAT(resp, "OK");
// JSON.GET key "." is the same as JSON.GET key "$"
resp = Run({"JSON.GET", "json1", "."});
EXPECT_THAT(resp, absl::StrCat("[", json, "]"));
}
} // namespace dfly