* Converts a mongo-like query document (query in the above grammar) into a corresponding * QL predicate. */
| 124 | * QL predicate. |
| 125 | */ |
| 126 | Reference<IPredicate> queryToPredicate(bson::BSONObj const& query, bool toplevel) { |
| 127 | std::vector<Reference<IPredicate>> terms; |
| 128 | |
| 129 | for (auto i = query.begin(); i.more();) { |
| 130 | auto el = i.next(); |
| 131 | |
| 132 | if (el.type() == bson::BSONType::RegEx) { |
| 133 | terms.push_back(re_predicate(el, el.fieldName())); |
| 134 | } else if (el.fieldName()[0] == '$') { |
| 135 | if (el.isABSONObj()) { |
| 136 | try { |
| 137 | terms.push_back(ExtBoolOperator::toPredicate(el.fieldName(), el.Obj())); |
| 138 | } catch (Error& e) { |
| 139 | if (e.code() == error_code_bad_dispatch) |
| 140 | throw fieldname_with_dollar(); |
| 141 | else |
| 142 | throw; |
| 143 | } |
| 144 | } else { |
| 145 | throw fieldname_with_dollar(); |
| 146 | } |
| 147 | } else if (el.isABSONObj() && !is_literal_match(el.Obj())) { |
| 148 | // If this is a top-level _id path then force use of elemMatch because _id can NOT be an array. |
| 149 | if (toplevel && strcmp(el.fieldName(), DocLayerConstants::ID_FIELD) == 0) |
| 150 | terms.push_back(ExtValueOperator::toPredicate("$elemMatch", el.fieldName(), el)); |
| 151 | else |
| 152 | valueQueryToPredicates(el.Obj(), el.fieldName(), terms); |
| 153 | } else { |
| 154 | terms.push_back(eq_predicate(el, el.fieldName())); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | return Reference<IPredicate>(new AndPredicate(terms)); |
| 159 | } |
| 160 | |
| 161 | Reference<Plan> planQuery(Reference<UnboundCollectionContext> cx, bson::BSONObj const& query) { |
| 162 | auto predicate = queryToPredicate(query, true); |
no test coverage detected