diff options
author | Drew Paroski <drew.paroski@mongodb.com> | 2021-12-06 05:23:32 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-12-10 18:46:28 +0000 |
commit | 8d4e810c89b771ded201390990a527a9a8799d2f (patch) | |
tree | d77ae8ca7d8cb86311b290fbcc40a77c8dbaabc7 | |
parent | 3318680eba6ed5a7de40fb400e0739a664ca9c6e (diff) | |
download | mongo-8d4e810c89b771ded201390990a527a9a8799d2f.tar.gz |
SERVER-61839 Fix SBE implementation of $exists and $type when a positional projection is used
-rw-r--r-- | jstests/core/positional_projection.js | 12 | ||||
-rw-r--r-- | src/mongo/db/query/sbe_stage_builder_filter.cpp | 50 |
2 files changed, 48 insertions, 14 deletions
diff --git a/jstests/core/positional_projection.js b/jstests/core/positional_projection.js index 9b0b013d51a..5c16231a667 100644 --- a/jstests/core/positional_projection.js +++ b/jstests/core/positional_projection.js @@ -181,6 +181,18 @@ testSingleDocument( testSingleDocument({a: {$in: [2, 3]}}, {'b.$': 1}, {a: [1, 2, 3], b: [4, 5, 6]}, {b: [5]}); testSingleDocument({$or: [{a: 2}, {a: 3}]}, {'b.$': 1}, {a: [1, 2, 3], b: [4, 5, 6]}, {b: [5]}); +// SERVER-61839: Test out some cases involving $exists and $type where we've had bugs in the past. +testSingleDocument( + {a: {$elemMatch: {y: {$exists: true}}}}, {"a.$": 1}, {a: [{y: 1}, {y: 2}]}, {a: [{y: 1}]}); + +testSingleDocument( + {a: {$elemMatch: {y: {$type: ["array"]}}}}, {"a.$": 1}, {a: [{y: 1}, {y: []}]}, {a: [{y: []}]}); + +testSingleDocument({a: {$elemMatch: {y: {$type: ["array", "double"]}}}}, + {"a.$": 1}, + {a: [{y: 1}, {y: []}]}, + {a: [{y: 1}]}); + // Tests involving getMore. Test the $-positional operator across multiple batches. assert.commandWorked(coll.insert([ { diff --git a/src/mongo/db/query/sbe_stage_builder_filter.cpp b/src/mongo/db/query/sbe_stage_builder_filter.cpp index 8e3c768e3d2..01bb3ccd8eb 100644 --- a/src/mongo/db/query/sbe_stage_builder_filter.cpp +++ b/src/mongo/db/query/sbe_stage_builder_filter.cpp @@ -1370,17 +1370,26 @@ public: } void visit(const ExistsMatchExpression* expr) final { - auto makePredicate = [](sbe::value::SlotId inputSlot, - EvalStage inputStage) -> EvalExprStagePair { - return {sbe::makeE<sbe::EFunction>("exists", - sbe::makeEs(sbe::makeE<sbe::EVariable>(inputSlot))), - std::move(inputStage)}; + const auto traversalMode = LeafTraversalMode::kDoNotTraverseLeaf; + + auto makePredicate = [expr, context = _context](sbe::value::SlotId inputSlot, + EvalStage inputStage) -> EvalExprStagePair { + auto resultExpr = sbe::makeE<sbe::EFunction>( + "exists", sbe::makeEs(sbe::makeE<sbe::EVariable>(inputSlot))); + + // $exists is always applied to the leaf of the field path. For kDoNotTraverseLeaf mode, + // generatePredicate() does not convert the predicate value to state when generating + // traversal for leaf nodes of field path. For this reason, we need to perform this + // conversion manually. + if (expr->fieldRef() && !expr->fieldRef()->empty() && + context->evalStack.topFrame().data().inputSlot) { + resultExpr = context->stateHelper.makeState(std::move(resultExpr)); + } + + return {std::move(resultExpr), std::move(inputStage)}; }; - generatePredicate(_context, - expr->fieldRef(), - std::move(makePredicate), - LeafTraversalMode::kDoNotTraverseLeaf); + generatePredicate(_context, expr->fieldRef(), std::move(makePredicate), traversalMode); } void visit(const ExprMatchExpression* matchExpr) final { @@ -1743,17 +1752,30 @@ public: void visit(const TwoDPtInAnnulusExpression* expr) final {} void visit(const TypeMatchExpression* expr) final { - auto makePredicate = [expr](sbe::value::SlotId inputSlot, - EvalStage inputStage) -> EvalExprStagePair { + const auto traversalMode = expr->typeSet().hasType(BSONType::Array) + ? LeafTraversalMode::kDoNotTraverseLeaf + : LeafTraversalMode::kArrayElementsOnly; + + auto makePredicate = + [expr, traversalMode, context = _context](sbe::value::SlotId inputSlot, + EvalStage inputStage) -> EvalExprStagePair { const MatcherTypeSet& ts = expr->typeSet(); auto resultExpr = makeFillEmptyFalse( sbe::makeE<sbe::ETypeMatch>(makeVariable(inputSlot), ts.getBSONTypeMask())); + + // $type is always applied to the leaf of the field path. For kDoNotTraverseLeaf mode, + // generatePredicate() does not convert the predicate value to state when generating + // traversal for leaf nodes of field path. For this reason, we need to perform this + // conversion manually. + if (expr->fieldRef() && !expr->fieldRef()->empty() && + context->evalStack.topFrame().data().inputSlot && + traversalMode == LeafTraversalMode::kDoNotTraverseLeaf) { + resultExpr = context->stateHelper.makeState(std::move(resultExpr)); + } + return {std::move(resultExpr), std::move(inputStage)}; }; - const auto traversalMode = expr->typeSet().hasType(BSONType::Array) - ? LeafTraversalMode::kDoNotTraverseLeaf - : LeafTraversalMode::kArrayElementsOnly; generatePredicate(_context, expr->fieldRef(), std::move(makePredicate), traversalMode); } |