summaryrefslogtreecommitdiff
path: root/src/mongo/db/exec/inclusion_projection_executor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/exec/inclusion_projection_executor.cpp')
-rw-r--r--src/mongo/db/exec/inclusion_projection_executor.cpp131
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);