summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/query/canonical_query_encoder.cpp5
-rw-r--r--src/mongo/db/query/planner_access.cpp2
-rw-r--r--src/mongo/db/query/planner_analysis.cpp34
-rw-r--r--src/mongo/db/query/projection.cpp4
-rw-r--r--src/mongo/db/query/projection.h4
-rw-r--r--src/mongo/db/query/projection_test.cpp27
-rw-r--r--src/mongo/db/query/query_planner.cpp3
-rw-r--r--src/mongo/db/query/sbe_stage_builder.cpp23
8 files changed, 59 insertions, 43 deletions
diff --git a/src/mongo/db/query/canonical_query_encoder.cpp b/src/mongo/db/query/canonical_query_encoder.cpp
index a6a94915e91..2013c8a635e 100644
--- a/src/mongo/db/query/canonical_query_encoder.cpp
+++ b/src/mongo/db/query/canonical_query_encoder.cpp
@@ -586,11 +586,11 @@ void encodeKeyForProj(const projection_ast::Projection* proj, StringBuilder* key
return;
}
- std::vector<std::string> requiredFields = proj->getRequiredFields();
+ std::set<std::string> requiredFields = proj->getRequiredFields();
// If the only requirement is that $sortKey be included with some value, we just act as if the
// entire document is needed.
- if (requiredFields.size() == 1 && requiredFields.front() == "$sortKey") {
+ if (requiredFields.size() == 1 && *requiredFields.begin() == "$sortKey") {
return;
}
@@ -599,7 +599,6 @@ void encodeKeyForProj(const projection_ast::Projection* proj, StringBuilder* key
*keyBuilder << kEncodeProjectionSection;
// Encode the fields required by the projection in order.
- std::sort(requiredFields.begin(), requiredFields.end());
bool isFirst = true;
for (auto&& requiredField : requiredFields) {
invariant(!requiredField.empty());
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index 4d3ec560aa5..f283979cc5c 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -1163,7 +1163,7 @@ bool isCoveredNullQuery(const CanonicalQuery& query,
// Note that it is not possible to project onto dotted paths of _id here, since they may be
// null or missing, and the index cannot differentiate between the two cases, so we would
// still need a FETCH stage.
- return projFields.size() == 1 && projFields[0] == "_id";
+ return projFields.size() == 1 && *projFields.begin() == "_id";
}
return false;
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp
index 79fa06fa65b..9f3460a1cc6 100644
--- a/src/mongo/db/query/planner_analysis.cpp
+++ b/src/mongo/db/query/planner_analysis.cpp
@@ -33,6 +33,7 @@
#include <set>
#include <vector>
+#include "mongo/base/simple_string_data_comparator.h"
#include "mongo/bson/simple_bsonelement_comparator.h"
#include "mongo/db/bson/dotted_path_support.h"
#include "mongo/db/index/expression_params.h"
@@ -322,9 +323,9 @@ void geoSkipValidationOn(const std::set<StringData>& twoDSphereFields,
/**
* If any field is missing from the list of fields the projection wants, we are not covered.
*/
-auto providesAllFields(const vector<std::string>& fields, const QuerySolutionNode& solnRoot) {
- for (size_t i = 0; i < fields.size(); ++i) {
- if (!solnRoot.hasField(fields[i]))
+auto providesAllFields(const std::set<std::string>& fields, const QuerySolutionNode& solnRoot) {
+ for (auto&& field : fields) {
+ if (!solnRoot.hasField(field))
return false;
}
return true;
@@ -540,6 +541,20 @@ bool canUseSimpleSort(const QuerySolutionNode& solnRoot,
!(plannerParams.options & QueryPlannerParams::PRESERVE_RECORD_ID);
}
+/**
+ * Returns true if 'setS' is a non-strict subset of 'setT'.
+ *
+ * The types of the sets are permitted to be different to allow checking something with compatible
+ * but different types e.g. std::set<std::string> and StringDataUnorderedMap.
+ */
+template <typename SetL, typename SetR>
+bool isSubset(const SetL& setL, const SetR& setR) {
+ return setL.size() <= setR.size() &&
+ std::all_of(setL.begin(), setL.end(), [&setR](auto&& lElem) {
+ return setR.find(lElem) != setR.end();
+ });
+}
+
void removeProjectSimpleBelowGroupRecursive(QuerySolutionNode* solnRoot) {
if (solnRoot == nullptr) {
return;
@@ -562,19 +577,16 @@ void removeProjectSimpleBelowGroupRecursive(QuerySolutionNode* solnRoot) {
// projection, it would have type PROJECTION_DEFAULT.
return;
}
-
// Check to see if the projectNode's field set is a super set of the groupNodes.
- auto projectNode = static_cast<ProjectionNodeSimple*>(projectNodeCandidate);
- auto projectFields = projectNode->proj.getRequiredFields();
- if (!std::any_of(projectFields.begin(), projectFields.end(), [groupNode](auto&& fld) {
- return groupNode->requiredFields.contains(fld);
- })) {
+ if (!isSubset(groupNode->requiredFields,
+ checked_cast<ProjectionNodeSimple*>(projectNodeCandidate)
+ ->proj.getRequiredFields())) {
// The dependency set of the GROUP stage is wider than the projectNode field set.
return;
- };
+ }
// Attach the projectNode's child to the groupNode's child.
- groupNode->children[0] = std::move(projectNode->children[0]);
+ groupNode->children[0] = std::move(projectNodeCandidate->children[0]);
} else {
// Keep traversing the tree in search of a GROUP stage.
for (size_t i = 0; i < solnRoot->children.size(); ++i) {
diff --git a/src/mongo/db/query/projection.cpp b/src/mongo/db/query/projection.cpp
index 73caaa8f312..3c93367ae8c 100644
--- a/src/mongo/db/query/projection.cpp
+++ b/src/mongo/db/query/projection.cpp
@@ -49,8 +49,8 @@ struct DepsAnalysisData {
fieldDependencyTracker.fields.insert(fieldName);
}
- std::vector<std::string> requiredFields() const {
- return {fieldDependencyTracker.fields.begin(), fieldDependencyTracker.fields.end()};
+ std::set<std::string> requiredFields() const {
+ return fieldDependencyTracker.fields;
}
};
diff --git a/src/mongo/db/query/projection.h b/src/mongo/db/query/projection.h
index 80ede2a7a1a..9987dd641db 100644
--- a/src/mongo/db/query/projection.h
+++ b/src/mongo/db/query/projection.h
@@ -48,7 +48,7 @@ struct ProjectionDependencies {
bool hasExpressions = false;
// Which fields are necessary to perform the projection, or boost::none if all are required.
- boost::optional<std::vector<std::string>> requiredFields;
+ boost::optional<std::set<std::string>> requiredFields;
bool hasDottedPath = false;
@@ -94,7 +94,7 @@ public:
* Return which fields are required to compute the projection, assuming the entire document is
* not needed.
*/
- const std::vector<std::string>& getRequiredFields() const {
+ const std::set<std::string>& getRequiredFields() const {
invariant(_type == ProjectType::kInclusion);
return *_deps.requiredFields;
}
diff --git a/src/mongo/db/query/projection_test.cpp b/src/mongo/db/query/projection_test.cpp
index 35abfb3e92f..1b9bd564104 100644
--- a/src/mongo/db/query/projection_test.cpp
+++ b/src/mongo/db/query/projection_test.cpp
@@ -117,8 +117,9 @@ TEST(QueryProjectionTest, MakeSingleFieldInclusion) {
ASSERT_FALSE(proj.requiresDocument());
const auto& fields = proj.getRequiredFields();
ASSERT_EQUALS(fields.size(), 2U);
- ASSERT_EQUALS(fields[0], "_id");
- ASSERT_EQUALS(fields[1], "a");
+ auto fieldsIt = fields.begin();
+ ASSERT_EQUALS(*fieldsIt++, "_id");
+ ASSERT_EQUALS(*fieldsIt++, "a");
}
TEST(QueryProjectionTest, MakeSingleFieldInclusionNoId) {
@@ -126,7 +127,7 @@ TEST(QueryProjectionTest, MakeSingleFieldInclusionNoId) {
ASSERT_FALSE(proj.requiresDocument());
const auto& fields = proj.getRequiredFields();
ASSERT_EQUALS(fields.size(), 1U);
- ASSERT_EQUALS(fields[0], "a");
+ ASSERT_EQUALS(*fields.begin(), "a");
}
TEST(QueryProjectionTest, MakeSingleFieldId) {
@@ -134,7 +135,7 @@ TEST(QueryProjectionTest, MakeSingleFieldId) {
ASSERT_FALSE(proj.requiresDocument());
const auto& fields = proj.getRequiredFields();
ASSERT_EQUALS(fields.size(), 1U);
- ASSERT_EQUALS(fields[0], "_id");
+ ASSERT_EQUALS(*fields.begin(), "_id");
}
TEST(QueryProjectionTest, MakeSingleFieldNoIdBoolean) {
@@ -142,7 +143,7 @@ TEST(QueryProjectionTest, MakeSingleFieldNoIdBoolean) {
ASSERT_FALSE(proj.requiresDocument());
const auto& fields = proj.getRequiredFields();
ASSERT_EQUALS(fields.size(), 1U);
- ASSERT_EQUALS(fields[0], "a");
+ ASSERT_EQUALS(*fields.begin(), "a");
}
TEST(QueryProjectionTest, MakeSingleFieldFalseIdBoolean) {
@@ -150,7 +151,7 @@ TEST(QueryProjectionTest, MakeSingleFieldFalseIdBoolean) {
ASSERT_FALSE(proj.requiresDocument());
const auto& fields = proj.getRequiredFields();
ASSERT_EQUALS(fields.size(), 1U);
- ASSERT_EQUALS(fields[0], "a");
+ ASSERT_EQUALS(*fields.begin(), "a");
}
//
@@ -504,7 +505,7 @@ TEST(QueryProjectionTest, ProjectionWithExpressionIsNotSimple) {
const auto& fields = proj.getRequiredFields();
ASSERT_EQ(fields.size(), 1);
- ASSERT_EQ(fields[0], "_id");
+ ASSERT_EQ(*fields.begin(), "_id");
}
TEST(QueryProjectionTest, ProjectionWithTopLevelExpressionConstantDoesNotRequireField) {
@@ -513,8 +514,9 @@ TEST(QueryProjectionTest, ProjectionWithTopLevelExpressionConstantDoesNotRequire
const auto& fields = proj.getRequiredFields();
ASSERT_EQ(fields.size(), 2);
- ASSERT_EQ(fields[0], "_id");
- ASSERT_EQ(fields[1], "b");
+ auto fieldsIt = fields.begin();
+ ASSERT_EQ(*fieldsIt++, "_id");
+ ASSERT_EQ(*fieldsIt++, "b");
}
TEST(QueryProjectionTest, ProjectionWithROOTNeedsWholeDocument) {
@@ -530,8 +532,9 @@ TEST(QueryProjectionTest, ProjectionWithFieldPathExpressionDoesNotNeedWholeDocum
const auto& fields = proj.getRequiredFields();
ASSERT_EQ(fields.size(), 2);
- ASSERT_EQ(fields[0], "b");
- ASSERT_EQ(fields[1], "c");
+ auto fieldsIt = fields.begin();
+ ASSERT_EQ(*fieldsIt++, "b");
+ ASSERT_EQ(*fieldsIt++, "c");
}
TEST(QueryProjectionTest, AssignmentToDottedPathRequiresFirstComponent) {
@@ -541,7 +544,7 @@ TEST(QueryProjectionTest, AssignmentToDottedPathRequiresFirstComponent) {
const auto& fields = proj.getRequiredFields();
ASSERT_EQ(fields.size(), 1);
- ASSERT_EQ(fields[0], "a");
+ ASSERT_EQ(*fields.begin(), "a");
}
} // namespace
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index 693ce386cdf..de727ab6190 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -180,8 +180,7 @@ std::pair<DepsTracker, DepsTracker> computeDeps(const QueryPlannerParams& params
outputDeps.needWholeDocument = true;
return {std::move(filterDeps), std::move(outputDeps)};
}
- auto projectionFields = query.getProj()->getRequiredFields();
- outputDeps.fields.insert(projectionFields.begin(), projectionFields.end());
+ outputDeps.fields = query.getProj()->getRequiredFields();
if (auto sortPattern = query.getSortPattern()) {
sortPattern->addDependencies(&outputDeps);
}
diff --git a/src/mongo/db/query/sbe_stage_builder.cpp b/src/mongo/db/query/sbe_stage_builder.cpp
index 1ec091d515e..99384dc11fb 100644
--- a/src/mongo/db/query/sbe_stage_builder.cpp
+++ b/src/mongo/db/query/sbe_stage_builder.cpp
@@ -1635,16 +1635,19 @@ SlotBasedStageBuilder::buildProjectionSimple(const QuerySolutionNode* root,
const auto childResult = outputs.get(kResult);
outputs.set(kResult, _slotIdGenerator.generate());
- inputStage = sbe::makeS<sbe::MakeBsonObjStage>(std::move(inputStage),
- outputs.get(kResult),
- childResult,
- sbe::MakeBsonObjStage::FieldBehavior::keep,
- pn->proj.getRequiredFields(),
- std::vector<std::string>{},
- sbe::value::SlotVector{},
- true,
- false,
- root->nodeId());
+ inputStage = sbe::makeS<sbe::MakeBsonObjStage>(
+ std::move(inputStage),
+ outputs.get(kResult),
+ childResult,
+ sbe::MakeBsonObjStage::FieldBehavior::keep,
+ // TODO SERVER-67039 take a set instead of a vector here.
+ std::vector<std::string>{pn->proj.getRequiredFields().begin(),
+ pn->proj.getRequiredFields().end()},
+ std::vector<std::string>{},
+ sbe::value::SlotVector{},
+ true,
+ false,
+ root->nodeId());
return {std::move(inputStage), std::move(outputs)};
}