GCC Code Coverage Report


Directory: ./
File: src/mongo/db/pipeline/abt/match_expression_visitor.cpp
Date: 2022-10-04 12:39:21
Exec Total Coverage
Lines: 222 313 70.9%
Branches: 0 0 -%

Line Branch Exec Source
1 /**
2 * Copyright (C) 2022-present MongoDB, Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the Server Side Public License, version 1,
6 * as published by MongoDB, Inc.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Server Side Public License for more details.
12 *
13 * You should have received a copy of the Server Side Public License
14 * along with this program. If not, see
15 * <http://www.mongodb.com/licensing/server-side-public-license>.
16 *
17 * As a special exception, the copyright holders give permission to link the
18 * code of portions of this program with the OpenSSL library under certain
19 * conditions as described in each individual source file and distribute
20 * linked combinations including the program with the OpenSSL library. You
21 * must comply with the Server Side Public License in all respects for
22 * all of the code used other than as permitted herein. If you modify file(s)
23 * with this exception, you may extend this exception to your version of the
24 * file(s), but you are not obligated to do so. If you do not wish to do so,
25 * delete this exception statement from your version. If you delete this
26 * exception statement from all source files in the program, then also delete
27 * it in the license file.
28 */
29
30 #include "mongo/db/pipeline/abt/match_expression_visitor.h"
31 #include "mongo/db/matcher/expression_always_boolean.h"
32 #include "mongo/db/matcher/expression_array.h"
33 #include "mongo/db/matcher/expression_expr.h"
34 #include "mongo/db/matcher/expression_geo.h"
35 #include "mongo/db/matcher/expression_internal_bucket_geo_within.h"
36 #include "mongo/db/matcher/expression_internal_expr_comparison.h"
37 #include "mongo/db/matcher/expression_leaf.h"
38 #include "mongo/db/matcher/expression_text.h"
39 #include "mongo/db/matcher/expression_text_noop.h"
40 #include "mongo/db/matcher/expression_tree.h"
41 #include "mongo/db/matcher/expression_type.h"
42 #include "mongo/db/matcher/expression_visitor.h"
43 #include "mongo/db/matcher/expression_where.h"
44 #include "mongo/db/matcher/expression_where_noop.h"
45 #include "mongo/db/matcher/match_expression_walker.h"
46 #include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h"
47 #include "mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h"
48 #include "mongo/db/matcher/schema/expression_internal_schema_cond.h"
49 #include "mongo/db/matcher/schema/expression_internal_schema_eq.h"
50 #include "mongo/db/matcher/schema/expression_internal_schema_fmod.h"
51 #include "mongo/db/matcher/schema/expression_internal_schema_match_array_index.h"
52 #include "mongo/db/matcher/schema/expression_internal_schema_max_items.h"
53 #include "mongo/db/matcher/schema/expression_internal_schema_max_length.h"
54 #include "mongo/db/matcher/schema/expression_internal_schema_max_properties.h"
55 #include "mongo/db/matcher/schema/expression_internal_schema_min_items.h"
56 #include "mongo/db/matcher/schema/expression_internal_schema_min_length.h"
57 #include "mongo/db/matcher/schema/expression_internal_schema_min_properties.h"
58 #include "mongo/db/matcher/schema/expression_internal_schema_object_match.h"
59 #include "mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h"
60 #include "mongo/db/matcher/schema/expression_internal_schema_unique_items.h"
61 #include "mongo/db/matcher/schema/expression_internal_schema_xor.h"
62 #include "mongo/db/pipeline/abt/agg_expression_visitor.h"
63 #include "mongo/db/pipeline/abt/expr_algebrizer_context.h"
64 #include "mongo/db/pipeline/abt/utils.h"
65 #include "mongo/db/query/optimizer/utils/utils.h"
66
67 namespace mongo::optimizer {
68
69 class ABTMatchExpressionVisitor : public MatchExpressionConstVisitor {
70 public:
71 41472 ABTMatchExpressionVisitor(ExpressionAlgebrizerContext& ctx, const bool allowAggExpressions)
72 41472 : _allowAggExpressions(allowAggExpressions), _ctx(ctx) {}
73
74 void visit(const AlwaysFalseMatchExpression* expr) override {
75 generateBoolConstant(false);
76 }
77
78 void visit(const AlwaysTrueMatchExpression* expr) override {
79 generateBoolConstant(true);
80 }
81
82 19944 void visit(const AndMatchExpression* expr) override {
83 19944 visitAndOrExpression<PathComposeM, true>(expr);
84 19944 }
85
86 void visit(const BitsAllClearMatchExpression* expr) override {
87 unsupportedExpression(expr);
88 }
89
90 void visit(const BitsAllSetMatchExpression* expr) override {
91 unsupportedExpression(expr);
92 }
93
94 void visit(const BitsAnyClearMatchExpression* expr) override {
95 unsupportedExpression(expr);
96 }
97
98 void visit(const BitsAnySetMatchExpression* expr) override {
99 unsupportedExpression(expr);
100 }
101
102 2328 void visit(const ElemMatchObjectMatchExpression* expr) override {
103 2328 generateElemMatch<false /*isValueElemMatch*/>(expr);
104 2328 }
105
106 738 void visit(const ElemMatchValueMatchExpression* expr) override {
107 738 generateElemMatch<true /*isValueElemMatch*/>(expr);
108 738 }
109
110 620712 void visit(const EqualityMatchExpression* expr) override {
111 620712 generateSimpleComparison(expr, Operations::Eq);
112 620712 }
113
114 2040 void visit(const ExistsMatchExpression* expr) override {
115 2040 assertSupportedPathExpression(expr);
116
117 2040 ABT result = make<PathDefault>(Constant::boolean(false));
118 2040 if (!expr->path().empty()) {
119 2028 result = generateFieldPath(FieldPath(expr->path().toString()), std::move(result));
120 }
121 2040 _ctx.push(std::move(result));
122 2040 }
123
124 36 void visit(const ExprMatchExpression* expr) override {
125 36 uassert(6624246, "Cannot generate an agg expression in this context", _allowAggExpressions);
126
127 ABT result = generateAggExpression(
128 72 expr->getExpression().get(), _ctx.getRootProjection(), _ctx.getUniqueIdPrefix());
129
130 36 if (auto filterPtr = result.cast<EvalFilter>();
131 36 filterPtr != nullptr && filterPtr->getInput() == _ctx.getRootProjVar()) {
132 // If we have an EvalFilter, just return the path.
133 6 _ctx.push(std::move(filterPtr->getPath()));
134 } else {
135 30 _ctx.push<PathConstant>(std::move(result));
136 }
137 36 }
138
139 4032 void visit(const GTEMatchExpression* expr) override {
140 4032 generateSimpleComparison(expr, Operations::Gte);
141 4032 }
142
143 9846 void visit(const GTMatchExpression* expr) override {
144 9846 generateSimpleComparison(expr, Operations::Gt);
145 9846 }
146
147 void visit(const GeoMatchExpression* expr) override {
148 unsupportedExpression(expr);
149 }
150
151 void visit(const GeoNearMatchExpression* expr) override {
152 unsupportedExpression(expr);
153 }
154
155 4308 void visit(const InMatchExpression* expr) override {
156 4308 uassert(ErrorCodes::InternalErrorNotSupported,
157 "$in with regexes is not supported.",
158 expr->getRegexes().empty());
159
160 4308 assertSupportedPathExpression(expr);
161
162 4308 const auto& equalities = expr->getEqualities();
163
164 // $in with an empty equalities list matches nothing; replace with constant false.
165 4308 if (equalities.empty()) {
166 72 generateBoolConstant(false);
167 72 return;
168 }
169
170 8472 ABT result = make<PathIdentity>();
171
172 4236 const auto [tagTraverse, valTraverse] = sbe::value::makeNewArray();
173 8472 sbe::value::ValueGuard arrGuard{tagTraverse, valTraverse};
174 4236 auto arrTraversePtr = sbe::value::getArrayView(valTraverse);
175 4236 arrTraversePtr->reserve(equalities.size());
176
177 4236 const auto [tagArraysOnly, valArraysOnly] = sbe::value::makeNewArray();
178 4236 sbe::value::ValueGuard arrOnlyGuard{tagArraysOnly, valArraysOnly};
179 4236 auto arraysOnlyPtr = sbe::value::getArrayView(valArraysOnly);
180 4236 bool addNullPathDefault = false;
181 4236 arraysOnlyPtr->reserve(equalities.size());
182
183 15282 for (const auto& pred : equalities) {
184 11046 const auto [tag, val] = convertFrom(Value(pred));
185 11046 arrTraversePtr->push_back(tag, val);
186
187 11046 if (tag == sbe::value::TypeTags::Null) {
188 192 addNullPathDefault = true;
189 10854 } else if (tag == sbe::value::TypeTags::Array) {
190 1002 const auto [tag2, val2] = sbe::value::copyValue(tag, val);
191 1002 arraysOnlyPtr->push_back(tag2, val2);
192 }
193 }
194
195 // If there is only one term in the match expression, we don't need to use EqMember
196 4236 if (arrTraversePtr->size() == 1) {
197 6 const auto [tagSingle, valSingle] = sbe::value::copyValue(
198 6 arrTraversePtr->getAt(0).first, arrTraversePtr->getAt(0).second);
199 6 result = make<PathCompare>(Operations::Eq, make<Constant>(tagSingle, valSingle));
200 } else {
201 result =
202 4230 make<PathCompare>(Operations::EqMember, make<Constant>(tagTraverse, valTraverse));
203 4230 arrGuard.reset();
204 }
205
206 4236 if (addNullPathDefault) {
207 192 maybeComposePath<PathComposeA>(result, make<PathDefault>(Constant::boolean(true)));
208 }
209
210 // The path can be empty if we are within an $elemMatch. In this case elemMatch would
211 // insert a traverse.
212 4236 if (!expr->path().empty()) {
213 // When the path we are comparing is a path to an array, the comparison is
214 // considered true if it evaluates to true for the array itself or for any of the
215 // array���s elements. 'result' evaluates comparison on the array elements, and
216 // 'arraysOnly' evaluates the comparison on the array itself.
217 4128 result = make<PathTraverse>(std::move(result), PathTraverse::kSingleLevel);
218
219 4128 if (arraysOnlyPtr->size() == 1) {
220 858 const auto [tagSingle, valSingle] = sbe::value::copyValue(
221 858 arraysOnlyPtr->getAt(0).first, arraysOnlyPtr->getAt(0).second);
222 858 maybeComposePath<PathComposeA>(
223 result,
224 1716 make<PathCompare>(Operations::Eq, make<Constant>(tagSingle, valSingle)));
225 3270 } else if (arraysOnlyPtr->size() > 0) {
226 72 maybeComposePath<PathComposeA>(
227 result,
228 144 make<PathCompare>(Operations::EqMember,
229 144 make<Constant>(tagArraysOnly, valArraysOnly)));
230 72 arrOnlyGuard.reset();
231 }
232 4128 result = generateFieldPath(FieldPath(expr->path().toString()), std::move(result));
233 }
234 4236 _ctx.push(std::move(result));
235 }
236
237 void visit(const InternalBucketGeoWithinMatchExpression* expr) override {
238 unsupportedExpression(expr);
239 }
240
241 42 void visit(const InternalExprEqMatchExpression* expr) override {
242 // Ignored. Translate to "true".
243 42 _ctx.push(make<PathConstant>(Constant::boolean(true)));
244 42 }
245
246 void visit(const InternalExprGTMatchExpression* expr) override {
247 // Ignored. Translate to "true".
248 _ctx.push(make<PathConstant>(Constant::boolean(true)));
249 }
250
251 void visit(const InternalExprGTEMatchExpression* expr) override {
252 // Ignored. Translate to "true".
253 _ctx.push(make<PathConstant>(Constant::boolean(true)));
254 }
255
256 void visit(const InternalExprLTMatchExpression* expr) override {
257 // Ignored. Translate to "true".
258 _ctx.push(make<PathConstant>(Constant::boolean(true)));
259 }
260
261 void visit(const InternalExprLTEMatchExpression* expr) override {
262 // Ignored. Translate to "true".
263 _ctx.push(make<PathConstant>(Constant::boolean(true)));
264 }
265
266 void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) override {
267 unsupportedExpression(expr);
268 }
269
270 void visit(const InternalSchemaAllowedPropertiesMatchExpression* expr) override {
271 unsupportedExpression(expr);
272 }
273
274 void visit(const InternalSchemaBinDataEncryptedTypeExpression* expr) override {
275 unsupportedExpression(expr);
276 }
277
278 void visit(const InternalSchemaBinDataFLE2EncryptedTypeExpression* expr) override {
279 unsupportedExpression(expr);
280 }
281
282 void visit(const InternalSchemaBinDataSubTypeExpression* expr) override {
283 unsupportedExpression(expr);
284 }
285
286 void visit(const InternalSchemaCondMatchExpression* expr) override {
287 unsupportedExpression(expr);
288 }
289
290 void visit(const InternalSchemaEqMatchExpression* expr) override {
291 unsupportedExpression(expr);
292 }
293
294 void visit(const InternalSchemaFmodMatchExpression* expr) override {
295 unsupportedExpression(expr);
296 }
297
298 void visit(const InternalSchemaMatchArrayIndexMatchExpression* expr) override {
299 unsupportedExpression(expr);
300 }
301
302 void visit(const InternalSchemaMaxItemsMatchExpression* expr) override {
303 unsupportedExpression(expr);
304 }
305
306 void visit(const InternalSchemaMaxLengthMatchExpression* expr) override {
307 unsupportedExpression(expr);
308 }
309
310 void visit(const InternalSchemaMaxPropertiesMatchExpression* expr) override {
311 unsupportedExpression(expr);
312 }
313
314 void visit(const InternalSchemaMinItemsMatchExpression* expr) override {
315 unsupportedExpression(expr);
316 }
317
318 void visit(const InternalSchemaMinLengthMatchExpression* expr) override {
319 unsupportedExpression(expr);
320 }
321
322 void visit(const InternalSchemaMinPropertiesMatchExpression* expr) override {
323 unsupportedExpression(expr);
324 }
325
326 void visit(const InternalSchemaObjectMatchExpression* expr) override {
327 unsupportedExpression(expr);
328 }
329
330 void visit(const InternalSchemaRootDocEqMatchExpression* expr) override {
331 unsupportedExpression(expr);
332 }
333
334 void visit(const InternalSchemaTypeExpression* expr) override {
335 unsupportedExpression(expr);
336 }
337
338 void visit(const InternalSchemaUniqueItemsMatchExpression* expr) override {
339 unsupportedExpression(expr);
340 }
341
342 void visit(const InternalSchemaXorMatchExpression* expr) override {
343 unsupportedExpression(expr);
344 }
345
346 1050 void visit(const LTEMatchExpression* expr) override {
347 1050 generateSimpleComparison(expr, Operations::Lte);
348 1050 }
349
350 3144 void visit(const LTMatchExpression* expr) override {
351 3144 generateSimpleComparison(expr, Operations::Lt);
352 3144 }
353
354 void visit(const ModMatchExpression* expr) override {
355 unsupportedExpression(expr);
356 }
357
358 void visit(const NorMatchExpression* expr) override {
359 unsupportedExpression(expr);
360 }
361
362 60 void visit(const NotMatchExpression* expr) override {
363 60 ABT result = _ctx.pop();
364 60 _ctx.push(make<PathConstant>(make<UnaryOp>(
365 60 Operations::Not,
366 120 make<EvalFilter>(std::move(result), make<Variable>(_ctx.getRootProjection())))));
367 60 }
368
369 8346 void visit(const OrMatchExpression* expr) override {
370 8346 visitAndOrExpression<PathComposeA, false>(expr);
371 8346 }
372
373 void visit(const RegexMatchExpression* expr) override {
374 unsupportedExpression(expr);
375 }
376
377 6 void visit(const SizeMatchExpression* expr) override {
378 6 assertSupportedPathExpression(expr);
379
380 18 const std::string lambdaProjName = _ctx.getNextId("lambda_sizeMatch");
381 12 ABT result = make<PathLambda>(make<LambdaAbstraction>(
382 lambdaProjName,
383 make<BinaryOp>(
384 12 Operations::Eq,
385 12 make<FunctionCall>("getArraySize", makeSeq(make<Variable>(lambdaProjName))),
386 12 Constant::int64(expr->getData()))));
387
388 6 if (!expr->path().empty()) {
389 // No traverse.
390 30 result = translateFieldPath(
391 12 FieldPath(expr->path().toString()),
392 6 std::move(result),
393 3 [](const std::string& fieldName, const bool /*isLastElement*/, ABT input) {
394 3 return make<PathGet>(fieldName, std::move(input));
395 6 });
396 }
397 6 _ctx.push(std::move(result));
398 6 }
399
400 void visit(const TextMatchExpression* expr) override {
401 unsupportedExpression(expr);
402 }
403
404 void visit(const TextNoOpMatchExpression* expr) override {
405 unsupportedExpression(expr);
406 }
407
408 void visit(const TwoDPtInAnnulusExpression* expr) override {
409 unsupportedExpression(expr);
410 }
411
412 48 void visit(const TypeMatchExpression* expr) override {
413 48 assertSupportedPathExpression(expr);
414
415 144 const std::string lambdaProjName = _ctx.getNextId("lambda_typeMatch");
416 96 ABT result = make<PathLambda>(make<LambdaAbstraction>(
417 lambdaProjName,
418 96 make<FunctionCall>("typeMatch",
419 96 makeSeq(make<Variable>(lambdaProjName),
420 96 Constant::int32(expr->typeSet().getBSONTypeMask())))));
421
422 // The path can be empty if we are within an $elemMatch. In this case elemMatch would insert
423 // a traverse.
424 48 if (!expr->path().empty()) {
425 48 result = make<PathTraverse>(std::move(result), PathTraverse::kSingleLevel);
426 48 if (expr->typeSet().hasType(BSONType::Array)) {
427 // If we are testing against array type, insert a comparison against the
428 // non-traversed path (the array itself if we have one).
429 12 result = make<PathComposeA>(make<PathArr>(), std::move(result));
430 }
431
432 48 result = generateFieldPath(FieldPath(expr->path().toString()), std::move(result));
433 }
434 48 _ctx.push(std::move(result));
435 48 }
436
437 void visit(const WhereMatchExpression* expr) override {
438 unsupportedExpression(expr);
439 }
440
441 void visit(const WhereNoOpMatchExpression* expr) override {
442 unsupportedExpression(expr);
443 }
444
445 void visit(const BetweenMatchExpression* expr) override {
446 unsupportedExpression(expr);
447 }
448
449 private:
450 13212 void generateBoolConstant(const bool value) {
451 13212 _ctx.push<PathConstant>(Constant::boolean(value));
452 13212 }
453
454 template <bool isValueElemMatch>
455 3066 void generateElemMatch(const ArrayMatchingMatchExpression* expr) {
456 3066 assertSupportedPathExpression(expr);
457
458 // Returns true if at least one sub-objects matches the condition.
459
460 3066 const size_t childCount = expr->numChildren();
461 3066 if (childCount == 0) {
462 _ctx.push(Constant::boolean(true));
463 }
464
465 3066 _ctx.ensureArity(childCount);
466 3066 ABT result = _ctx.pop();
467 3300 for (size_t i = 1; i < childCount; i++) {
468 234 maybeComposePath(result, _ctx.pop());
469 }
470 if constexpr (!isValueElemMatch) {
471 // Make sure we consider only objects or arrays as elements of the array.
472 2328 maybeComposePath(result, make<PathComposeA>(make<PathObj>(), make<PathArr>()));
473 }
474 3066 result = make<PathTraverse>(std::move(result), PathTraverse::kSingleLevel);
475
476 // Make sure we consider only arrays fields on the path.
477 3066 maybeComposePath(result, make<PathArr>());
478
479 3066 if (!expr->path().empty()) {
480 12000 result = translateFieldPath(
481 6000 FieldPath{expr->path().toString()},
482 3000 std::move(result),
483 2514 [&](const std::string& fieldName, const bool isLastElement, ABT input) {
484 2514 if (!isLastElement) {
485 1014 input = make<PathTraverse>(std::move(input), PathTraverse::kSingleLevel);
486 }
487 2514 return make<PathGet>(fieldName, std::move(input));
488 });
489 }
490
491 3066 _ctx.push(std::move(result));
492 3066 }
493
494 644202 ABT generateFieldPath(const FieldPath& fieldPath, ABT initial) {
495 return translateFieldPath(
496 fieldPath,
497 644202 std::move(initial),
498 325698 [&](const std::string& fieldName, const bool isLastElement, ABT input) {
499 325698 if (!isLastElement) {
500 3597 input = make<PathTraverse>(std::move(input), PathTraverse::kSingleLevel);
501 }
502 325698 return make<PathGet>(fieldName, std::move(input));
503 1288404 });
504 }
505
506 648252 void assertSupportedPathExpression(const PathMatchExpression* expr) {
507 648252 uassert(ErrorCodes::InternalErrorNotSupported,
508 "Expression contains a numeric path component",
509 !FieldRef(expr->path()).hasNumericPathComponents());
510 648252 }
511
512 638784 void generateSimpleComparison(const ComparisonMatchExpressionBase* expr, const Operations op) {
513 638784 assertSupportedPathExpression(expr);
514
515 638784 auto [tag, val] = convertFrom(Value(expr->getData()));
516 638784 ABT result = make<PathCompare>(op, make<Constant>(tag, val));
517
518 638784 bool tagNullMatchMissingField =
519 638784 tag == sbe::value::TypeTags::Null && (op == Operations::Lte || op == Operations::Gte);
520
521 638784 switch (op) {
522 4194 case Operations::Lt:
523 case Operations::Lte: {
524 8388 auto&& [constant, inclusive] = getMinMaxBoundForType(true /*isMin*/, tag);
525 4194 if (constant) {
526 4074 maybeComposePath(result,
527 8148 make<PathCompare>(inclusive ? Operations::Gte : Operations::Gt,
528 4074 std::move(constant.get())));
529 }
530 // Handle null and missing semantics
531 // find({a: {$lt: MaxKey()}}) matches {a: null} and {b: 1}
532 // find({a: {$lte: null}}) matches {a: null} and {b: 1})
533 4194 if (tag == sbe::value::TypeTags::MaxKey || tagNullMatchMissingField) {
534 90 maybeComposePath<PathComposeA>(result,
535 180 make<PathDefault>(Constant::boolean(true)));
536 }
537 4194 break;
538 }
539
540 13878 case Operations::Gt:
541 case Operations::Gte: {
542 27756 auto&& [constant, inclusive] = getMinMaxBoundForType(false /*isMin*/, tag);
543 13878 if (constant) {
544 13764 maybeComposePath(result,
545 27528 make<PathCompare>(inclusive ? Operations::Lte : Operations::Lt,
546 13764 std::move(constant.get())));
547 }
548 // Handle null and missing semantics
549 // find({a: {$gt: MinKey()}}) matches {a: null} and {b: 1}
550 // find({a: {$gte: null}}) matches {a: null} and {b: 1})
551 13878 if (tag == sbe::value::TypeTags::MinKey || tagNullMatchMissingField) {
552 84 maybeComposePath<PathComposeA>(result,
553 168 make<PathDefault>(Constant::boolean(true)));
554 }
555 13878 break;
556 }
557
558 620712 case Operations::Eq: {
559 620712 if (tag == sbe::value::TypeTags::Null) {
560 // Handle null and missing semantics. Matching against null also implies
561 // matching against missing.
562 384 result = make<PathComposeA>(make<PathDefault>(Constant::boolean(true)),
563 384 std::move(result));
564 }
565 620712 break;
566 }
567
568 default:
569 break;
570 }
571
572 // The path can be empty if we are within an $elemMatch. In this case elemMatch would
573 // insert a traverse.
574 638784 if (!expr->path().empty()) {
575 637998 if (tag == sbe::value::TypeTags::Array || tag == sbe::value::TypeTags::MinKey ||
576 637296 tag == sbe::value::TypeTags::MaxKey) {
577 // The behavior of PathTraverse when it encounters an array is to apply its subpath
578 // to every element of the array and not the array itself. When an expression is
579 // comparing a field to an array, minKey or maxKey constant, we need to ensure that
580 // these comparisons happen to every element of the array and the array itself.
581 // For example:
582 // find({a: [1]}) matches {a: [1]} and {a: [[1]]}
583 // find({a: {$gt: MinKey()}}) matches {a: []}
584 1668 result = make<PathComposeA>(make<PathTraverse>(result, PathTraverse::kSingleLevel),
585 834 result);
586 } else {
587 637164 result = make<PathTraverse>(std::move(result), PathTraverse::kSingleLevel);
588 }
589
590 637998 result = generateFieldPath(FieldPath(expr->path().toString()), std::move(result));
591 }
592 638784 _ctx.push(std::move(result));
593 638784 }
594
595 template <class Composition, bool defaultResult>
596 28290 void visitAndOrExpression(const ListOfMatchExpression* expr) {
597 28290 const size_t childCount = expr->numChildren();
598 28290 if (childCount == 0) {
599 13140 generateBoolConstant(defaultResult);
600 13140 return;
601 }
602 15150 if (childCount == 1) {
603 return;
604 }
605
606 15150 ABTVector nodes;
607 646998 for (size_t i = 0; i < childCount; i++) {
608 631848 nodes.push_back(_ctx.pop());
609 }
610
611 // Construct a balanced composition tree.
612 15150 maybeComposePaths<Composition>(nodes);
613 15150 _ctx.push(std::move(nodes.front()));
614 }
615
616 void unsupportedExpression(const MatchExpression* expr) const {
617 uasserted(ErrorCodes::InternalErrorNotSupported,
618 str::stream() << "Match expression is not supported: " << expr->matchType());
619 }
620
621 // If we are parsing a partial index filter, we don't allow agg expressions.
622 const bool _allowAggExpressions;
623
624 // We don't own this
625 ExpressionAlgebrizerContext& _ctx;
626 };
627
628 20736 ABT generateMatchExpression(const MatchExpression* expr,
629 const bool allowAggExpressions,
630 const std::string& rootProjection,
631 const std::string& uniqueIdPrefix) {
632 ExpressionAlgebrizerContext ctx(
633 41472 false /*assertExprSort*/, true /*assertPathSort*/, rootProjection, uniqueIdPrefix);
634 41472 ABTMatchExpressionVisitor visitor(ctx, allowAggExpressions);
635 20736 MatchExpressionWalker walker(nullptr /*preVisitor*/, nullptr /*inVisitor*/, &visitor);
636 20736 tree_walker::walk<true, MatchExpression>(expr, &walker);
637 41472 return ctx.pop();
638 }
639
640 } // namespace mongo::optimizer
641