11 #include "detail/config.hpp"
13 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
14 #include <boost/algorithm/string.hpp>
15 #include <boost/range/algorithm.hpp>
16 #include <boost/type_traits/has_operator.hpp>
17 JBSON_CLANG_POP_WARNINGS
19 #include "document.hpp"
20 #include "expression_parser.hpp"
26 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
27 void select(ElemRangeT&& doc, StrRngT path, OutIterator out);
29 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
30 void select_name(ElemRangeT&& doc, StrRngT path, StrRngT name, OutIterator out) {
34 using namespace boost;
36 if(range::equal(name, as_literal(
"*")) || range::equal(name, as_literal(
".."))) {
40 select(get<element_type::document_element>(e), path, out);
42 select(get<element_type::array_element>(e), path, out);
45 out = range::copy(doc, out);
48 auto doc_it = range::find_if(doc, [&](
auto&& e) {
return range::equal(name, e.name()); });
50 if(doc_it != doc.end()) {
53 select(get<element_type::document_element>(*doc_it), path, out);
55 select(get<element_type::array_element>(*doc_it), path, out);
62 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
63 void select_expr(ElemRangeT&& doc, StrRngT path, StrRngT expr, OutIterator out);
65 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
66 void select_sub(ElemRangeT&& doc, StrRngT path, StrRngT subscript, OutIterator out) {
67 using namespace boost;
69 path.drop_front(subscript.size() + 2);
70 if(!subscript.empty() && subscript.back() ==
']')
71 BOOST_THROW_EXCEPTION(jbson_error());
73 std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
74 while(!subscript.empty()) {
76 if(subscript.front() ==
'"') {
77 subscript.drop_front();
78 elem_name = range::find<return_begin_found>(subscript,
'"');
79 subscript.drop_front(elem_name.size() + 1);
80 }
else if(subscript.front() ==
'\'') {
81 subscript.drop_front();
82 elem_name = range::find<return_begin_found>(subscript,
'\'');
83 subscript.drop_front(elem_name.size() + 1);
84 }
else if(::isdigit(subscript.front())) {
85 elem_name = range::find<return_begin_found>(subscript,
',');
86 subscript.drop_front(elem_name.size());
87 }
else if(subscript.front() ==
'*') {
88 elem_name = {subscript.begin(), std::next(subscript.begin())};
89 subscript.drop_front();
90 }
else if(!range::binary_search(
"(?", subscript.front()))
91 BOOST_THROW_EXCEPTION(jbson_error());
93 if(!subscript.empty() && range::binary_search(
"(?", subscript.front())) {
94 elem_name = range::find_if<return_begin_next>(subscript, boost::is_any_of(
",]"));
95 subscript.drop_front(elem_name.size());
96 select_expr(std::forward<ElemRangeT>(doc), path, elem_name, out);
98 select_name(std::forward<ElemRangeT>(doc), path, elem_name, std::back_inserter(vec));
100 if(!subscript.empty() && range::binary_search(
",]", subscript.front()))
101 subscript.drop_front();
103 out = range::unique_copy(vec, out);
106 namespace expression {
131 template <
typename OutIterator>
bool compile_expr(ast::nil, OutIterator) {
136 template <
typename OutIterator>
bool compile_expr(int64_t x, OutIterator out) {
142 template <
typename OutIterator>
bool compile_expr(
bool x, OutIterator out) {
143 *out++ = x ? op_true : op_false;
147 template <
typename OutIterator>
bool compile_expr(ast::variable
const& x, OutIterator out) {
149 out = boost::range::copy(x.name, out);
154 template <
typename OutIterator>
bool compile_expr(std::string
const& x, OutIterator out) {
156 out = boost::range::copy(x, out);
162 using result_type = bool;
163 template <
typename... Args>
auto operator()(Args&&... args)
const;
166 template <
typename OutIterator>
bool compile_expr(ast::operation
const& x, OutIterator out) {
167 using namespace std::placeholders;
168 auto f = std::bind(Visitor(), _1, out);
169 if(!x.operand_.apply_visitor(f))
171 switch(x.operator_) {
172 case ast::optoken::plus:
175 case ast::optoken::minus:
178 case ast::optoken::times:
181 case ast::optoken::divide:
184 case ast::optoken::equal:
187 case ast::optoken::not_equal:
190 case ast::optoken::less:
193 case ast::optoken::less_equal:
196 case ast::optoken::greater:
199 case ast::optoken::greater_equal:
202 case ast::optoken::op_and:
205 case ast::optoken::op_or:
215 template <
typename OutIterator>
bool compile_expr(ast::unary
const& x, OutIterator out) {
216 using namespace std::placeholders;
217 auto f = std::bind(Visitor(), _1, out);
218 if(!boost::apply_visitor(f, x.operand_))
220 switch(x.operator_) {
221 case ast::optoken::negative:
224 case ast::optoken::op_not:
227 case ast::optoken::positive:
237 template <
typename OutIterator>
bool compile_expr(ast::expression
const& x, OutIterator out) {
238 using namespace std::placeholders;
239 auto f = std::bind(Visitor(), _1, out);
240 if(!boost::apply_visitor(f, x.first))
242 for(
const auto& oper : x.rest) {
243 if(!compile_expr(oper, out))
249 template <
typename... Args>
auto Visitor::operator()(Args&&... args)
const {
250 return compile_expr(std::forward<Args>(args)...);
253 template <
typename Derived>
struct CompareVariable_impl : boost::static_visitor<bool> {
254 template <
typename C,
typename U>
255 bool operator()(
const basic_element<C>& e,
const U& v, std::enable_if_t<!is_element<U>::value>* =
nullptr)
const {
258 return (*static_cast<const Derived*>(
this))(e.template value<bool>(), v);
260 return (*static_cast<const Derived*>(
this))(e.template value<int32_t>(), v);
262 return (*static_cast<const Derived*>(
this))(e.template value<int64_t>(), v);
264 return (*static_cast<const Derived*>(
this))(get<element_type::string_element>(e), v);
271 template <
typename U,
typename C>
272 bool operator()(
const U& v,
const basic_element<C>& e, std::enable_if_t<!is_element<U>::value>* =
nullptr)
const {
273 return (*static_cast<const Derived*>(
this))(e, v);
277 template <byte_code op>
struct CompareVariable;
279 template <>
struct CompareVariable<op_eq> : CompareVariable_impl<CompareVariable<op_eq>> {
280 using CompareVariable_impl<CompareVariable<op_eq>>::operator();
281 template <
typename T,
typename U>
282 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_equal_to<T, U>::value>* =
nullptr)
const {
285 template <
typename T,
typename U>
286 bool operator()(
const T& lhs,
const U& rhs, std::enable_if_t<boost::has_equal_to<T, U>::value>* =
nullptr)
const {
291 template <>
struct CompareVariable<op_neq> : CompareVariable_impl<CompareVariable<op_neq>> {
292 using CompareVariable_impl<CompareVariable<op_neq>>::operator();
293 template <
typename T,
typename U>
294 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_equal_to<T, U>::value>* =
nullptr)
const {
297 template <
typename T,
typename U>
298 bool operator()(
const T& lhs,
const U& rhs, std::enable_if_t<boost::has_equal_to<T, U>::value>* =
nullptr)
const {
299 return !(lhs == rhs);
303 template <>
struct CompareVariable<op_lt> : CompareVariable_impl<CompareVariable<op_lt>> {
304 using CompareVariable_impl<CompareVariable<op_lt>>::operator();
305 template <
typename T,
typename U>
306 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_less<T, U>::value>* =
nullptr)
const {
309 template <
typename T,
typename U>
310 bool operator()(
const T& lhs,
const U& rhs, std::enable_if_t<boost::has_less<T, U>::value>* =
nullptr)
const {
315 template <>
struct CompareVariable<op_lte> : CompareVariable_impl<CompareVariable<op_lte>> {
316 using CompareVariable_impl<CompareVariable<op_lte>>::operator();
317 template <
typename T,
typename U>
318 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_less_equal<T, U>::value>* =
nullptr)
const {
321 template <
typename T,
typename U>
322 bool operator()(
const T& lhs,
const U& rhs, std::enable_if_t<boost::has_less_equal<T, U>::value>* =
nullptr)
const {
327 template <>
struct CompareVariable<op_gt> : CompareVariable_impl<CompareVariable<op_gt>> {
328 using CompareVariable_impl<CompareVariable<op_gt>>::operator();
329 template <
typename T,
typename U>
330 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_greater<T, U>::value>* =
nullptr)
const {
333 template <
typename T,
typename U>
334 bool operator()(
const T& lhs,
const U& rhs, std::enable_if_t<boost::has_greater<T, U>::value>* =
nullptr)
const {
339 template <>
struct CompareVariable<op_gte> : CompareVariable_impl<CompareVariable<op_gte>> {
340 using CompareVariable_impl<CompareVariable<op_gte>>::operator();
341 template <
typename T,
typename U>
342 bool operator()(
const T&,
const U&, std::enable_if_t<!boost::has_greater_equal<T, U>::value>* =
nullptr)
const {
345 template <
typename T,
typename U>
346 bool operator()(
const T& lhs,
const U& rhs,
347 std::enable_if_t<boost::has_greater_equal<T, U>::value>* =
nullptr)
const {
352 template <
typename ElemRangeT>
auto eval_expr(ElemRangeT&& doc,
const std::vector<int>& code) {
353 using variable_type = boost::variant<bool, int64_t, std::string, typename std::decay_t<ElemRangeT>::value_type>;
354 std::array<variable_type, 32> stack;
355 auto stack_ptr = stack.begin();
356 auto pc = code.begin();
358 while(pc != code.end()) {
359 BOOST_ASSERT(pc != code.end());
361 using namespace expression;
363 assert(stack_ptr != stack.begin());
364 assert(stack_ptr[-1].which() == 1);
365 stack_ptr[-1] = -boost::get<int64_t>(stack_ptr[-1]);
368 assert(stack_ptr != stack.begin());
369 assert(stack_ptr[-1].which() == 1);
370 stack_ptr[-1] = +boost::get<int64_t>(stack_ptr[-1]);
373 assert(stack_ptr != stack.begin());
374 assert(stack_ptr[-1].which() == 0);
375 stack_ptr[-1] = !bool(boost::get<bool>(stack_ptr[-1]));
379 assert(stack_ptr != stack.begin());
380 assert(stack_ptr != stack.end());
381 assert(stack_ptr[-1].which() == stack_ptr[0].which());
382 assert(stack_ptr[-1].which() == 1);
383 stack_ptr[-1] = boost::get<int64_t>(stack_ptr[-1]) + boost::get<int64_t>(stack_ptr[0]);
387 assert(stack_ptr != stack.begin());
388 assert(stack_ptr != stack.end());
389 assert(stack_ptr[-1].which() == stack_ptr[0].which());
390 assert(stack_ptr[-1].which() == 1);
391 stack_ptr[-1] = boost::get<int64_t>(stack_ptr[-1]) - boost::get<int64_t>(stack_ptr[0]);
395 assert(stack_ptr != stack.begin());
396 assert(stack_ptr != stack.end());
397 assert(stack_ptr[-1].which() == stack_ptr[0].which());
398 assert(stack_ptr[-1].which() == 1);
399 stack_ptr[-1] = boost::get<int64_t>(stack_ptr[-1]) * boost::get<int64_t>(stack_ptr[0]);
403 assert(stack_ptr != stack.begin());
404 assert(stack_ptr != stack.end());
405 assert(stack_ptr[-1].which() == stack_ptr[0].which());
406 assert(stack_ptr[-1].which() == 1);
407 stack_ptr[-1] = boost::get<int64_t>(stack_ptr[-1]) / boost::get<int64_t>(stack_ptr[0]);
411 assert(stack_ptr != stack.begin());
412 assert(stack_ptr != stack.end());
413 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_eq>{}, stack_ptr[-1], stack_ptr[0]);
417 assert(stack_ptr != stack.begin());
418 assert(stack_ptr != stack.end());
419 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_neq>{}, stack_ptr[-1], stack_ptr[0]);
423 assert(stack_ptr != stack.begin());
424 assert(stack_ptr != stack.end());
425 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_lt>{}, stack_ptr[-1], stack_ptr[0]);
429 assert(stack_ptr != stack.begin());
430 assert(stack_ptr != stack.end());
431 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_lte>{}, stack_ptr[-1], stack_ptr[0]);
435 assert(stack_ptr != stack.begin());
436 assert(stack_ptr != stack.end());
437 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_gt>{}, stack_ptr[-1], stack_ptr[0]);
441 assert(stack_ptr != stack.begin());
442 assert(stack_ptr != stack.end());
443 stack_ptr[-1] = boost::apply_visitor(CompareVariable<op_gte>{}, stack_ptr[-1], stack_ptr[0]);
447 assert(stack_ptr != stack.begin());
448 assert(stack_ptr != stack.end());
449 assert(stack_ptr[-1].which() == stack_ptr[0].which());
450 assert(stack_ptr[-1].which() == 0);
451 stack_ptr[-1] = boost::get<bool>(stack_ptr[-1]) && boost::get<bool>(stack_ptr[0]);
455 assert(stack_ptr != stack.begin());
456 assert(stack_ptr != stack.end());
457 assert(stack_ptr[-1].which() == stack_ptr[0].which());
458 assert(stack_ptr[-1].which() == 0);
459 stack_ptr[-1] = boost::get<bool>(stack_ptr[-1]) || boost::get<bool>(stack_ptr[0]);
463 for(; pc != code.end() && *pc != 0; ++pc)
467 std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
468 select(doc, boost::make_iterator_range(str), std::back_inserter(vec));
470 stack_ptr = boost::range::copy(vec, stack_ptr);
472 return variable_type{
false};
476 stack[*pc++] = stack_ptr[0];
479 *stack_ptr++ = int64_t(*pc++);
483 for(; pc != code.end() && *pc != 0; ++pc)
494 *stack_ptr++ =
false;
498 return stack_ptr[-1];
503 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
504 void select_expr(ElemRangeT&& doc, StrRngT path, StrRngT expr, OutIterator out) {
505 using namespace boost;
510 if(expr.back() !=
')')
514 std::vector<int> code;
516 if(starts_with(expr, as_literal(
"?("))) {
522 expression::error_handler<typename std::decay_t<StrRngT>::const_iterator> err_h{expr.begin(), expr.end()};
523 expression::parser<typename std::decay_t<StrRngT>::const_iterator> expr_parser{err_h};
524 expression::ast::expression ast;
526 boost::spirit::ascii::space_type space;
527 auto r = boost::spirit::qi::phrase_parse(expr.begin(), expr.end(), expr_parser, space, ast);
529 r = expression::compile_expr(ast, std::back_inserter(code));
532 using variable_type = decltype(expression::eval_expr(std::forward<ElemRangeT>(doc), code));
534 std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
536 v = expression::eval_expr(std::forward<ElemRangeT>(doc), code);
538 for(
auto&& e : doc) {
541 v = expression::eval_expr(get<element_type::document_element>(e), code);
543 v = expression::eval_expr(get<element_type::array_element>(e), code);
550 if(e.name() == std::to_string(get<int64_t>(v)))
554 if(e.name() == get<std::string>(v))
564 out = boost::copy(vec, out);
568 for(
auto&& e : vec) {
570 select(get<element_type::document_element>(e), path, out);
572 select(get<element_type::array_element>(e), path, out);
576 template <
typename ElemRangeT,
typename StrRngT,
typename OutIterator>
577 void select(ElemRangeT&& doc, StrRngT path, OutIterator out) {
580 static_assert(is_iterator_range<std::decay_t<StrRngT>>::value,
"");
581 using namespace boost;
584 out = range::copy(doc, out);
588 if(path.front() ==
'@')
592 if(!starts_with(path, as_literal(
"..")))
593 path = range::find_if<boost::return_found_end>(path, [](
auto c) {
return c !=
'.'; });
595 if(path.front() ==
'[') {
596 elem_name = range::find<return_begin_found>(path,
']');
597 elem_name.drop_front();
598 select_sub(std::forward<ElemRangeT>(doc), path, elem_name, out);
600 if(starts_with(path, as_literal(
".."))) {
601 elem_name = {path.begin(), std::next(path.begin(), 2)};
602 select_name(std::forward<ElemRangeT>(doc), path, elem_name, out);
605 elem_name = range::find_if<return_begin_found>(path, is_any_of(
".["));
606 path.drop_front(elem_name.size());
607 if(!path.empty() && path.front() ==
'.' && !starts_with(path, as_literal(
"..")))
609 select_name(std::forward<ElemRangeT>(doc), path, elem_name, out);
615 template <
typename Container,
typename EContainer,
typename StrRngT>
616 auto path_select(basic_document<Container, EContainer>&& doc, StrRngT&& path_rng) {
617 auto path = boost::as_literal(path_rng);
618 std::vector<basic_element<Container>> vec;
620 if(path.empty() || (path.size() == 1 && path.front() ==
'$')) {
621 boost::range::push_back(vec, doc);
623 return std::move(vec);
626 path = boost::range::find_if<boost::return_found_end>(path, [](
auto c) {
return c !=
'$'; });
628 detail::select(doc, path, std::back_inserter(vec));
630 return std::move(vec);
633 template <
typename ElemRangeT,
typename StrRngT>
635 ElemRangeT&& doc, StrRngT&& path_rng,
637 static_assert(std::is_lvalue_reference<ElemRangeT>::value,
"");
638 auto path = boost::as_literal(path_rng);
639 std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
641 if(path.empty() || (path.size() == 1 && path.front() ==
'$')) {
642 boost::range::push_back(vec, doc);
644 return std::move(vec);
647 path = boost::range::find_if<boost::return_found_end>(path, [](
auto c) {
return c !=
'$'; });
649 detail::select(doc, path, std::back_inserter(vec));
651 return std::move(vec);
654 template <
typename DocRangeT,
typename StrRngT>
655 auto path_select(DocRangeT&& docs, StrRngT&& path_rng,
657 DocRangeT, boost::mpl::bind<boost::mpl::quote2<detail::is_range_of_value>, boost::mpl::arg<1>,
658 boost::mpl::quote1<detail::is_element>>>::value>* =
nullptr) {
659 auto path = boost::as_literal(path_rng);
660 std::vector<typename std::decay_t<DocRangeT>::value_type> vec;
662 for(
auto&& doc : docs) {
663 if(path.empty() || (path.size() == 1 && path.front() ==
'$')) {
664 boost::range::push_back(vec, doc);
665 return std::move(vec);
668 path = boost::range::find_if<boost::return_found_end>(path, [](
auto c) {
return c !=
'$'; });
669 detail::select(doc, path, std::back_inserter(vec));
672 return std::move(vec);
677 #endif // JBSON_PATH_HPP
std::string or boost::string_ref (string_type)
is_range_of< RangeT, ElementTrait, mpl::quote1< boost::range_value >> is_range_of_value
Type trait to apply a unary metafunction trait to the value_type of a Range.
basic_document> (document_type)