diff options
Diffstat (limited to 'src/mongo/db/exec/inclusion_projection_executor.cpp')
| -rw-r--r-- | src/mongo/db/exec/inclusion_projection_executor.cpp | 131 |
1 files changed, 35 insertions, 96 deletions
diff --git a/src/mongo/db/exec/inclusion_projection_executor.cpp b/src/mongo/db/exec/inclusion_projection_executor.cpp index d06cedf61b6..d7384be8109 100644 --- a/src/mongo/db/exec/inclusion_projection_executor.cpp +++ b/src/mongo/db/exec/inclusion_projection_executor.cpp @@ -67,7 +67,7 @@ void FastPathEligibleInclusionNode::_applyProjections(BSONObj bson, BSONObjBuild const auto bsonElement{it.next()}; const auto fieldName{bsonElement.fieldNameStringData()}; - if (_projectedFieldsSet.find(fieldName) != _projectedFieldsSet.end()) { + if (_projectedFields.find(fieldName) != _projectedFields.end()) { bob->append(bsonElement); --nFieldsNeeded; } else if (auto childIt = _children.find(fieldName); childIt != _children.end()) { @@ -99,55 +99,6 @@ boost::intrusive_ptr<Expression> substituteInExpr(boost::intrusive_ptr<Expressio } return ex; }; - -/** - * Returns a vector of top-level dependencies where each index i in the vector corresponds to the - * dependencies from the ith expression according to 'orderToProcess'. Will return boost::none if - * any expression needs the whole document. - */ -boost::optional<std::vector<OrderedPathSet>> getTopLevelDeps( - const std::vector<std::string>& orderToProcess, - const StringMap<boost::intrusive_ptr<Expression>>& expressions, - const StringMap<std::unique_ptr<ProjectionNode>>& children) { - std::vector<OrderedPathSet> topLevelDeps; - for (const auto& field : orderToProcess) { - DepsTracker deps; - if (auto exprIt = expressions.find(field); exprIt != expressions.end()) { - exprIt->second->addDependencies(&deps); - } else { - // Each expression in orderToProcess should either be in expressions or children. - auto childIt = children.find(field); - tassert(6657000, "Unable to calculate dependencies", childIt != children.end()); - childIt->second->reportDependencies(&deps); - } - - if (deps.needWholeDocument) { - return boost::none; - } - - OrderedPathSet ops{deps.fields.begin(), deps.fields.end()}; - topLevelDeps.push_back( - DepsTracker::simplifyDependencies(ops, DepsTracker::TruncateToRootLevel::yes)); - } - return topLevelDeps; -} - -/** - * Returns whether or not there is an expression in the projection which depends on 'field' other - * than the expression which computes 'field'. For example, given field "a" and projection - * {a: "$b", c: {$sum: ["$a", 5]}}, return true. Given field "a" and projection - * {a: {$sum: ["$a", 5]}, c: "$b"}, return false. 'field' should be a top level path. - */ -bool computedExprDependsOnField(const std::vector<OrderedPathSet>& topLevelDeps, - const std::string& field, - const size_t fieldIndex) { - for (size_t i = 0; i < topLevelDeps.size(); i++) { - if (i != fieldIndex && topLevelDeps[i].count(field) > 0) { - return true; - } - } - return false; -} } // namespace std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject( @@ -158,14 +109,8 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject( return {BSONObj{}, false}; } - boost::optional<std::vector<OrderedPathSet>> topLevelDeps = - getTopLevelDeps(_orderToProcessAdditionsAndChildren, _expressions, _children); - - // If one of the expression requires the whole document, then we should not extract the - // projection and topLevelDeps will not hold any field names. - if (!topLevelDeps) { - return {BSONObj{}, false}; - } + DepsTracker allDeps; + reportDependencies(&allDeps); // Auxiliary vector with extracted computed projections: <name, expression, replacement // strategy>. If the replacement strategy flag is true, the expression is replaced with a @@ -173,15 +118,19 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject( std::vector<std::tuple<StringData, boost::intrusive_ptr<Expression>, bool>> addFieldsExpressions; bool replaceWithProjField = true; - for (size_t i = 0; i < _orderToProcessAdditionsAndChildren.size(); i++) { - auto&& field = _orderToProcessAdditionsAndChildren[i]; - + for (auto&& field : _orderToProcessAdditionsAndChildren) { if (reservedNames.count(field) > 0) { // Do not pushdown computed projection with reserved name. replaceWithProjField = false; continue; } - + if (allDeps.fields.count(field) > 0) { + // Do not extract a computed projection if its name is the same as a dependent field. If + // the extracted $addFields were to be placed before this projection, the dependency + // with the common name would be shadowed by the computed projection. + replaceWithProjField = false; + continue; + } auto expressionIt = _expressions.find(field); if (expressionIt == _expressions.end()) { // After seeing the first dotted path expression we need to replace computed @@ -189,17 +138,13 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject( replaceWithProjField = false; continue; } + DepsTracker deps; + expressionIt->second->addDependencies(&deps); + auto topLevelFieldNames = + deps.toProjectionWithoutMetadata(DepsTracker::TruncateToRootLevel::yes) + .getFieldNames<std::set<std::string>>(); + topLevelFieldNames.erase("_id"); - // Do not extract a computed projection if it is computing a value that other fields in the - // same projection depend on. If the extracted $addFields were to be placed before this - // projection, the dependency with the common name would be shadowed by the computed - // projection. - if (computedExprDependsOnField(topLevelDeps.get(), field, i)) { - replaceWithProjField = false; - continue; - } - - const auto& topLevelFieldNames = topLevelDeps.get()[i]; if (topLevelFieldNames.size() == 1 && topLevelFieldNames.count(oldName.toString()) == 1) { // Substitute newName for oldName in the expression. StringMap<std::string> renames; @@ -219,12 +164,11 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInProject( for (const auto& expressionSpec : addFieldsExpressions) { auto&& fieldName = std::get<0>(expressionSpec).toString(); auto oldExpr = std::get<1>(expressionSpec); - oldExpr->serialize().addToBsonObj(&bb, fieldName); + oldExpr->serialize(false).addToBsonObj(&bb, fieldName); if (std::get<2>(expressionSpec)) { // Replace the expression with an inclusion projected field. - auto it = _projectedFields.insert(_projectedFields.end(), fieldName); - _projectedFieldsSet.insert(StringData(*it)); + _projectedFields.insert(fieldName); _expressions.erase(fieldName); // Only computed projections at the beginning of the list were marked to become // projected fields. The new projected field is at the beginning of the @@ -253,40 +197,35 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInAddFields( return {BSONObj{}, false}; } - boost::optional<std::vector<OrderedPathSet>> topLevelDeps = - getTopLevelDeps(_orderToProcessAdditionsAndChildren, _expressions, _children); - - // If one of the expression requires the whole document, then we should not extract the - // projection and topLevelDeps will not hold any field names. - if (!topLevelDeps) { - return {BSONObj{}, false}; - } + DepsTracker allDeps; + reportDependencies(&allDeps); // Auxiliary vector with extracted computed projections: <name, expression>. // To preserve the original fields order, only projections at the beginning of the // _orderToProcessAdditionsAndChildren list can be extracted for pushdown. std::vector<std::pair<StringData, boost::intrusive_ptr<Expression>>> addFieldsExpressions; - for (size_t i = 0; i < _orderToProcessAdditionsAndChildren.size(); i++) { - auto&& field = _orderToProcessAdditionsAndChildren[i]; + for (auto&& field : _orderToProcessAdditionsAndChildren) { // Do not extract for pushdown computed projection with reserved name. if (reservedNames.count(field) > 0) { break; } - - auto expressionIt = _expressions.find(field); - if (expressionIt == _expressions.end()) { + if (allDeps.fields.count(field) > 0) { + // Do not extract a computed projection if its name is the same as a dependent field. If + // the extracted $addFields were to be placed before this $addFields, the dependency + // with the common name would be shadowed by the computed projection. break; } - - // Do not extract a computed projection if it is computing a value that other fields in the - // same projection depend on. If the extracted $addFields were to be placed before this - // projection, the dependency with the common name would be shadowed by the computed - // projection. - if (computedExprDependsOnField(topLevelDeps.get(), field, i)) { + auto expressionIt = _expressions.find(field); + if (expressionIt == _expressions.end()) { break; } + DepsTracker deps; + expressionIt->second->addDependencies(&deps); + auto topLevelFieldNames = + deps.toProjectionWithoutMetadata(DepsTracker::TruncateToRootLevel::yes) + .getFieldNames<std::set<std::string>>(); + topLevelFieldNames.erase("_id"); - auto& topLevelFieldNames = topLevelDeps.get()[i]; if (topLevelFieldNames.size() == 1 && topLevelFieldNames.count(oldName.toString()) == 1) { // Substitute newName for oldName in the expression. StringMap<std::string> renames; @@ -303,7 +242,7 @@ std::pair<BSONObj, bool> InclusionNode::extractComputedProjectionsInAddFields( for (const auto& expressionSpec : addFieldsExpressions) { auto&& fieldName = expressionSpec.first.toString(); auto expr = expressionSpec.second; - expr->serialize().addToBsonObj(&bb, fieldName); + expr->serialize(false).addToBsonObj(&bb, fieldName); // Remove the expression from this inclusion node. _expressions.erase(fieldName); |
