diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
| commit | 959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch) | |
| tree | acc8d60aedb12b70048e676e8a7349deb0010db8 /src/mongo/db/pipeline/document_source.cpp | |
| parent | 76588293975fc059cf076779e4283e6ffaf8afff (diff) | |
New upstream version 6.0.20upstream
Diffstat (limited to 'src/mongo/db/pipeline/document_source.cpp')
| -rw-r--r-- | src/mongo/db/pipeline/document_source.cpp | 101 |
1 files changed, 68 insertions, 33 deletions
diff --git a/src/mongo/db/pipeline/document_source.cpp b/src/mongo/db/pipeline/document_source.cpp index 55d400af60b..ea1cf479480 100644 --- a/src/mongo/db/pipeline/document_source.cpp +++ b/src/mongo/db/pipeline/document_source.cpp @@ -36,6 +36,7 @@ #include "mongo/db/commands/feature_compatibility_version_documentation.h" #include "mongo/db/exec/document_value/value.h" #include "mongo/db/matcher/expression_algo.h" +#include "mongo/db/pipeline/change_stream_constants.h" #include "mongo/db/pipeline/document_source_add_fields.h" #include "mongo/db/pipeline/document_source_group.h" #include "mongo/db/pipeline/document_source_internal_shard_filter.h" @@ -171,45 +172,77 @@ bool groupMatchSwapVerified(const DocumentSourceMatch& nextMatch, return !expression::hasExistencePredicateOnPath(*(nextMatch.getMatchExpression()), "_id"_sd); } +/** + * Returns 'true' if the given stage is an internal change stream stage that can appear in a router + * (mongoS) pipeline, or 'false' otherwise. + */ +bool isChangeStreamRouterPipelineStage(StringData stageName) { + return change_stream_constants::kChangeStreamRouterPipelineStages.contains(stageName); +} } // namespace bool DocumentSource::pushMatchBefore(Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container) { - auto nextMatch = dynamic_cast<DocumentSourceMatch*>((*std::next(itr)).get()); - auto thisGroup = dynamic_cast<DocumentSourceGroup*>(this); - if (constraints().canSwapWithMatch && nextMatch && !nextMatch->isTextQuery() && - (!thisGroup || groupMatchSwapVerified(*nextMatch, *thisGroup))) { - // We're allowed to swap with a $match and the stage after us is a $match. Furthermore, the - // $match does not contain a text search predicate, which we do not attempt to optimize - // because such a $match must already be the first stage in the pipeline. We can attempt to - // swap the $match or part of the $match before ourselves. - auto splitMatch = - DocumentSourceMatch::splitMatchByModifiedFields(nextMatch, getModifiedPaths()); - invariant(splitMatch.first || splitMatch.second); - - if (splitMatch.first) { - // At least part of the $match can be moved before this stage. Erase the original $match - // and put the independent part before this stage. If splitMatch.second is not null, - // then there is a new $match stage to insert after ourselves which is dependent on the - // modified fields. - LOGV2_DEBUG( - 5943503, + if (!constraints().canSwapWithMatch) { + return false; + } + + auto nextStageAsMatch = dynamic_cast<DocumentSourceMatch*>((*std::next(itr)).get()); + if (!nextStageAsMatch || nextStageAsMatch->isTextQuery()) { + // We do not need to attempt this optimization if the $match contains a text search + // predicate because, in that scenario, $match is already required to be the first stage in + // the pipeline. + return false; + } + + // At this point: + // 1) The next stage after 'this' is $match. + // 2) The $match stage does not contain a text search predicate. + + // TODO SERVER-55492: Remove the following workaround when there are rename checks for 'other' + // match expressions. + if (isChangeStreamRouterPipelineStage(this->getSourceName())) { + // Always move the $match stage ahead of internal change stream stages appearing in the + // router (mongoS) pipeline, because they do not access or modify any paths in the input + // document. + container->splice(itr, *container, std::next(itr)); + return true; + } + + auto thisStageAsGroup = dynamic_cast<DocumentSourceGroup*>(this); + if (thisStageAsGroup && !groupMatchSwapVerified(*nextStageAsMatch, *thisStageAsGroup)) { + return false; + } + + auto [renameableMatchPart, nonRenameableMatchPart] = + DocumentSourceMatch::splitMatchByModifiedFields(nextStageAsMatch, getModifiedPaths()); + invariant(renameableMatchPart || nonRenameableMatchPart); + if (!renameableMatchPart) { + return false; + } + + LOGV2_DEBUG(5943503, 5, "Swapping all or part of a $match stage in front of another stage: ", - "matchMovingBefore"_attr = redact(splitMatch.first->serializeToBSONForDebug()), + "matchMovingBefore"_attr = redact(renameableMatchPart->serializeToBSONForDebug()), "thisStage"_attr = redact(serializeToBSONForDebug()), "matchLeftAfter"_attr = redact( - splitMatch.second ? splitMatch.second->serializeToBSONForDebug() : BSONObj())); - container->erase(std::next(itr)); - container->insert(itr, std::move(splitMatch.first)); - if (splitMatch.second) { - container->insert(std::next(itr), std::move(splitMatch.second)); - } - - return true; - } + nonRenameableMatchPart ? nonRenameableMatchPart->serializeToBSONForDebug() + : BSONObj())); + + // At this point we know that at least part of the $match expression can be moved ahead of + // 'this'. So, we erase the original $match and move that renameable part ahead of 'this' stage. + container->erase(std::next(itr)); + container->insert(itr, std::move(renameableMatchPart)); + + // If 'nonRenameableMatchPart' is not null, the 'renameableMatchPart' of the $match expression + // was only one component of the original $match. So, we need to create a new $match stage for + // the remaining 'nonRenameableMatchPart' and insert it after 'this' - effectively keeping it in + // its original position in the pipeline. + if (nonRenameableMatchPart) { + container->insert(std::next(itr), std::move(nonRenameableMatchPart)); } - return false; + return true; } bool DocumentSource::pushSampleBefore(Pipeline::SourceContainer::iterator itr, @@ -227,7 +260,9 @@ bool DocumentSource::pushSampleBefore(Pipeline::SourceContainer::iterator itr, BSONObj DocumentSource::serializeToBSONForDebug() const { std::vector<Value> serialized; - serializeToArray(serialized, ExplainOptions::Verbosity::kQueryPlanner); + auto opts = + SerializationOptions{boost::make_optional(ExplainOptions::Verbosity::kQueryPlanner)}; + serializeToArray(serialized, opts); if (serialized.empty()) { LOGV2_DEBUG(5943501, 5, @@ -278,8 +313,8 @@ Pipeline::SourceContainer::iterator DocumentSource::optimizeAt( } void DocumentSource::serializeToArray(vector<Value>& array, - boost::optional<ExplainOptions::Verbosity> explain) const { - Value entry = serialize(explain); + const SerializationOptions& opts) const { + Value entry = serialize(opts); if (!entry.missing()) { array.push_back(entry); } |
