diff options
Diffstat (limited to 'src/mongo')
| -rw-r--r-- | src/mongo/db/pipeline/document_source_lookup.cpp | 44 | ||||
| -rw-r--r-- | src/mongo/db/pipeline/document_source_lookup.h | 8 | ||||
| -rw-r--r-- | src/mongo/db/pipeline/pipeline.cpp | 33 | ||||
| -rw-r--r-- | src/mongo/db/pipeline/pipeline.h | 4 |
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); |
