View on GitHub
jbson
C++11/1y BSON library
path.hpp
1 // Copyright Christian Manning 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef JBSON_PATH_HPP
7 #define JBSON_PATH_HPP
8 
9 #include <vector>
10 
11 #include "detail/config.hpp"
12 
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
18 
19 #include "document.hpp"
20 #include "expression_parser.hpp"
21 
22 namespace jbson {
23 
24 namespace detail {
25 
26 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
27 void select(ElemRangeT&& doc, StrRngT path, OutIterator out);
28 
29 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
30 void select_name(ElemRangeT&& doc, StrRngT path, StrRngT name, OutIterator out) {
31  if(name.empty())
32  return;
33 
34  using namespace boost;
35 
36  if(range::equal(name, as_literal("*")) || range::equal(name, as_literal(".."))) {
37  if(!path.empty()) { // descend
38  for(auto&& e : doc) {
39  if(e.type() == element_type::document_element)
40  select(get<element_type::document_element>(e), path, out);
41  else if(e.type() == element_type::array_element)
42  select(get<element_type::array_element>(e), path, out);
43  }
44  } else
45  out = range::copy(doc, out);
46  }
47 
48  auto doc_it = range::find_if(doc, [&](auto&& e) { return range::equal(name, e.name()); });
49 
50  if(doc_it != doc.end()) {
51  if(!path.empty()) { // descend
52  if(doc_it->type() == element_type::document_element)
53  select(get<element_type::document_element>(*doc_it), path, out);
54  else if(doc_it->type() == element_type::array_element)
55  select(get<element_type::array_element>(*doc_it), path, out);
56  } else
57  *out++ = *doc_it;
58  } else
59  return;
60 }
61 
62 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
63 void select_expr(ElemRangeT&& doc, StrRngT path, StrRngT expr, OutIterator out);
64 
65 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
66 void select_sub(ElemRangeT&& doc, StrRngT path, StrRngT subscript, OutIterator out) {
67  using namespace boost;
68 
69  path.drop_front(subscript.size() + 2);
70  if(!subscript.empty() && subscript.back() == ']')
71  BOOST_THROW_EXCEPTION(jbson_error());
72 
73  std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
74  while(!subscript.empty()) {
75  StrRngT elem_name;
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());
92 
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);
97  } else
98  select_name(std::forward<ElemRangeT>(doc), path, elem_name, std::back_inserter(vec));
99 
100  if(!subscript.empty() && range::binary_search(",]", subscript.front()))
101  subscript.drop_front();
102  }
103  out = range::unique_copy(vec, out);
104 }
105 
106 namespace expression {
107 enum byte_code {
108  op_neg, // negate the top stack entry
109  op_pos,
110  op_add, // add top two stack entries
111  op_sub, // subtract top two stack entries
112  op_mul, // multiply top two stack entries
113  op_div, // divide top two stack entries
114  op_not, // boolean negate the top stack entry
115  op_eq, // compare the top two stack entries for ==
116  op_neq, // compare the top two stack entries for !=
117  op_lt, // compare the top two stack entries for <
118  op_lte, // compare the top two stack entries for <=
119  op_gt, // compare the top two stack entries for >
120  op_gte, // compare the top two stack entries for >=
121  op_and, // logical and top two stack entries
122  op_or, // logical or top two stack entries
123  op_load, // load a variable
124  op_store, // store a variable
125  op_int, // push constant integer into the stack
126  op_string,
127  op_true, // push constant 0 into the stack
128  op_false, // push constant 1 into the stack
129 };
130 
131 template <typename OutIterator> bool compile_expr(ast::nil, OutIterator) {
132  BOOST_ASSERT(0);
133  return false;
134 }
135 
136 template <typename OutIterator> bool compile_expr(int64_t x, OutIterator out) {
137  *out++ = op_int;
138  *out++ = x;
139  return true;
140 }
141 
142 template <typename OutIterator> bool compile_expr(bool x, OutIterator out) {
143  *out++ = x ? op_true : op_false;
144  return true;
145 }
146 
147 template <typename OutIterator> bool compile_expr(ast::variable const& x, OutIterator out) {
148  *out++ = op_load;
149  out = boost::range::copy(x.name, out);
150  *out++ = 0;
151  return true;
152 }
153 
154 template <typename OutIterator> bool compile_expr(std::string const& x, OutIterator out) {
155  *out++ = op_string;
156  out = boost::range::copy(x, out);
157  *out++ = 0;
158  return true;
159 }
160 
161 struct Visitor {
162  using result_type = bool;
163  template <typename... Args> auto operator()(Args&&... args) const;
164 };
165 
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))
170  return false;
171  switch(x.operator_) {
172  case ast::optoken::plus:
173  *out++ = (op_add);
174  break;
175  case ast::optoken::minus:
176  *out++ = (op_sub);
177  break;
178  case ast::optoken::times:
179  *out++ = (op_mul);
180  break;
181  case ast::optoken::divide:
182  *out++ = (op_div);
183  break;
184  case ast::optoken::equal:
185  *out++ = (op_eq);
186  break;
187  case ast::optoken::not_equal:
188  *out++ = (op_neq);
189  break;
190  case ast::optoken::less:
191  *out++ = (op_lt);
192  break;
193  case ast::optoken::less_equal:
194  *out++ = (op_lte);
195  break;
196  case ast::optoken::greater:
197  *out++ = (op_gt);
198  break;
199  case ast::optoken::greater_equal:
200  *out++ = (op_gte);
201  break;
202  case ast::optoken::op_and:
203  *out++ = (op_and);
204  break;
205  case ast::optoken::op_or:
206  *out++ = (op_or);
207  break;
208  default:
209  BOOST_ASSERT(0);
210  return false;
211  }
212  return true;
213 }
214 
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_))
219  return false;
220  switch(x.operator_) {
221  case ast::optoken::negative:
222  *out++ = (op_neg);
223  break;
224  case ast::optoken::op_not:
225  *out++ = (op_not);
226  break;
227  case ast::optoken::positive:
228  *out++ = (op_pos);
229  break;
230  default:
231  BOOST_ASSERT(0);
232  return false;
233  }
234  return true;
235 }
236 
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))
241  return false;
242  for(const auto& oper : x.rest) {
243  if(!compile_expr(oper, out))
244  return false;
245  }
246  return true;
247 }
248 
249 template <typename... Args> auto Visitor::operator()(Args&&... args) const {
250  return compile_expr(std::forward<Args>(args)...);
251 }
252 
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 {
256  switch(e.type()) {
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);
265  default:
266  break;
267  }
268 
269  return false;
270  }
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);
274  }
275 };
276 
277 template <byte_code op> struct CompareVariable;
278 
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 {
283  return false;
284  }
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 {
287  return lhs == rhs;
288  }
289 };
290 
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 {
295  return false;
296  }
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);
300  }
301 };
302 
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 {
307  return false;
308  }
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 {
311  return lhs < rhs;
312  }
313 };
314 
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 {
319  return false;
320  }
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 {
323  return lhs <= rhs;
324  }
325 };
326 
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 {
331  return false;
332  }
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 {
335  return lhs > rhs;
336  }
337 };
338 
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 {
343  return false;
344  }
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 {
348  return lhs >= rhs;
349  }
350 };
351 
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();
357 
358  while(pc != code.end()) {
359  BOOST_ASSERT(pc != code.end());
360  switch(*pc++) {
361  using namespace expression;
362  case op_neg:
363  assert(stack_ptr != stack.begin());
364  assert(stack_ptr[-1].which() == 1);
365  stack_ptr[-1] = -boost::get<int64_t>(stack_ptr[-1]);
366  break;
367  case op_pos:
368  assert(stack_ptr != stack.begin());
369  assert(stack_ptr[-1].which() == 1);
370  stack_ptr[-1] = +boost::get<int64_t>(stack_ptr[-1]);
371  break;
372  case op_not:
373  assert(stack_ptr != stack.begin());
374  assert(stack_ptr[-1].which() == 0);
375  stack_ptr[-1] = !bool(boost::get<bool>(stack_ptr[-1]));
376  break;
377  case op_add:
378  --stack_ptr;
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]);
384  break;
385  case op_sub:
386  --stack_ptr;
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]);
392  break;
393  case op_mul:
394  --stack_ptr;
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]);
400  break;
401  case op_div:
402  --stack_ptr;
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]);
408  break;
409  case op_eq:
410  --stack_ptr;
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]);
414  break;
415  case op_neq:
416  --stack_ptr;
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]);
420  break;
421  case op_lt:
422  --stack_ptr;
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]);
426  break;
427  case op_lte:
428  --stack_ptr;
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]);
432  break;
433  case op_gt:
434  --stack_ptr;
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]);
438  break;
439  case op_gte:
440  --stack_ptr;
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]);
444  break;
445  case op_and:
446  --stack_ptr;
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]);
452  break;
453  case op_or:
454  --stack_ptr;
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]);
460  break;
461  case op_load: {
462  std::string str;
463  for(; pc != code.end() && *pc != 0; ++pc)
464  str += char(*pc);
465  if(pc != code.end())
466  ++pc;
467  std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
468  select(doc, boost::make_iterator_range(str), std::back_inserter(vec));
469  if(!vec.empty())
470  stack_ptr = boost::range::copy(vec, stack_ptr);
471  else
472  return variable_type{false};
473  } break;
474  case op_store:
475  --stack_ptr;
476  stack[*pc++] = stack_ptr[0];
477  break;
478  case op_int:
479  *stack_ptr++ = int64_t(*pc++);
480  break;
481  case op_string: {
482  std::string str;
483  for(; pc != code.end() && *pc != 0; ++pc)
484  str += char(*pc);
485  if(pc != code.end())
486  ++pc;
487  *stack_ptr++ = str;
488  break;
489  }
490  case op_true:
491  *stack_ptr++ = true;
492  break;
493  case op_false:
494  *stack_ptr++ = false;
495  break;
496  }
497  }
498  return stack_ptr[-1];
499 }
500 
501 } // namespace expresion
502 
503 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
504 void select_expr(ElemRangeT&& doc, StrRngT path, StrRngT expr, OutIterator out) {
505  using namespace boost;
506 
507  if(expr.empty())
508  return;
509 
510  if(expr.back() != ')')
511  return;
512  expr.drop_back();
513 
514  std::vector<int> code;
515  bool filter = false;
516  if(starts_with(expr, as_literal("?("))) {
517  expr.drop_front(2);
518  filter = true;
519  } else
520  expr.drop_front();
521 
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;
525 
526  boost::spirit::ascii::space_type space;
527  auto r = boost::spirit::qi::phrase_parse(expr.begin(), expr.end(), expr_parser, space, ast);
528  assert(r);
529  r = expression::compile_expr(ast, std::back_inserter(code));
530  assert(r);
531 
532  using variable_type = decltype(expression::eval_expr(std::forward<ElemRangeT>(doc), code));
533  variable_type v;
534  std::vector<typename std::decay_t<ElemRangeT>::value_type> vec;
535  if(!filter) {
536  v = expression::eval_expr(std::forward<ElemRangeT>(doc), code);
537  } else {
538  for(auto&& e : doc) {
539  v = false;
540  if(e.type() == element_type::document_element)
541  v = expression::eval_expr(get<element_type::document_element>(e), code);
542  else if(e.type() == element_type::array_element)
543  v = expression::eval_expr(get<element_type::array_element>(e), code);
544  switch(v.which()) {
545  case 0: // bool
546  if(get<bool>(v))
547  vec.push_back(e);
548  break;
549  case 1: // int64
550  if(e.name() == std::to_string(get<int64_t>(v)))
551  vec.push_back(e);
552  break;
553  case 2: // string
554  if(e.name() == get<std::string>(v))
555  vec.push_back(e);
556  break;
557  case 3: // element
558  vec.push_back(e);
559  break;
560  }
561  }
562  }
563  if(path.empty()) {
564  out = boost::copy(vec, out);
565  return;
566  }
567 
568  for(auto&& e : vec) {
569  if(e.type() == element_type::document_element)
570  select(get<element_type::document_element>(e), path, out);
571  else if(e.type() == element_type::array_element)
572  select(get<element_type::array_element>(e), path, out);
573  }
574 }
575 
576 template <typename ElemRangeT, typename StrRngT, typename OutIterator>
577 void select(ElemRangeT&& doc, StrRngT path, OutIterator out) {
578  static_assert(detail::is_range_of_value<std::decay_t<ElemRangeT>, boost::mpl::quote1<detail::is_element>>::value,
579  "");
580  static_assert(is_iterator_range<std::decay_t<StrRngT>>::value, "");
581  using namespace boost;
582 
583  if(path.empty()) {
584  out = range::copy(doc, out);
585  return;
586  }
587 
588  if(path.front() == '@')
589  path.drop_front();
590 
591  StrRngT elem_name;
592  if(!starts_with(path, as_literal("..")))
593  path = range::find_if<boost::return_found_end>(path, [](auto c) { return c != '.'; });
594 
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);
599  } else {
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);
603  path.drop_front(2);
604  }
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("..")))
608  path.drop_front();
609  select_name(std::forward<ElemRangeT>(doc), path, elem_name, out);
610  }
611 }
612 
613 } // namespace detail
614 
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;
619 
620  if(path.empty() || (path.size() == 1 && path.front() == '$')) {
621  boost::range::push_back(vec, doc);
622 
623  return std::move(vec);
624  }
625 
626  path = boost::range::find_if<boost::return_found_end>(path, [](auto c) { return c != '$'; });
627 
628  detail::select(doc, path, std::back_inserter(vec));
629 
630  return std::move(vec);
631 }
632 
633 template <typename ElemRangeT, typename StrRngT>
634 auto path_select(
635  ElemRangeT&& doc, StrRngT&& path_rng,
636  std::enable_if_t<detail::is_range_of_value<ElemRangeT, boost::mpl::quote1<detail::is_element>>::value>* = nullptr) {
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;
640 
641  if(path.empty() || (path.size() == 1 && path.front() == '$')) {
642  boost::range::push_back(vec, doc);
643 
644  return std::move(vec);
645  }
646 
647  path = boost::range::find_if<boost::return_found_end>(path, [](auto c) { return c != '$'; });
648 
649  detail::select(doc, path, std::back_inserter(vec));
650 
651  return std::move(vec);
652 }
653 
654 template <typename DocRangeT, typename StrRngT>
655 auto path_select(DocRangeT&& docs, StrRngT&& path_rng,
656  std::enable_if_t<detail::is_range_of_value<
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;
661 
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);
666  }
667 
668  path = boost::range::find_if<boost::return_found_end>(path, [](auto c) { return c != '$'; });
669  detail::select(doc, path, std::back_inserter(vec));
670  }
671 
672  return std::move(vec);
673 }
674 
675 } // namespace jbson
676 
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.
Definition: traits.hpp:205
basic_document> (document_type)