summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Korshunov <anton.korshunov@mongodb.com>2019-10-11 20:13:48 +0000
committerevergreen <evergreen@mongodb.com>2019-10-11 20:13:48 +0000
commit70383ed065e8798aafc4394af7c6d2ac92927f41 (patch)
tree6f7f64aea51387ed436efd36fdc24e6932489be1
parent34719559a9ec4bd494a59d269d227d06b086affb (diff)
downloadmongo-70383ed065e8798aafc4394af7c6d2ac92927f41.tar.gz
SERVER-42423 Use ProjectionExecutor in ProjectionStageDefault
-rw-r--r--jstests/core/find_and_modify_server6865.js2
-rw-r--r--src/mongo/base/exact_cast.h2
-rw-r--r--src/mongo/db/exec/find_projection_executor.cpp10
-rw-r--r--src/mongo/db/exec/find_projection_executor_test.cpp8
-rw-r--r--src/mongo/db/exec/projection.cpp156
-rw-r--r--src/mongo/db/exec/projection.h19
-rw-r--r--src/mongo/db/exec/projection_executor.cpp26
-rw-r--r--src/mongo/db/exec/projection_executor.h2
-rw-r--r--src/mongo/db/query/canonical_query.h5
-rw-r--r--src/mongo/db/query/get_executor.cpp19
-rw-r--r--src/mongo/db/query/projection.cpp38
-rw-r--r--src/mongo/db/query/projection.h19
-rw-r--r--src/mongo/db/query/projection_ast.h45
-rw-r--r--src/mongo/db/query/projection_ast_path_tracking_visitor.h56
-rw-r--r--src/mongo/db/query/projection_ast_util.cpp47
-rw-r--r--src/mongo/db/query/projection_ast_util.h2
-rw-r--r--src/mongo/db/query/projection_ast_visitor.h29
-rw-r--r--src/mongo/db/query/projection_ast_walker.h9
-rw-r--r--src/mongo/db/query/projection_parser.cpp2
-rw-r--r--src/mongo/db/query/projection_test.cpp15
-rw-r--r--src/mongo/db/query/query_planner_test_lib.cpp10
-rw-r--r--src/mongo/db/query/query_solution.cpp3
-rw-r--r--src/mongo/db/query/stage_builder.cpp18
23 files changed, 308 insertions, 234 deletions
diff --git a/jstests/core/find_and_modify_server6865.js b/jstests/core/find_and_modify_server6865.js
index 1c5d9363a6f..79d55c5b3a7 100644
--- a/jstests/core/find_and_modify_server6865.js
+++ b/jstests/core/find_and_modify_server6865.js
@@ -258,7 +258,7 @@ testFAMWorked({
update: {$set: {c: 'xyz'}},
new: true
},
- {_id: 42, b: [{name: 'second', value: 2}], c: 'xyz'});
+ {_id: 42, c: 'xyz', b: [{name: 'second', value: 2}]});
// Query on an array of objects while using $elemMatch in the projection,
// where the matched array element is modified.
diff --git a/src/mongo/base/exact_cast.h b/src/mongo/base/exact_cast.h
index 99622670024..ff96eebe22e 100644
--- a/src/mongo/base/exact_cast.h
+++ b/src/mongo/base/exact_cast.h
@@ -41,7 +41,7 @@ namespace mongo {
template <class DerivedPtr, class Base>
auto exact_pointer_cast(Base* b) -> DerivedPtr {
static_assert(std::is_pointer<DerivedPtr>::value);
- using Derived = typename std::remove_pointer<DerivedPtr>::type;
+ using Derived = typename std::remove_cv<typename std::remove_pointer<DerivedPtr>::type>::type;
static_assert(std::is_final<Derived>::value);
if (b == nullptr) {
diff --git a/src/mongo/db/exec/find_projection_executor.cpp b/src/mongo/db/exec/find_projection_executor.cpp
index 098cdb93d71..9ff1bcd4602 100644
--- a/src/mongo/db/exec/find_projection_executor.cpp
+++ b/src/mongo/db/exec/find_projection_executor.cpp
@@ -128,17 +128,19 @@ Value applySliceProjectionHelper(const Document& input,
size_t fieldPathIndex) {
invariant(fieldPathIndex < params.path.getPathLength());
- auto fieldName = params.path.getFieldName(fieldPathIndex);
+ auto fieldName = params.path.getFieldName(fieldPathIndex++);
Value val{input[fieldName]};
switch (val.getType()) {
case BSONType::Array:
- val = (fieldPathIndex + 1 == params.path.getPathLength())
+ val = (fieldPathIndex == params.path.getPathLength())
? sliceArray(val.getArray(), params.skip, params.limit)
- : applySliceProjectionToArray(val.getArray(), params, fieldPathIndex + 1);
+ : applySliceProjectionToArray(val.getArray(), params, fieldPathIndex);
break;
case BSONType::Object:
- val = applySliceProjectionHelper(val.getDocument(), params, fieldPathIndex + 1);
+ if (fieldPathIndex < params.path.getPathLength()) {
+ val = applySliceProjectionHelper(val.getDocument(), params, fieldPathIndex);
+ }
break;
default:
break;
diff --git a/src/mongo/db/exec/find_projection_executor_test.cpp b/src/mongo/db/exec/find_projection_executor_test.cpp
index 2bff975ed8c..39194fbdd93 100644
--- a/src/mongo/db/exec/find_projection_executor_test.cpp
+++ b/src/mongo/db/exec/find_projection_executor_test.cpp
@@ -270,6 +270,10 @@ TEST(SliceProjection, CorrectlyProjectsSimplePath) {
doc = Document{fromjson("{a: 1}")};
ASSERT_DOCUMENT_EQ(Document{fromjson("{a: 1}")},
applySliceProjection(doc, "a", boost::none, 2));
+
+ doc = Document{fromjson("{a: {b: 1}}")};
+ ASSERT_DOCUMENT_EQ(Document{fromjson("{a: {b: 1}}")},
+ applySliceProjection(doc, "a", boost::none, 2));
}
TEST(SliceProjection, CorrectlyProjectsDottedPath) {
@@ -293,6 +297,10 @@ TEST(SliceProjection, CorrectlyProjectsDottedPath) {
ASSERT_DOCUMENT_EQ(Document{fromjson("{a: {b: 1}}")},
applySliceProjection(doc, "a.b", boost::none, 2));
+ doc = Document{fromjson("{a: {b: {c: 1}}}")};
+ ASSERT_DOCUMENT_EQ(Document{fromjson("{a: {b: {c: 1}}}")},
+ applySliceProjection(doc, "a.b", boost::none, 2));
+
doc = Document{fromjson("{a: [{b: [1,2,3], c: 1}]}")};
ASSERT_DOCUMENT_EQ(Document{fromjson("{a: [{b: [3], c: 1}]}")},
applySliceProjection(doc, "a.b", boost::none, -1));
diff --git a/src/mongo/db/exec/projection.cpp b/src/mongo/db/exec/projection.cpp
index f334655562e..5d50aee547c 100644
--- a/src/mongo/db/exec/projection.cpp
+++ b/src/mongo/db/exec/projection.cpp
@@ -50,68 +50,68 @@ static const char* kIdField = "_id";
namespace {
-BSONObj indexKey(const WorkingSetMember& member) {
- return member.metadata().getIndexKey();
+void transitionMemberToOwnedObj(Document&& doc, WorkingSetMember* member) {
+ member->keyData.clear();
+ member->recordId = {};
+ member->doc = {{}, std::move(doc)};
+ member->transitionToOwnedObj();
}
-BSONObj sortKey(const WorkingSetMember& member) {
- return DocumentMetadataFields::serializeSortKey(member.metadata().isSingleElementKey(),
- member.metadata().getSortKey());
+void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
+ transitionMemberToOwnedObj(Document{bo}, member);
}
-double geoDistance(const WorkingSetMember& member) {
- return member.metadata().getGeoNearDistance();
+/**
+ * Moves document metadata fields from the WSM into the given document 'doc', and returns the same
+ * document but with populated metadata.
+ */
+auto attachMetadataToDocument(Document&& doc, WorkingSetMember* member) {
+ MutableDocument md{std::move(doc)};
+ md.setMetadata(member->releaseMetadata());
+ return md.freeze();
}
-Value geoPoint(const WorkingSetMember& member) {
- return member.metadata().getGeoNearPoint();
+/**
+ * Moves document metadata fields from the document 'doc' into the WSM, and returns the same
+ * document but without metadata.
+ */
+auto attachMetadataToWorkingSetMember(Document&& doc, WorkingSetMember* member) {
+ MutableDocument md{std::move(doc)};
+ member->setMetadata(md.releaseMetadata());
+ return md.freeze();
}
-double textScore(const WorkingSetMember& member) {
- auto&& metadata = member.metadata();
- if (metadata.hasTextScore()) {
- return metadata.getTextScore();
- } else {
- // It is permitted to request a text score when none has been computed. Zero is returned as
- // an empty value in this case.
- return 0.0;
+/**
+ * Given an index key 'dehyratedKey' with no field names, returns a new Document representing the
+ * index key after adding field names according to 'keyPattern'.
+ *
+ * For example, given:
+ * - the 'keyPatern' of {'a.b': 1, c: 1}
+ * - the 'dehydratedKey' of {'': 'abc', '': 10}
+ *
+ * The resulting document will be: {a: {b: 'abc'}, c: 10}
+ */
+auto rehydrateIndexKey(const BSONObj& keyPattern, const BSONObj& dehydratedKey) {
+ MutableDocument md;
+ BSONObjIterator keyIter{keyPattern};
+ BSONObjIterator valueIter{dehydratedKey};
+
+ while (keyIter.more() && valueIter.more()) {
+ auto fieldName = keyIter.next().fieldNameStringData();
+ auto value = valueIter.next();
+
+ // Skip the $** index virtual field, as it's not part of the actual index key.
+ if (fieldName == "$_path") {
+ continue;
+ }
+
+ md.setNestedField(fieldName, Value{value});
}
-}
-void transitionMemberToOwnedObj(const BSONObj& bo, WorkingSetMember* member) {
- member->keyData.clear();
- member->recordId = RecordId();
- member->resetDocument(SnapshotId(), bo);
- member->transitionToOwnedObj();
-}
+ invariant(!keyIter.more());
+ invariant(!valueIter.more());
-StatusWith<BSONObj> provideMetaFieldsAndPerformExec(const ProjectionExec& exec,
- const WorkingSetMember& member) {
- if (exec.needsGeoNearDistance() && !member.metadata().hasGeoNearDistance())
- return Status(ErrorCodes::InternalError, "near loc dist requested but no data available");
-
- if (exec.needsGeoNearPoint() && !member.metadata().hasGeoNearPoint())
- return Status(ErrorCodes::InternalError, "near loc proj requested but no data available");
-
- return member.hasObj()
- ? exec.project(member.doc.value().toBson(),
- exec.needsGeoNearDistance()
- ? boost::optional<const double>(geoDistance(member))
- : boost::none,
- exec.needsGeoNearPoint() ? geoPoint(member) : Value{},
- exec.needsSortKey() ? sortKey(member) : BSONObj(),
- exec.needsTextScore() ? boost::optional<const double>(textScore(member))
- : boost::none,
- member.recordId.repr())
- : exec.projectCovered(
- member.keyData,
- exec.needsGeoNearDistance() ? boost::optional<const double>(geoDistance(member))
- : boost::none,
- exec.needsGeoNearPoint() ? geoPoint(member) : Value{},
- exec.needsSortKey() ? sortKey(member) : BSONObj(),
- exec.needsTextScore() ? boost::optional<const double>(textScore(member))
- : boost::none,
- member.recordId.repr());
+ return md.freeze();
}
} // namespace
@@ -191,26 +191,52 @@ std::unique_ptr<PlanStageStats> ProjectionStage::getStats() {
return ret;
}
-ProjectionStageDefault::ProjectionStageDefault(OperationContext* opCtx,
+ProjectionStageDefault::ProjectionStageDefault(boost::intrusive_ptr<ExpressionContext> expCtx,
const BSONObj& projObj,
+ const projection_ast::Projection* projection,
WorkingSet* ws,
- std::unique_ptr<PlanStage> child,
- const MatchExpression& fullExpression,
- const CollatorInterface* collator)
- : ProjectionStage(opCtx, projObj, ws, std::move(child), "PROJECTION_DEFAULT"),
- _exec(opCtx, projObj, &fullExpression, collator) {}
+ std::unique_ptr<PlanStage> child)
+ : ProjectionStage{expCtx->opCtx, projObj, ws, std::move(child), "PROJECTION_DEFAULT"},
+ _wantRecordId{projection->metadataDeps()[DocumentMetadataFields::kRecordId]},
+ _projectType{projection->type()},
+ _executor{projection_executor::buildProjectionExecutor(expCtx, projection, {})} {}
Status ProjectionStageDefault::transform(WorkingSetMember* member) const {
- // The default no-fast-path case.
- if (_exec.needsSortKey() && !member->metadata().hasSortKey())
- return Status(ErrorCodes::InternalError,
- "sortKey meta-projection requested but no data available");
+ Document input;
+
+ // Most metadata should have already been stored within the WSM when we project out a document.
+ // The recordId metadata is different though, because it's a fundamental part of the WSM and
+ // we store it within the WSM itself rather than WSM metadata, so we need to transfer it into
+ // the metadata object if the projection has a recordId $meta expression.
+ if (_wantRecordId && !member->metadata().hasRecordId()) {
+ member->metadata().setRecordId(member->recordId);
+ }
- auto projected = provideMetaFieldsAndPerformExec(_exec, *member);
- if (!projected.isOK())
- return projected.getStatus();
+ if (member->hasObj()) {
+ input = std::move(member->doc.value());
+ } else {
+ // We have a covered projection, which is only supported in inclusion mode.
+ invariant(_projectType == projection_ast::ProjectType::kInclusion);
+ // We're pulling data from an index key, so there must be exactly one key entry in the WSM
+ // as the planner guarantees that it will never generate a covered plan in the case of index
+ // intersection.
+ invariant(member->keyData.size() == 1);
+
+ // For covered projection we will rehydrate in index key into a Document and then pass it
+ // through the projection executor to include only required fields, including metadata
+ // fields.
+ input = rehydrateIndexKey(member->keyData[0].indexKeyPattern, member->keyData[0].keyData);
+ }
- transitionMemberToOwnedObj(projected.getValue(), member);
+ // Before applying the projection we will move document metadata from the WSM into the document
+ // itself, in case the projection contains $meta expressions and needs this data, and will move
+ // it back to the WSM once the projection has been applied.
+ auto projected = attachMetadataToWorkingSetMember(
+ _executor->applyTransformation(attachMetadataToDocument(std::move(input), member)), member);
+ // An exclusion projection can return an unowned object since the output document is
+ // constructed from the input one backed by BSON which is owned by the storage system, so we
+ // need to make sure we transition an owned document.
+ transitionMemberToOwnedObj(projected.getOwned(), member);
return Status::OK();
}
diff --git a/src/mongo/db/exec/projection.h b/src/mongo/db/exec/projection.h
index e4ad2ef8af6..5b368750cad 100644
--- a/src/mongo/db/exec/projection.h
+++ b/src/mongo/db/exec/projection.h
@@ -30,15 +30,13 @@
#pragma once
#include "mongo/db/exec/plan_stage.h"
-#include "mongo/db/exec/projection_exec.h"
+#include "mongo/db/exec/projection_executor.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression.h"
+#include "mongo/db/query/projection_ast.h"
#include "mongo/db/record_id.h"
namespace mongo {
-
-class CollatorInterface;
-
/**
* This stage computes a projection. This is an abstract base class for various projection
* implementations.
@@ -101,12 +99,11 @@ public:
/**
* ProjectionNodeDefault should use this for construction.
*/
- ProjectionStageDefault(OperationContext* opCtx,
+ ProjectionStageDefault(boost::intrusive_ptr<ExpressionContext> expCtx,
const BSONObj& projObj,
+ const projection_ast::Projection* projection,
WorkingSet* ws,
- std::unique_ptr<PlanStage> child,
- const MatchExpression& fullExpression,
- const CollatorInterface* collator);
+ std::unique_ptr<PlanStage> child);
StageType stageType() const final {
return STAGE_PROJECTION_DEFAULT;
@@ -115,8 +112,10 @@ public:
private:
Status transform(WorkingSetMember* member) const final;
- // Fully-general heavy execution object.
- ProjectionExec _exec;
+ // True, if the projection contains a recordId $meta expression.
+ const bool _wantRecordId;
+ const projection_ast::ProjectType _projectType;
+ std::unique_ptr<parsed_aggregation_projection::ParsedAggregationProjection> _executor;
};
/**
diff --git a/src/mongo/db/exec/projection_executor.cpp b/src/mongo/db/exec/projection_executor.cpp
index 91c56032cd6..29192f19547 100644
--- a/src/mongo/db/exec/projection_executor.cpp
+++ b/src/mongo/db/exec/projection_executor.cpp
@@ -97,7 +97,7 @@ auto makeProjectionPostImageExpression(const ProjectionExecutorVisitorData<Execu
* a field path expressions will be created to access a projection post-image document.
*/
template <typename Executor>
-auto createFindPositionalExpression(projection_ast::ProjectionPositionalASTNode* node,
+auto createFindPositionalExpression(const projection_ast::ProjectionPositionalASTNode* node,
const ProjectionExecutorVisitorData<Executor>& data,
const FieldPath& path) {
invariant(node);
@@ -123,7 +123,7 @@ auto createFindPositionalExpression(projection_ast::ProjectionPositionalASTNode*
* a field path expressions will be created to access a projection post-image document.
*/
template <typename Executor>
-auto createFindSliceExpression(projection_ast::ProjectionSliceASTNode* node,
+auto createFindSliceExpression(const projection_ast::ProjectionSliceASTNode* node,
const ProjectionExecutorVisitorData<Executor>& data,
const FieldPath& path) {
invariant(node);
@@ -137,7 +137,7 @@ auto createFindSliceExpression(projection_ast::ProjectionSliceASTNode* node,
* 'path' on the input document.
*/
template <typename Executor>
-auto createFindElemMatchExpression(projection_ast::ProjectionElemMatchASTNode* node,
+auto createFindElemMatchExpression(const projection_ast::ProjectionElemMatchASTNode* node,
const ProjectionExecutorVisitorData<Executor>& data,
const FieldPath& path) {
invariant(node);
@@ -166,14 +166,14 @@ auto createFindElemMatchExpression(projection_ast::ProjectionElemMatchASTNode* n
* 'PathTrackingVisitorContext'.
*/
template <typename Executor>
-class ProjectionExecutorVisitor final : public projection_ast::ProjectionASTVisitor {
+class ProjectionExecutorVisitor final : public projection_ast::ProjectionASTConstVisitor {
public:
ProjectionExecutorVisitor(ProjectionExecutorVisitorContext<Executor>* context)
: _context{context} {
invariant(_context);
}
- void visit(projection_ast::ProjectionPositionalASTNode* node) final {
+ void visit(const projection_ast::ProjectionPositionalASTNode* node) final {
constexpr auto isInclusion = std::is_same_v<Executor, ParsedInclusionProjection>;
invariant(isInclusion);
@@ -184,7 +184,7 @@ public:
userData.setRootReplacementExpression(createFindPositionalExpression(node, userData, path));
}
- void visit(projection_ast::ProjectionSliceASTNode* node) final {
+ void visit(const projection_ast::ProjectionSliceASTNode* node) final {
const auto& path = _context->fullPath();
auto& userData = _context->data();
@@ -198,7 +198,7 @@ public:
userData.setRootReplacementExpression(createFindSliceExpression(node, userData, path));
}
- void visit(projection_ast::ProjectionElemMatchASTNode* node) final {
+ void visit(const projection_ast::ProjectionElemMatchASTNode* node) final {
const auto& path = _context->fullPath();
const auto& userData = _context->data();
@@ -206,14 +206,14 @@ public:
path.fullPath(), createFindElemMatchExpression(node, userData, path));
}
- void visit(projection_ast::ExpressionASTNode* node) final {
+ void visit(const projection_ast::ExpressionASTNode* node) final {
const auto& path = _context->fullPath();
const auto& userData = _context->data();
userData.rootNode()->addExpressionForPath(path.fullPath(), node->expression());
}
- void visit(projection_ast::BooleanConstantASTNode* node) final {
+ void visit(const projection_ast::BooleanConstantASTNode* node) final {
const auto& path = _context->fullPath();
const auto& userData = _context->data();
@@ -231,8 +231,8 @@ public:
userData.rootNode()->addProjectionForPath(path.fullPath());
}
- void visit(projection_ast::ProjectionPathASTNode* node) final {}
- void visit(projection_ast::MatchExpressionASTNode* node) final {}
+ void visit(const projection_ast::ProjectionPathASTNode* node) final {}
+ void visit(const projection_ast::MatchExpressionASTNode* node) final {}
private:
ProjectionExecutorVisitorContext<Executor>* _context;
@@ -245,7 +245,7 @@ private:
*/
template <typename Executor>
auto buildProjectionExecutor(boost::intrusive_ptr<ExpressionContext> expCtx,
- projection_ast::ProjectionPathASTNode* root,
+ const projection_ast::ProjectionPathASTNode* root,
const ProjectionPolicies policies) {
ProjectionExecutorVisitorContext<Executor> context{
{std::make_unique<Executor>(expCtx, policies), expCtx}};
@@ -258,7 +258,7 @@ auto buildProjectionExecutor(boost::intrusive_ptr<ExpressionContext> expCtx,
std::unique_ptr<ParsedAggregationProjection> buildProjectionExecutor(
boost::intrusive_ptr<ExpressionContext> expCtx,
- projection_ast::Projection* projection,
+ const projection_ast::Projection* projection,
const ProjectionPolicies policies) {
invariant(projection);
diff --git a/src/mongo/db/exec/projection_executor.h b/src/mongo/db/exec/projection_executor.h
index 98e7fe89bfd..e3b7b2b24dc 100644
--- a/src/mongo/db/exec/projection_executor.h
+++ b/src/mongo/db/exec/projection_executor.h
@@ -40,6 +40,6 @@ namespace mongo::projection_executor {
*/
std::unique_ptr<parsed_aggregation_projection::ParsedAggregationProjection> buildProjectionExecutor(
boost::intrusive_ptr<ExpressionContext> expCtx,
- projection_ast::Projection* projection,
+ const projection_ast::Projection* projection,
ProjectionPolicies policies);
} // namespace mongo::projection_executor
diff --git a/src/mongo/db/query/canonical_query.h b/src/mongo/db/query/canonical_query.h
index 39ed4e927f5..317544c4944 100644
--- a/src/mongo/db/query/canonical_query.h
+++ b/src/mongo/db/query/canonical_query.h
@@ -128,6 +128,11 @@ public:
const projection_ast::Projection* getProj() const {
return _proj.get_ptr();
}
+
+ projection_ast::Projection* getProj() {
+ return _proj.get_ptr();
+ }
+
const CollatorInterface* getCollator() const {
return _collator.get();
}
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 29cc73c097a..dec282568dc 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -417,15 +417,14 @@ StatusWith<PrepareExecutionResult> prepareExecution(OperationContext* opCtx,
// Stuff the right data into the params depending on what proj impl we use.
if (!canonicalQuery->getProj()->isSimple()) {
root = std::make_unique<ProjectionStageDefault>(
- opCtx,
- canonicalQuery->getProj()->getProjObj(),
+ canonicalQuery->getExpCtx(),
+ canonicalQuery->getQueryRequest().getProj(),
+ canonicalQuery->getProj(),
ws,
- std::move(root),
- *canonicalQuery->root(),
- canonicalQuery->getCollator());
+ std::move(root));
} else {
root = std::make_unique<ProjectionStageSimple>(
- opCtx, canonicalQuery->getProj()->getProjObj(), ws, std::move(root));
+ opCtx, canonicalQuery->getQueryRequest().getProj(), ws, std::move(root));
}
}
@@ -683,12 +682,8 @@ StatusWith<unique_ptr<PlanStage>> applyProjection(OperationContext* opCtx,
"Cannot use a $meta sortKey projection in findAndModify commands."};
}
- return {std::make_unique<ProjectionStageDefault>(opCtx,
- projObj,
- ws,
- std::unique_ptr<PlanStage>(root.release()),
- *cq->root(),
- cq->getCollator())};
+ return {std::make_unique<ProjectionStageDefault>(
+ cq->getExpCtx(), projObj, &proj, ws, std::unique_ptr<PlanStage>(root.release()))};
}
} // namespace
diff --git a/src/mongo/db/query/projection.cpp b/src/mongo/db/query/projection.cpp
index d2a4ee89264..cd6f83091b2 100644
--- a/src/mongo/db/query/projection.cpp
+++ b/src/mongo/db/query/projection.cpp
@@ -58,32 +58,32 @@ struct DepsAnalysisData {
* Does "broad" analysis on the projection, about whether the entire document, or details from the
* match expression are needed and so on.
*/
-class ProjectionAnalysisVisitor final : public ProjectionASTVisitor {
+class ProjectionAnalysisVisitor final : public ProjectionASTConstVisitor {
public:
ProjectionAnalysisVisitor(ProjectionDependencies* deps) : _deps(deps) {
invariant(_deps);
}
- void visit(ProjectionPathASTNode* node) final {
+ void visit(const ProjectionPathASTNode* node) final {
if (node->parent()) {
_deps->hasDottedPath = true;
}
}
- void visit(ProjectionPositionalASTNode* node) final {
+ void visit(const ProjectionPositionalASTNode* node) final {
_deps->requiresMatchDetails = true;
_deps->requiresDocument = true;
}
- void visit(ProjectionSliceASTNode* node) final {
+ void visit(const ProjectionSliceASTNode* node) final {
_deps->requiresDocument = true;
}
- void visit(ProjectionElemMatchASTNode* node) final {
+ void visit(const ProjectionElemMatchASTNode* node) final {
_deps->requiresDocument = true;
}
- void visit(ExpressionASTNode* node) final {
+ void visit(const ExpressionASTNode* node) final {
const Expression* expr = node->expressionRaw();
const ExpressionMeta* meta = dynamic_cast<const ExpressionMeta*>(expr);
@@ -94,8 +94,8 @@ public:
}
}
- void visit(BooleanConstantASTNode* node) final {}
- void visit(MatchExpressionASTNode* node) final {}
+ void visit(const BooleanConstantASTNode* node) final {}
+ void visit(const MatchExpressionASTNode* node) final {}
private:
ProjectionDependencies* _deps;
@@ -108,33 +108,33 @@ private:
* 'PathTrackingWalker' which will help to maintain the current path via
* 'PathTrackingVisitorContext'.
*/
-class DepsAnalysisVisitor final : public ProjectionASTVisitor {
+class DepsAnalysisVisitor final : public ProjectionASTConstVisitor {
public:
DepsAnalysisVisitor(PathTrackingVisitorContext<DepsAnalysisData>* context) : _context{context} {
invariant(_context);
}
- void visit(MatchExpressionASTNode* node) final {
+ void visit(const MatchExpressionASTNode* node) final {
node->matchExpression()->addDependencies(&_context->data().fieldDependencyTracker);
}
- void visit(ProjectionPositionalASTNode* node) final {
+ void visit(const ProjectionPositionalASTNode* node) final {
// Positional projection on a.b.c.$ may actually modify a, a.b, a.b.c, etc.
// Treat the top-level field as a dependency.
addTopLevelPathAsDependency();
}
- void visit(ProjectionSliceASTNode* node) final {
+ void visit(const ProjectionSliceASTNode* node) final {
// find() $slice on a.b.c may modify a, a.b, and a.b.c if they're all arrays.
// Treat the top-level field as a dependency.
addTopLevelPathAsDependency();
}
- void visit(ProjectionElemMatchASTNode* node) final {
+ void visit(const ProjectionElemMatchASTNode* node) final {
addFullPathAsDependency();
}
- void visit(ExpressionASTNode* node) final {
+ void visit(const ExpressionASTNode* node) final {
// The output of an expression on a dotted path depends on whether that field is an array.
invariant(node->parent());
if (!node->parent()->isRoot()) {
@@ -144,14 +144,14 @@ public:
node->expression()->addDependencies(&_context->data().fieldDependencyTracker);
}
- void visit(BooleanConstantASTNode* node) final {
+ void visit(const BooleanConstantASTNode* node) final {
// For inclusions, we depend on the field.
if (node->value()) {
addFullPathAsDependency();
}
}
- void visit(ProjectionPathASTNode* node) final {}
+ void visit(const ProjectionPathASTNode* node) final {}
private:
void addTopLevelPathAsDependency() {
@@ -169,7 +169,7 @@ private:
PathTrackingVisitorContext<DepsAnalysisData>* _context;
};
-auto analyzeProjection(ProjectionPathASTNode* root, ProjectType type) {
+auto analyzeProjection(const ProjectionPathASTNode* root, ProjectType type) {
ProjectionDependencies deps;
PathTrackingVisitorContext<DepsAnalysisData> context;
DepsAnalysisVisitor depsAnalysisVisitor{&context};
@@ -194,8 +194,8 @@ auto analyzeProjection(ProjectionPathASTNode* root, ProjectType type) {
} // namespace
-Projection::Projection(ProjectionPathASTNode root, ProjectType type, const BSONObj& bson)
- : _root(std::move(root)), _type(type), _deps(analyzeProjection(&_root, type)), _bson(bson) {}
+Projection::Projection(ProjectionPathASTNode root, ProjectType type)
+ : _root(std::move(root)), _type(type), _deps(analyzeProjection(&_root, type)) {}
namespace {
diff --git a/src/mongo/db/query/projection.h b/src/mongo/db/query/projection.h
index 91afe2b931f..486f9ed9abf 100644
--- a/src/mongo/db/query/projection.h
+++ b/src/mongo/db/query/projection.h
@@ -59,7 +59,11 @@ struct ProjectionDependencies {
enum class ProjectType { kInclusion, kExclusion };
class Projection {
public:
- Projection(ProjectionPathASTNode root, ProjectType type, const BSONObj& bson);
+ Projection(ProjectionPathASTNode root, ProjectType type);
+
+ const ProjectionPathASTNode* root() const {
+ return &_root;
+ }
ProjectionPathASTNode* root() {
return &_root;
@@ -105,15 +109,6 @@ public:
bool isFieldRetainedExactly(StringData path);
/**
- * TODO SERVER-42423: Delete this method and _bson.
- *
- * This method is deprecated and new call sites should not be added.
- */
- BSONObj getProjObj() const {
- return _bson;
- }
-
- /**
* A projection is considered "simple" if it doesn't require the full document, operates only
* on top-level fields, has no positional projection, and doesn't require the sort key.
*/
@@ -125,11 +120,7 @@ public:
private:
ProjectionPathASTNode _root;
ProjectType _type;
-
ProjectionDependencies _deps;
-
- // Do NOT add new usages of this.
- BSONObj _bson;
};
} // namespace projection_ast
diff --git a/src/mongo/db/query/projection_ast.h b/src/mongo/db/query/projection_ast.h
index 2c136ba0a00..8a279fbbf8c 100644
--- a/src/mongo/db/query/projection_ast.h
+++ b/src/mongo/db/query/projection_ast.h
@@ -67,7 +67,8 @@ public:
virtual std::unique_ptr<ASTNode> clone() const = 0;
- virtual void acceptVisitor(ProjectionASTVisitor* visitor) = 0;
+ virtual void acceptVisitor(ProjectionASTMutableVisitor* visitor) = 0;
+ virtual void acceptVisitor(ProjectionASTConstVisitor* visitor) const = 0;
const ASTNodeVector& children() const {
return _children;
@@ -103,7 +104,11 @@ public:
return std::make_unique<MatchExpressionASTNode>(*this);
}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -124,7 +129,11 @@ public:
invariant(_children.size() == _fieldNames.size());
}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -163,7 +172,11 @@ public:
addChildToInternalVector(std::move(child));
}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -176,7 +189,11 @@ class ProjectionSliceASTNode final : public ASTNode {
public:
ProjectionSliceASTNode(boost::optional<int> skip, int limit) : _skip(skip), _limit(limit) {}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -204,7 +221,11 @@ public:
addChildToInternalVector(std::move(child));
}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -228,7 +249,11 @@ public:
_expr = clonedExpr;
}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
@@ -252,7 +277,11 @@ class BooleanConstantASTNode final : public ASTNode {
public:
BooleanConstantASTNode(bool val) : _val(val) {}
- void acceptVisitor(ProjectionASTVisitor* visitor) override {
+ void acceptVisitor(ProjectionASTMutableVisitor* visitor) override {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(ProjectionASTConstVisitor* visitor) const override {
visitor->visit(this);
}
diff --git a/src/mongo/db/query/projection_ast_path_tracking_visitor.h b/src/mongo/db/query/projection_ast_path_tracking_visitor.h
index 2108233ac8e..affc3732dd6 100644
--- a/src/mongo/db/query/projection_ast_path_tracking_visitor.h
+++ b/src/mongo/db/query/projection_ast_path_tracking_visitor.h
@@ -106,14 +106,14 @@ namespace {
* This is intended to be used with the 'ProjectionPathTrackingWalker' only to correctly maintain
* the state about the current path being visited.
*/
-template <class UserData = PathTrackingDummyDefaultType>
-class PathTrackingPreVisitor final : public ProjectionASTVisitor {
+template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
+class PathTrackingPreVisitor final : public ProjectionASTVisitor<IsConst> {
public:
PathTrackingPreVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
invariant(_context);
}
- void visit(ProjectionPathASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {
if (node->parent()) {
_context->setBasePath(_context->fullPath());
_context->popFrontFieldName();
@@ -122,12 +122,12 @@ public:
_context->pushFieldNames({node->fieldNames().begin(), node->fieldNames().end()});
}
- void visit(MatchExpressionASTNode* node) final {}
- void visit(ProjectionPositionalASTNode* node) final {}
- void visit(ProjectionSliceASTNode* node) final {}
- void visit(ProjectionElemMatchASTNode* node) final {}
- void visit(ExpressionASTNode* node) final {}
- void visit(BooleanConstantASTNode* node) final {}
+ void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) final {}
+ void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) final {}
+ void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) final {}
+ void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) final {}
+ void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) final {}
+ void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) final {}
private:
PathTrackingVisitorContext<UserData>* _context;
@@ -139,14 +139,14 @@ private:
* This is intended to be used with the 'PathTrackingWalker' only to correctly maintain the state
* about the current path being visited.
*/
-template <class UserData = PathTrackingDummyDefaultType>
-class PathTrackingPostVisitor final : public ProjectionASTVisitor {
+template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
+class PathTrackingPostVisitor final : public ProjectionASTVisitor<IsConst> {
public:
PathTrackingPostVisitor(PathTrackingVisitorContext<UserData>* context) : _context{context} {
invariant(_context);
}
- void visit(projection_ast::ProjectionPathASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) final {
_context->popFieldNames();
if (_context->basePath()) {
@@ -161,27 +161,27 @@ public:
}
}
- void visit(ProjectionPositionalASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) final {
_context->popFrontFieldName();
}
- void visit(ProjectionSliceASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) final {
_context->popFrontFieldName();
}
- void visit(ProjectionElemMatchASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) final {
_context->popFrontFieldName();
}
- void visit(ExpressionASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) final {
_context->popFrontFieldName();
}
- void visit(BooleanConstantASTNode* node) final {
+ void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) final {
_context->popFrontFieldName();
}
- void visit(MatchExpressionASTNode* node) final {}
+ void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) final {}
private:
PathTrackingVisitorContext<UserData>* _context;
@@ -197,12 +197,12 @@ private:
* The visitors specified in the 'preVisitors' and 'postVisitors' parameters will be visited in
* the same order as they were added to the vector.
*/
-template <class UserData = PathTrackingDummyDefaultType>
+template <class UserData = PathTrackingDummyDefaultType, bool IsConst = true>
class PathTrackingWalker final {
public:
PathTrackingWalker(PathTrackingVisitorContext<UserData>* context,
- std::vector<ProjectionASTVisitor*> preVisitors,
- std::vector<ProjectionASTVisitor*> postVisitors)
+ std::vector<ProjectionASTVisitor<IsConst>*> preVisitors,
+ std::vector<ProjectionASTVisitor<IsConst>*> postVisitors)
: _pathTrackingPreVisitor{context},
_pathTrackingPostVisitor{context},
_preVisitors{std::move(preVisitors)},
@@ -211,25 +211,25 @@ public:
_postVisitors.push_back(&_pathTrackingPostVisitor);
}
- void preVisit(projection_ast::ASTNode* node) {
+ void preVisit(MaybeConstPtr<IsConst, projection_ast::ASTNode> node) {
for (auto visitor : _preVisitors) {
node->acceptVisitor(visitor);
}
}
- void postVisit(projection_ast::ASTNode* node) {
+ void postVisit(MaybeConstPtr<IsConst, projection_ast::ASTNode> node) {
for (auto visitor : _postVisitors) {
node->acceptVisitor(visitor);
}
}
- void inVisit(long count, projection_ast::ASTNode* node) {}
+ void inVisit(long count, MaybeConstPtr<IsConst, ASTNode> node) {}
private:
- PathTrackingPreVisitor<UserData> _pathTrackingPreVisitor;
- PathTrackingPostVisitor<UserData> _pathTrackingPostVisitor;
- std::vector<ProjectionASTVisitor*> _preVisitors;
- std::vector<ProjectionASTVisitor*> _postVisitors;
+ PathTrackingPreVisitor<UserData, IsConst> _pathTrackingPreVisitor;
+ PathTrackingPostVisitor<UserData, IsConst> _pathTrackingPostVisitor;
+ std::vector<ProjectionASTVisitor<IsConst>*> _preVisitors;
+ std::vector<ProjectionASTVisitor<IsConst>*> _postVisitors;
};
} // namespace projection_ast
} // namespace mongo
diff --git a/src/mongo/db/query/projection_ast_util.cpp b/src/mongo/db/query/projection_ast_util.cpp
index f2b4c2aa1ee..1d31ddef614 100644
--- a/src/mongo/db/query/projection_ast_util.cpp
+++ b/src/mongo/db/query/projection_ast_util.cpp
@@ -44,16 +44,17 @@ struct BSONVisitorContext {
}
};
-class BSONPreVisitor : public ProjectionASTVisitor {
+class BSONPreVisitor : public ProjectionASTConstVisitor {
public:
BSONPreVisitor(BSONVisitorContext* context) : _context(context) {}
- virtual void visit(MatchExpressionASTNode* node) {
+ virtual void visit(const MatchExpressionASTNode* node) {
static_cast<const MatchExpressionASTNode*>(node)->matchExpression()->serialize(
&_context->builder());
_context->fieldNames.top().pop_front();
}
- virtual void visit(ProjectionPathASTNode* node) {
+
+ virtual void visit(const ProjectionPathASTNode* node) {
if (!node->parent()) {
// No root of the tree, thus this node has no field name.
_context->builders.push(BSONObjBuilder());
@@ -65,7 +66,8 @@ public:
_context->fieldNames.push(
std::list<std::string>(node->fieldNames().begin(), node->fieldNames().end()));
}
- virtual void visit(ProjectionPositionalASTNode* node) {
+
+ virtual void visit(const ProjectionPositionalASTNode* node) {
// ProjectionPositional always has the original query's match expression node as its
// child. Serialize as: {"positional.projection.field.$": <original match expression>}.
_context->builders.push(_context->builder().subobjStart(getFieldName() + ".$"));
@@ -74,7 +76,8 @@ public:
// been put on the stack (just like every other node), and will pop it.
_context->fieldNames.push({"<dummy>"});
}
- virtual void visit(ProjectionSliceASTNode* node) {
+
+ virtual void visit(const ProjectionSliceASTNode* node) {
BSONObjBuilder sub(_context->builder().subobjStart(getFieldName()));
if (node->skip()) {
sub.appendArray("$slice", BSON_ARRAY(*node->skip() << node->limit()));
@@ -82,13 +85,16 @@ public:
sub.appendNumber("$slice", node->limit());
}
}
- virtual void visit(ProjectionElemMatchASTNode* node) {
+
+ virtual void visit(const ProjectionElemMatchASTNode* node) {
// Defer to the child, match expression node.
}
- virtual void visit(ExpressionASTNode* node) {
+
+ virtual void visit(const ExpressionASTNode* node) {
node->expression()->serialize(false).addToBsonObj(&_context->builder(), getFieldName());
}
- virtual void visit(BooleanConstantASTNode* node) {
+
+ virtual void visit(const BooleanConstantASTNode* node) {
_context->builders.top().append(getFieldName(), node->value());
}
@@ -104,12 +110,11 @@ private:
BSONVisitorContext* _context;
};
-class BSONPostVisitor : public ProjectionASTVisitor {
+class BSONPostVisitor : public ProjectionASTConstVisitor {
public:
BSONPostVisitor(BSONVisitorContext* context) : _context(context) {}
- virtual void visit(MatchExpressionASTNode* node) {}
- virtual void visit(ProjectionPathASTNode* node) {
+ virtual void visit(const ProjectionPathASTNode* node) {
// Don't pop the top builder.
if (node->parent()) {
// Pop the BSONObjBuilder that was added in the pre visitor.
@@ -120,15 +125,17 @@ public:
invariant(_context->fieldNames.top().empty());
_context->fieldNames.pop();
}
- virtual void visit(ProjectionPositionalASTNode* node) {
+
+ virtual void visit(const ProjectionPositionalASTNode* node) {
_context->builders.pop();
_context->fieldNames.pop();
}
- virtual void visit(ProjectionSliceASTNode* node) {}
- virtual void visit(ProjectionElemMatchASTNode* node) {}
- virtual void visit(ExpressionASTNode* node) {}
- virtual void visit(BooleanConstantASTNode* node) {}
+ virtual void visit(const MatchExpressionASTNode* node) {}
+ virtual void visit(const ProjectionSliceASTNode* node) {}
+ virtual void visit(const ProjectionElemMatchASTNode* node) {}
+ virtual void visit(const ExpressionASTNode* node) {}
+ virtual void visit(const BooleanConstantASTNode* node) {}
private:
BSONVisitorContext* _context;
@@ -138,15 +145,15 @@ class BSONWalker {
public:
BSONWalker() : _preVisitor(&_context), _postVisitor(&_context) {}
- void preVisit(ASTNode* node) {
+ void preVisit(const ASTNode* node) {
node->acceptVisitor(&_preVisitor);
}
- void postVisit(ASTNode* node) {
+ void postVisit(const ASTNode* node) {
node->acceptVisitor(&_postVisitor);
}
- void inVisit(long count, ASTNode* node) {
+ void inVisit(long count, const ASTNode* node) {
// No op.
}
@@ -165,7 +172,7 @@ private:
};
} // namespace
-BSONObj astToDebugBSON(ASTNode* root) {
+BSONObj astToDebugBSON(const ASTNode* root) {
BSONWalker walker;
projection_ast_walker::walk(&walker, root);
diff --git a/src/mongo/db/query/projection_ast_util.h b/src/mongo/db/query/projection_ast_util.h
index 231f037b48c..af89254a9a1 100644
--- a/src/mongo/db/query/projection_ast_util.h
+++ b/src/mongo/db/query/projection_ast_util.h
@@ -36,6 +36,6 @@ namespace projection_ast {
/**
* This is intended to be used for debug output, not for serialization.
*/
-BSONObj astToDebugBSON(ASTNode* root);
+BSONObj astToDebugBSON(const ASTNode* root);
} // namespace projection_ast
} // namespace mongo
diff --git a/src/mongo/db/query/projection_ast_visitor.h b/src/mongo/db/query/projection_ast_visitor.h
index 39caedac3d8..81cc07bf932 100644
--- a/src/mongo/db/query/projection_ast_visitor.h
+++ b/src/mongo/db/query/projection_ast_visitor.h
@@ -31,7 +31,6 @@
namespace mongo {
namespace projection_ast {
-
class MatchExpressionASTNode;
class ProjectionPathASTNode;
class ProjectionPositionalASTNode;
@@ -41,20 +40,34 @@ class ExpressionASTNode;
class BooleanConstantASTNode;
/**
+ * A template type which resolves to 'const T*' if 'IsConst' argument is 'true', and to 'T*'
+ * otherwise.
+ */
+template <bool IsConst, typename T>
+using MaybeConstPtr = typename std::conditional<IsConst, const T*, T*>::type;
+
+/**
* Visitor pattern for ProjectionAST.
*
* This code is not responsible for traversing the AST, only for performing the double-dispatch.
+ *
+ * If the visitor doesn't intend to modify the AST, then the template argument 'IsConst' should be
+ * set to 'true'. In this case all 'visit()' methods will take a const pointer to a visiting node.
*/
+template <bool IsConst = false>
class ProjectionASTVisitor {
public:
virtual ~ProjectionASTVisitor() = default;
- virtual void visit(MatchExpressionASTNode* node) = 0;
- virtual void visit(ProjectionPathASTNode* node) = 0;
- virtual void visit(ProjectionPositionalASTNode* node) = 0;
- virtual void visit(ProjectionSliceASTNode* node) = 0;
- virtual void visit(ProjectionElemMatchASTNode* node) = 0;
- virtual void visit(ExpressionASTNode* node) = 0;
- virtual void visit(BooleanConstantASTNode* node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, MatchExpressionASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, ProjectionPathASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, ProjectionPositionalASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, ProjectionSliceASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, ProjectionElemMatchASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, ExpressionASTNode> node) = 0;
+ virtual void visit(MaybeConstPtr<IsConst, BooleanConstantASTNode> node) = 0;
};
+
+using ProjectionASTMutableVisitor = ProjectionASTVisitor<false>;
+using ProjectionASTConstVisitor = ProjectionASTVisitor<true>;
} // namespace projection_ast
} // namespace mongo
diff --git a/src/mongo/db/query/projection_ast_walker.h b/src/mongo/db/query/projection_ast_walker.h
index 5d697efee7a..64674dbd3d0 100644
--- a/src/mongo/db/query/projection_ast_walker.h
+++ b/src/mongo/db/query/projection_ast_walker.h
@@ -44,9 +44,12 @@ namespace mongo::projection_ast_walker {
* * walker.postVisit() once after walking to each child.
* Each of the ASTNode's child ASTNode is recursively walked and the same three methods are
* called for it.
+ *
+ * If the caller doesn't intend to modify the AST, then the template argument 'IsConst' should be
+ * set to 'true'. In this case the 'node' pointer will be qualified with 'const'.
*/
-template <typename Walker>
-void walk(Walker* walker, projection_ast::ASTNode* node) {
+template <typename Walker, bool IsConst = true>
+void walk(Walker* walker, projection_ast::MaybeConstPtr<IsConst, projection_ast::ASTNode> node) {
if (node) {
walker->preVisit(node);
@@ -55,7 +58,7 @@ void walk(Walker* walker, projection_ast::ASTNode* node) {
if (count)
walker->inVisit(count, node);
++count;
- walk(walker, child.get());
+ walk<Walker, IsConst>(walker, child.get());
}
walker->postVisit(node);
diff --git a/src/mongo/db/query/projection_parser.cpp b/src/mongo/db/query/projection_parser.cpp
index a64d2e7dbd8..9642b2456d2 100644
--- a/src/mongo/db/query/projection_parser.cpp
+++ b/src/mongo/db/query/projection_parser.cpp
@@ -347,7 +347,7 @@ Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx,
addNodeAtPath(&root, "_id", std::make_unique<BooleanConstantASTNode>(true));
}
- return Projection{std::move(root), *type, obj};
+ return Projection{std::move(root), *type};
}
Projection parse(boost::intrusive_ptr<ExpressionContext> expCtx,
diff --git a/src/mongo/db/query/projection_test.cpp b/src/mongo/db/query/projection_test.cpp
index c0044173cd4..c4180584f2f 100644
--- a/src/mongo/db/query/projection_test.cpp
+++ b/src/mongo/db/query/projection_test.cpp
@@ -229,9 +229,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjection) {
// A projection with just a $meta projection defaults to an exclusion projection.
auto proj = createProjection("{}", "{foo: {$meta: 'sortKey'}}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{foo: {$meta: 'sortKey'}}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
-
ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
@@ -241,10 +239,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjection) {
TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjectionWithOtherFields) {
auto proj = createProjection("{}", "{a: 0, foo: {$meta: 'sortKey'}}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 0, foo: {$meta: 'sortKey'}}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
-
- ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
ASSERT_TRUE(proj.requiresDocument());
@@ -253,9 +248,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInExclusionProjectionWithOtherFie
TEST(QueryProjectionTest, SortKeyMetaProjectionInInclusionProjection) {
auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 1, foo: {$meta: 'sortKey'}}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
-
ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
@@ -265,9 +258,7 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionInInclusionProjection) {
TEST(QueryProjectionTest, SortKeyMetaProjectionDoesNotRequireDocument) {
auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(), fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
-
ASSERT_FALSE(proj.requiresDocument());
ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
@@ -277,11 +268,8 @@ TEST(QueryProjectionTest, SortKeyMetaProjectionDoesNotRequireDocument) {
TEST(QueryProjectionTest, SortKeyMetaAndSlice) {
auto proj = createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$slice: 1}}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(),
- fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$slice: 1}}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
ASSERT_TRUE(proj.requiresDocument());
-
ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
@@ -291,11 +279,8 @@ TEST(QueryProjectionTest, SortKeyMetaAndElemMatch) {
auto proj =
createProjection("{}", "{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$elemMatch: {a: 1}}}");
- ASSERT_BSONOBJ_EQ(proj.getProjObj(),
- fromjson("{a: 1, foo: {$meta: 'sortKey'}, _id: 0, b: {$elemMatch: {a: 1}}}"));
ASSERT_TRUE(proj.metadataDeps()[DocumentMetadataFields::kSortKey]);
ASSERT_TRUE(proj.requiresDocument());
-
ASSERT_FALSE(proj.requiresMatchDetails());
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearDist]);
ASSERT_FALSE(proj.metadataDeps()[DocumentMetadataFields::kGeoNearPoint]);
diff --git a/src/mongo/db/query/query_planner_test_lib.cpp b/src/mongo/db/query/query_planner_test_lib.cpp
index 23287d59225..a08283aa6de 100644
--- a/src/mongo/db/query/query_planner_test_lib.cpp
+++ b/src/mongo/db/query/query_planner_test_lib.cpp
@@ -43,6 +43,8 @@
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/query/collation/collator_factory_mock.h"
+#include "mongo/db/query/projection_ast_util.h"
+#include "mongo/db/query/projection_parser.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_solution.h"
#include "mongo/unittest/unittest.h"
@@ -617,7 +619,13 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln,
return false;
}
- return SimpleBSONObjComparator::kInstance.evaluate(spec.Obj() == pn->proj.getProjObj()) &&
+ // Create an empty/dummy expression context without access to the operation context and
+ // collator. This should be sufficient to parse a projection.
+ auto expCtx = make_intrusive<ExpressionContext>(nullptr, nullptr);
+ auto projection = projection_ast::parse(expCtx, spec.Obj(), {});
+ auto specProjObj = projection_ast::astToDebugBSON(projection.root());
+ auto solnProjObj = projection_ast::astToDebugBSON(pn->proj.root());
+ return SimpleBSONObjComparator::kInstance.evaluate(specProjObj == solnProjObj) &&
solutionMatches(child.Obj(), pn->children[0], relaxBoundsCheck);
} else if (STAGE_SORT == trueSoln->getType()) {
const SortNode* sn = static_cast<const SortNode*>(trueSoln);
diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp
index e09c8c82bd0..245621eb388 100644
--- a/src/mongo/db/query/query_solution.cpp
+++ b/src/mongo/db/query/query_solution.cpp
@@ -42,6 +42,7 @@
#include "mongo/db/query/index_bounds_builder.h"
#include "mongo/db/query/planner_analysis.h"
#include "mongo/db/query/planner_wildcard_helpers.h"
+#include "mongo/db/query/projection_ast_util.h"
#include "mongo/db/query/query_planner_common.h"
namespace mongo {
@@ -878,7 +879,7 @@ void ProjectionNode::appendToString(str::stream* ss, int indent) const {
addIndent(ss, indent);
*ss << "PROJ\n";
addIndent(ss, indent + 1);
- *ss << "proj = " << proj.getProjObj().toString() << '\n';
+ *ss << "proj = " << projection_ast::astToDebugBSON(proj.root()).toString() << '\n';
addIndent(ss, indent + 1);
*ss << "type = " << projectionImplementationTypeToString() << '\n';
addCommon(ss, indent);
diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp
index b70ca49a5a3..a3cc6aee999 100644
--- a/src/mongo/db/query/stage_builder.cpp
+++ b/src/mongo/db/query/stage_builder.cpp
@@ -145,24 +145,26 @@ std::unique_ptr<PlanStage> buildStages(OperationContext* opCtx,
case STAGE_PROJECTION_DEFAULT: {
auto pn = static_cast<const ProjectionNodeDefault*>(root);
auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws);
- return std::make_unique<ProjectionStageDefault>(opCtx,
- pn->proj.getProjObj(),
+ return std::make_unique<ProjectionStageDefault>(cq.getExpCtx(),
+ cq.getQueryRequest().getProj(),
+ cq.getProj(),
ws,
- std::move(childStage),
- pn->fullExpression,
- cq.getCollator());
+ std::move(childStage));
}
case STAGE_PROJECTION_COVERED: {
auto pn = static_cast<const ProjectionNodeCovered*>(root);
auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws);
- return std::make_unique<ProjectionStageCovered>(
- opCtx, pn->proj.getProjObj(), ws, std::move(childStage), pn->coveredKeyObj);
+ return std::make_unique<ProjectionStageCovered>(opCtx,
+ cq.getQueryRequest().getProj(),
+ ws,
+ std::move(childStage),
+ pn->coveredKeyObj);
}
case STAGE_PROJECTION_SIMPLE: {
auto pn = static_cast<const ProjectionNodeSimple*>(root);
auto childStage = buildStages(opCtx, collection, cq, qsol, pn->children[0], ws);
return std::make_unique<ProjectionStageSimple>(
- opCtx, pn->proj.getProjObj(), ws, std::move(childStage));
+ opCtx, cq.getQueryRequest().getProj(), ws, std::move(childStage));
}
case STAGE_LIMIT: {
const LimitNode* ln = static_cast<const LimitNode*>(root);