summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.cpp44
-rw-r--r--src/mongo/db/pipeline/document_source_lookup.h8
-rw-r--r--src/mongo/db/pipeline/pipeline.cpp33
-rw-r--r--src/mongo/db/pipeline/pipeline.h4
4 files changed, 76 insertions, 13 deletions
diff --git a/src/mongo/db/pipeline/document_source_lookup.cpp b/src/mongo/db/pipeline/document_source_lookup.cpp
index 4109334be9a..37f2d4823dd 100644
--- a/src/mongo/db/pipeline/document_source_lookup.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup.cpp
@@ -184,10 +184,14 @@ DocumentSourceLookUp::DocumentSourceLookUp(
// We append an additional BSONObj to '_resolvedPipeline' as a placeholder for the $match stage
// we'll eventually construct from the input document.
_resolvedPipeline.reserve(_resolvedPipeline.size() + 1);
- _resolvedPipeline.push_back(BSON("$match" << BSONObj()));
- _fieldMatchPipelineIdx = _resolvedPipeline.size() - 1;
+ // Initialize the introspection pipeline before we insert the $match. This is okay because we do
+ // not use the introspection pipeline during/after query execution, which is when the $match is
+ // necessary.
initializeResolvedIntrospectionPipeline();
+
+ _resolvedPipeline.push_back(BSON("$match" << BSONObj()));
+ _fieldMatchPipelineIdx = _resolvedPipeline.size() - 1;
}
DocumentSourceLookUp::DocumentSourceLookUp(
@@ -209,8 +213,9 @@ DocumentSourceLookUp::DocumentSourceLookUp(
// Append a BSONObj to '_resolvedPipeline' as a placeholder for the stage corresponding to
// the local/foreignField $match.
_resolvedPipeline.reserve(_resolvedPipeline.size() + 1);
- _resolvedPipeline.push_back(BSON("$match" << BSONObj()));
- _fieldMatchPipelineIdx = _resolvedPipeline.size() - 1;
+ // Save the correct position of the $match, but wait to insert it until we have finished
+ // constructing the pipeline and created the introspection pipeline below.
+ _fieldMatchPipelineIdx = _resolvedPipeline.size();
} else {
// When local/foreignFields are included, we cannot enable the cache because the $match
// is a correlated prefix that will not be detected. Here, local/foreignFields are absent,
@@ -232,7 +237,20 @@ DocumentSourceLookUp::DocumentSourceLookUp(
_variablesParseState.defineVariable(varName));
}
+ // Initialize the introspection pipeline before we insert the $match (if applicable). This is
+ // okay because we only use the introspection pipeline for reference while doing query analysis
+ // and analyzing involved dependencies/variables/collections/constraints. We do not use the
+ // introspection pipeline during/after query execution, which is when the $match is necessary.
+ // It wouldn't hurt anything to include the $match in this pipeline, but we also use the
+ // introspection pipeline in serialization, so it would be a bit odd to include an extra empty
+ // $match.
initializeResolvedIntrospectionPipeline();
+
+ // Finally, insert the $match placeholder if we need it.
+ if (_fieldMatchPipelineIdx) {
+ _resolvedPipeline.insert(_resolvedPipeline.begin() + *_fieldMatchPipelineIdx,
+ BSON("$match" << BSONObj()));
+ }
}
std::unique_ptr<DocumentSourceLookUp::LiteParsed> DocumentSourceLookUp::LiteParsed::parse(
@@ -871,7 +889,9 @@ void DocumentSourceLookUp::appendSpecificExecStats(MutableDocument& doc) const {
}
void DocumentSourceLookUp::serializeToArrayWithBothSyntaxes(
- std::vector<Value>& array, boost::optional<ExplainOptions::Verbosity> explain) const {
+ std::vector<Value>& array,
+ boost::optional<ExplainOptions::Verbosity> explain,
+ bool serializeForQueryAnalysis) const {
// Support alternative $lookup from config.cache.chunks* namespaces.
auto fromValue = (pExpCtx->ns.db() == _fromNs.db())
@@ -888,7 +908,14 @@ void DocumentSourceLookUp::serializeToArrayWithBothSyntaxes(
// Add a pipeline field if only-pipeline syntax was used (to ensure the output is valid $lookup
// syntax) or if a $match was absorbed.
- auto pipeline = _userPipeline.get_value_or(std::vector<BSONObj>());
+ auto pipeline = [&]() -> std::vector<BSONObj> {
+ if (serializeForQueryAnalysis) {
+ // If we are in query analysis, encrypted fields will have been marked in the
+ // introspection pipeline, so we need to serialize that here.
+ return _resolvedIntrospectionPipeline->serializeForQueryAnalysis();
+ }
+ return _userPipeline.get_value_or(std::vector<BSONObj>());
+ }();
if (_additionalFilter) {
pipeline.emplace_back(BSON("$match" << *_additionalFilter));
}
@@ -930,6 +957,11 @@ void DocumentSourceLookUp::serializeToArrayWithBothSyntaxes(
}
}
+void DocumentSourceLookUp::serializeToArrayForQueryAnalysis(std::vector<Value>& array) const {
+ return serializeToArrayWithBothSyntaxes(
+ array, boost::none /* explain */, true /* serializeForQueryAnalysis */);
+}
+
void DocumentSourceLookUp::serializeToArray(
std::vector<Value>& array, boost::optional<ExplainOptions::Verbosity> explain) const {
if (serverGlobalParams.featureCompatibility.isGreaterThanOrEqualTo(
diff --git a/src/mongo/db/pipeline/document_source_lookup.h b/src/mongo/db/pipeline/document_source_lookup.h
index 88e41866dbd..899bed9d622 100644
--- a/src/mongo/db/pipeline/document_source_lookup.h
+++ b/src/mongo/db/pipeline/document_source_lookup.h
@@ -113,7 +113,13 @@ public:
*/
void serializeToArrayWithBothSyntaxes(
std::vector<Value>& array,
- boost::optional<ExplainOptions::Verbosity> explain = boost::none) const;
+ boost::optional<ExplainOptions::Verbosity> explain = boost::none,
+ bool serializeForQueryAnalysis = false) const;
+
+ /**
+ * Serialize the $lookup stage for query analysis using the introspection sub-pipeline.
+ */
+ void serializeToArrayForQueryAnalysis(std::vector<Value>& array) const;
/**
* Returns the 'as' path, and possibly fields modified by an absorbed $unwind.
diff --git a/src/mongo/db/pipeline/pipeline.cpp b/src/mongo/db/pipeline/pipeline.cpp
index 5c62e945e5b..5222a11d300 100644
--- a/src/mongo/db/pipeline/pipeline.cpp
+++ b/src/mongo/db/pipeline/pipeline.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/operation_context.h"
#include "mongo/db/pipeline/accumulator.h"
#include "mongo/db/pipeline/document_source.h"
+#include "mongo/db/pipeline/document_source_lookup.h"
#include "mongo/db/pipeline/document_source_match.h"
#include "mongo/db/pipeline/document_source_merge.h"
#include "mongo/db/pipeline/document_source_out.h"
@@ -111,6 +112,19 @@ void validateTopLevelPipeline(const Pipeline& pipeline) {
}
}
+/**
+ * Convert a vector of Values to BSONObjs.
+ */
+std::vector<BSONObj> convertToBson(const std::vector<Value>& stages) {
+ std::vector<BSONObj> asBson;
+ asBson.reserve(stages.size());
+ for (const auto& stage : stages) {
+ invariant(stage.getType() == BSONType::Object);
+ asBson.push_back(stage.getDocument().toBson());
+ }
+ return asBson;
+}
+
} // namespace
MONGO_FAIL_POINT_DEFINE(disablePipelineOptimization);
@@ -422,13 +436,20 @@ vector<Value> Pipeline::serialize() const {
vector<BSONObj> Pipeline::serializeToBson() const {
const auto serialized = serialize();
- std::vector<BSONObj> asBson;
- asBson.reserve(serialized.size());
- for (auto&& stage : serialized) {
- invariant(stage.getType() == BSONType::Object);
- asBson.push_back(stage.getDocument().toBson());
+ return convertToBson(serialized);
+}
+
+std::vector<BSONObj> Pipeline::serializeForQueryAnalysis() const {
+ std::vector<Value> serializedSources;
+ for (auto&& source : _sources) {
+ // $lookup has a separate serialization path for query analysis.
+ if (auto* lookup = dynamic_cast<DocumentSourceLookUp*>(source.get())) {
+ lookup->serializeToArrayForQueryAnalysis(serializedSources);
+ } else {
+ source->serializeToArray(serializedSources);
+ }
}
- return asBson;
+ return convertToBson(serializedSources);
}
void Pipeline::stitch() {
diff --git a/src/mongo/db/pipeline/pipeline.h b/src/mongo/db/pipeline/pipeline.h
index b6764ff86d1..b0eb8c743f3 100644
--- a/src/mongo/db/pipeline/pipeline.h
+++ b/src/mongo/db/pipeline/pipeline.h
@@ -275,6 +275,10 @@ public:
*/
std::vector<Value> serialize() const;
std::vector<BSONObj> serializeToBson() const;
+ /**
+ * Serialize the pipeline accounting for changes from query analysis.
+ */
+ std::vector<BSONObj> serializeForQueryAnalysis() const;
// The initial source is special since it varies between mongos and mongod.
void addInitialSource(boost::intrusive_ptr<DocumentSource> source);