Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion src/core/json/detail/flat_dfs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,105 @@ auto FlatDfsItem::Init(const PathSegment& segment) -> AdvanceResult {
}

auto FlatDfsItem::Advance(const PathSegment& segment) -> AdvanceResult {
return AdvanceResult{};
if (state_ == kInit) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code here and below is copied from jsoncons_dfs.

return Init(segment);
}

if (!ShouldIterateAll(segment.type()))
return Exhausted();

++state_;
auto vec = obj().AsVector();
if (state_ >= vec.size())
return Exhausted();
return Next(vec[state_]);
}

FlatDfs FlatDfs::Traverse(absl::Span<const PathSegment> path, const flexbuffers::Reference root,
const PathFlatCallback& callback) {
DCHECK(!path.empty());
FlatDfs dfs;

if (path.size() == 1) {
dfs.PerformStep(path[0], root, callback);
return dfs;
}

using ConstItem = FlatDfsItem;
vector<ConstItem> stack;
stack.emplace_back(root);

do {
unsigned segment_index = stack.back().segment_idx();
const auto& path_segment = path[segment_index];

// init or advance the current object
ConstItem::AdvanceResult res = stack.back().Advance(path_segment);
if (res && !res->first.IsNull()) {
const flexbuffers::Reference next = res->first;
DVLOG(2) << "Handling now " << next.GetType() << " " << next.ToString();

// We descent only if next is object or an array.
if (IsRecursive(next.GetType())) {
unsigned next_seg_id = res->second;

if (next_seg_id + 1 < path.size()) {
stack.emplace_back(next, next_seg_id);
} else {
// terminal step
// TODO: to take into account MatchStatus
// for `json.set foo $.a[10]` or for `json.set foo $.*.b`
dfs.PerformStep(path[next_seg_id], next, callback);
}
}
} else {
stack.pop_back();
}
} while (!stack.empty());

return dfs;
}

auto FlatDfs::PerformStep(const PathSegment& segment, const flexbuffers::Reference node,
const PathFlatCallback& callback) -> nonstd::expected<void, MatchStatus> {
switch (segment.type()) {
case SegmentType::IDENTIFIER: {
if (!node.IsMap())
return make_unexpected(MISMATCH);
auto map = node.AsMap();
flexbuffers::Reference value = map[segment.identifier().c_str()];
if (!value.IsNull()) {
DoCall(callback, string_view{segment.identifier()}, value);
}
} break;
case SegmentType::INDEX: {
if (!node.IsVector())
return make_unexpected(MISMATCH);
auto vec = node.AsVector();
if (segment.index() >= vec.size()) {
return make_unexpected(OUT_OF_BOUNDS);
}
DoCall(callback, nullopt, vec[segment.index()]);
} break;

case SegmentType::DESCENT:
case SegmentType::WILDCARD: {
auto vec = node.AsVector(); // always succeeds
auto keys = node.AsMap().Keys(); // always succeeds
string str;
for (size_t i = 0; i < vec.size(); ++i) {
flexbuffers::Reference key = keys[i];
optional<string_view> opt_key;
if (key.IsString()) {
str = key.ToString();
opt_key = str;
}
DoCall(callback, opt_key, vec[i]);
}
} break;
default:
LOG(DFATAL) << "Unknown segment " << SegmentName(segment.type());
}
return {};
}

Expand Down
71 changes: 61 additions & 10 deletions src/core/json/driver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@ namespace dfly::json {
namespace {

class SingleValueImpl : public AggFunction {
JsonType GetResultImpl() const final {
return visit(Overloaded{
[](monostate) { return JsonType::null(); },
[](double d) { return JsonType(d); },
[](int64_t i) { return JsonType(i); },
},
val_);
Result GetResultImpl() const final {
return val_;
}

protected:
Expand All @@ -35,7 +30,15 @@ class SingleValueImpl : public AggFunction {
}
}

variant<monostate, double, int64_t> val_;
void Init(const flexbuffers::Reference src) {
if (src.IsFloat()) {
val_.emplace<double>(src.AsDouble());
} else {
val_.emplace<int64_t>(src.AsInt64());
}
}

Result val_;
};

class MaxImpl : public SingleValueImpl {
Expand All @@ -58,6 +61,25 @@ class MaxImpl : public SingleValueImpl {

return true;
}

bool ApplyImpl(flexbuffers::Reference src) final {
if (!src.IsNumeric()) {
return false;
}

visit(Overloaded{
[&](monostate) { Init(src); },
[&](double d) { val_ = max(d, src.AsDouble()); },
[&](int64_t i) {
if (src.IsFloat())
val_ = max(double(i), src.AsDouble());
else
val_ = max(i, src.AsInt64());
},
},
val_);
return true;
}
};

class MinImpl : public SingleValueImpl {
Expand All @@ -81,6 +103,25 @@ class MinImpl : public SingleValueImpl {

return true;
}

bool ApplyImpl(flexbuffers::Reference src) final {
if (!src.IsNumeric()) {
return false;
}

visit(Overloaded{
[&](monostate) { Init(src); },
[&](double d) { val_ = min(d, src.AsDouble()); },
[&](int64_t i) {
if (src.IsFloat())
val_ = min(double(i), src.AsDouble());
else
val_ = min(i, src.AsInt64());
},
},
val_);
return true;
}
};

class AvgImpl : public AggFunction {
Expand All @@ -95,9 +136,19 @@ class AvgImpl : public AggFunction {
return true;
}

JsonType GetResultImpl() const final {
bool ApplyImpl(flexbuffers::Reference src) final {
if (!src.IsNumeric()) {
return false;
}
sum_ += src.AsDouble();
count_++;

return true;
}

Result GetResultImpl() const final {
DCHECK_GT(count_, 0u); // AggFunction guarantees that
return JsonType(sum_ / count_);
return Result(double(sum_ / count_));
}

double sum_ = 0;
Expand Down
Loading