summaryrefslogtreecommitdiff
path: root/src/mongo/db/query
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query')
-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
15 files changed, 178 insertions, 139 deletions
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);