mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2024-12-14 11:58:02 +00:00
feat: jsonpath supports index/wildcard expressions (#2578)
Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
parent
21e725774c
commit
4afe278487
5 changed files with 79 additions and 56 deletions
|
@ -20,7 +20,7 @@ Driver::~Driver() {
|
|||
void Driver::SetInput(string str) {
|
||||
cur_str_ = std::move(str);
|
||||
lexer_->in(cur_str_);
|
||||
path_.Clear();
|
||||
path_.clear();
|
||||
}
|
||||
|
||||
void Driver::ResetScanner() {
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "src/core/json/path.h"
|
||||
|
||||
namespace dfly {
|
||||
namespace json {
|
||||
|
@ -14,54 +15,6 @@ namespace json {
|
|||
class Lexer;
|
||||
class location; // from jsonpath_grammar.hh
|
||||
|
||||
enum class SegmentType {
|
||||
IDENTIFIER = 1, // $.identifier
|
||||
INDEX = 2, // $.array[0]
|
||||
WILDCARD = 3, // $.array[*] or $.*
|
||||
};
|
||||
|
||||
class PathSegment {
|
||||
public:
|
||||
PathSegment(SegmentType type, std::string identifier = std::string())
|
||||
: type_(type), identifier_(std::move(identifier)) {
|
||||
}
|
||||
|
||||
SegmentType type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
const std::string& identifier() const {
|
||||
return identifier_;
|
||||
}
|
||||
|
||||
private:
|
||||
SegmentType type_;
|
||||
std::string identifier_;
|
||||
int index_;
|
||||
};
|
||||
|
||||
class Path {
|
||||
public:
|
||||
void AddSegment(PathSegment segment) {
|
||||
segments_.push_back(std::move(segment));
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return segments_.size();
|
||||
}
|
||||
|
||||
const PathSegment& operator[](size_t i) const {
|
||||
return segments_[i];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
segments_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PathSegment> segments_;
|
||||
};
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
Driver();
|
||||
|
@ -76,7 +29,15 @@ class Driver {
|
|||
virtual void Error(const location& l, const std::string& msg) = 0;
|
||||
|
||||
void AddIdentifier(const std::string& identifier) {
|
||||
path_.AddSegment(PathSegment(SegmentType::IDENTIFIER, identifier));
|
||||
AddSegment(PathSegment(SegmentType::IDENTIFIER, identifier));
|
||||
}
|
||||
|
||||
void AddWildcard() {
|
||||
AddSegment(PathSegment(SegmentType::WILDCARD));
|
||||
}
|
||||
|
||||
void AddSegment(PathSegment segment) {
|
||||
path_.push_back(std::move(segment));
|
||||
}
|
||||
|
||||
Path TakePath() {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
// Added to header file before parser declaration.
|
||||
%code requires {
|
||||
#include "src/core/json/path.h"
|
||||
namespace dfly {
|
||||
namespace json {
|
||||
class Driver;
|
||||
|
@ -56,6 +57,8 @@ using namespace std;
|
|||
%token <unsigned> UINT "integer"
|
||||
|
||||
%nterm <std::string> identifier
|
||||
%nterm <PathSegment> bracket_expr index_expr
|
||||
|
||||
|
||||
%%
|
||||
// Based on the following specification:
|
||||
|
@ -67,19 +70,19 @@ opt_relative_location:
|
|||
| relative_location
|
||||
|
||||
relative_location: DOT relative_path
|
||||
| LBRACKET bracket_expr RBRACKET
|
||||
| LBRACKET bracket_expr RBRACKET { driver->AddSegment($2); }
|
||||
|
||||
relative_path: identifier { driver->AddIdentifier($1); } opt_relative_location
|
||||
| WILDCARD opt_relative_location
|
||||
| WILDCARD { driver->AddWildcard(); } opt_relative_location
|
||||
|
||||
|
||||
identifier: UNQ_STR
|
||||
// | single_quoted_string | double_quoted_string
|
||||
|
||||
bracket_expr: WILDCARD
|
||||
| index_expr
|
||||
bracket_expr: WILDCARD { $$ = PathSegment{SegmentType::WILDCARD}; }
|
||||
| index_expr { $$ = $1; }
|
||||
|
||||
index_expr: UINT
|
||||
index_expr: UINT { $$ = PathSegment(SegmentType::INDEX, $1); }
|
||||
|
||||
%%
|
||||
|
||||
|
|
|
@ -119,6 +119,15 @@ TEST_F(JsonPathTest, Parser) {
|
|||
EXPECT_THAT(path[1], SegType(SegmentType::IDENTIFIER));
|
||||
EXPECT_EQ("foo", path[0].identifier());
|
||||
EXPECT_EQ("bar", path[1].identifier());
|
||||
|
||||
EXPECT_EQ(0, Parse("$.*.bar[1]"));
|
||||
path = driver_.TakePath();
|
||||
ASSERT_EQ(3, path.size());
|
||||
EXPECT_THAT(path[0], SegType(SegmentType::WILDCARD));
|
||||
EXPECT_THAT(path[1], SegType(SegmentType::IDENTIFIER));
|
||||
EXPECT_THAT(path[2], SegType(SegmentType::INDEX));
|
||||
EXPECT_EQ("bar", path[1].identifier());
|
||||
EXPECT_EQ(1, path[2].index());
|
||||
}
|
||||
|
||||
} // namespace dfly::json
|
||||
|
|
50
src/core/json/path.h
Normal file
50
src/core/json/path.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2024, DragonflyDB authors. All rights reserved.
|
||||
// See LICENSE for licensing terms.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace dfly::json {
|
||||
|
||||
enum class SegmentType {
|
||||
IDENTIFIER = 1, // $.identifier
|
||||
INDEX = 2, // $.array[0]
|
||||
WILDCARD = 3, // $.array[*] or $.*
|
||||
};
|
||||
|
||||
class PathSegment {
|
||||
public:
|
||||
PathSegment() : PathSegment(SegmentType::IDENTIFIER) {
|
||||
}
|
||||
|
||||
PathSegment(SegmentType type, std::string identifier = std::string())
|
||||
: type_(type), value_(std::move(identifier)) {
|
||||
}
|
||||
|
||||
PathSegment(SegmentType type, unsigned index) : type_(type), value_(index) {
|
||||
}
|
||||
|
||||
SegmentType type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
const std::string& identifier() const {
|
||||
return std::get<std::string>(value_);
|
||||
}
|
||||
|
||||
unsigned index() const {
|
||||
return std::get<unsigned>(value_);
|
||||
}
|
||||
|
||||
private:
|
||||
SegmentType type_;
|
||||
std::variant<std::string, unsigned> value_;
|
||||
};
|
||||
|
||||
using Path = std::vector<PathSegment>;
|
||||
|
||||
} // namespace dfly::json
|
Loading…
Reference in a new issue