summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline/document_source.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
commit4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch)
tree1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/pipeline/document_source.cpp
parentaa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff)
parent8f0827553e09872941945a093b647a4211a9db7f (diff)
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0' with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/pipeline/document_source.cpp')
-rw-r--r--src/mongo/db/pipeline/document_source.cpp165
1 files changed, 49 insertions, 116 deletions
diff --git a/src/mongo/db/pipeline/document_source.cpp b/src/mongo/db/pipeline/document_source.cpp
index ea1cf479480..3a5d5d0030c 100644
--- a/src/mongo/db/pipeline/document_source.cpp
+++ b/src/mongo/db/pipeline/document_source.cpp
@@ -36,13 +36,11 @@
#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"
#include "mongo/db/pipeline/document_source_match.h"
#include "mongo/db/pipeline/document_source_project.h"
-#include "mongo/db/pipeline/document_source_redact.h"
#include "mongo/db/pipeline/document_source_replace_root.h"
#include "mongo/db/pipeline/document_source_sample.h"
#include "mongo/db/pipeline/document_source_sequential_document_cache.h"
@@ -172,77 +170,45 @@ 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) {
- 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,
+ 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,
5,
"Swapping all or part of a $match stage in front of another stage: ",
- "matchMovingBefore"_attr = redact(renameableMatchPart->serializeToBSONForDebug()),
+ "matchMovingBefore"_attr = redact(splitMatch.first->serializeToBSONForDebug()),
"thisStage"_attr = redact(serializeToBSONForDebug()),
"matchLeftAfter"_attr = redact(
- 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));
+ 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;
+ }
}
- return true;
+ return false;
}
bool DocumentSource::pushSampleBefore(Pipeline::SourceContainer::iterator itr,
@@ -260,9 +226,7 @@ bool DocumentSource::pushSampleBefore(Pipeline::SourceContainer::iterator itr,
BSONObj DocumentSource::serializeToBSONForDebug() const {
std::vector<Value> serialized;
- auto opts =
- SerializationOptions{boost::make_optional(ExplainOptions::Verbosity::kQueryPlanner)};
- serializeToArray(serialized, opts);
+ serializeToArray(serialized, ExplainOptions::Verbosity::kQueryPlanner);
if (serialized.empty()) {
LOGV2_DEBUG(5943501,
5,
@@ -276,24 +240,21 @@ BSONObj DocumentSource::serializeToBSONForDebug() const {
return serialized[0].getDocument().toBson();
}
-bool DocumentSource::pushSingleDocumentTransformOrRedactBefore(
- Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container) {
- if (constraints().canSwapWithSingleDocTransformOrRedact) {
- auto nextItr = std::next(itr);
- if (dynamic_cast<DocumentSourceSingleDocumentTransformation*>(nextItr->get()) ||
- dynamic_cast<DocumentSourceRedact*>(nextItr->get())) {
- LOGV2_DEBUG(5943500,
- 5,
- "Pushing a single document transform stage or a redact stage in ahead of "
- "the current stage: ",
- "singleDocTransformOrRedactStage"_attr =
- redact((*nextItr)->serializeToBSONForDebug()),
- "currentStage"_attr = redact(serializeToBSONForDebug()));
-
- // Swap 'itr' and 'nextItr' list nodes.
- container->splice(itr, *container, nextItr);
- return true;
- }
+bool DocumentSource::pushSingleDocumentTransformBefore(Pipeline::SourceContainer::iterator itr,
+ Pipeline::SourceContainer* container) {
+ auto singleDocTransform =
+ dynamic_cast<DocumentSourceSingleDocumentTransformation*>((*std::next(itr)).get());
+
+ if (constraints().canSwapWithSingleDocTransform && singleDocTransform) {
+ LOGV2_DEBUG(5943500,
+ 5,
+ "Swapping a single document transform stage in front of another stage: ",
+ "singleDocTransform"_attr =
+ redact(singleDocTransform->serializeToBSONForDebug()),
+ "thisStage"_attr = redact(serializeToBSONForDebug()));
+ container->insert(itr, std::move(singleDocTransform));
+ container->erase(std::next(itr));
+ return true;
}
return false;
}
@@ -313,43 +274,15 @@ Pipeline::SourceContainer::iterator DocumentSource::optimizeAt(
}
void DocumentSource::serializeToArray(vector<Value>& array,
- const SerializationOptions& opts) const {
- Value entry = serialize(opts);
+ boost::optional<ExplainOptions::Verbosity> explain) const {
+ Value entry = serialize(explain);
if (!entry.missing()) {
array.push_back(entry);
}
}
-namespace {
-std::list<boost::intrusive_ptr<DocumentSource>> throwOnParse(
- BSONElement spec, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
- uasserted(6047400, spec.fieldNameStringData() + " stage is only allowed on MongoDB Atlas");
-}
-std::unique_ptr<LiteParsedDocumentSource> throwOnParseLite(NamespaceString nss,
- const BSONElement& spec) {
- uasserted(6047401, spec.fieldNameStringData() + " stage is only allowed on MongoDB Atlas");
-}
-} // namespace
MONGO_INITIALIZER_GROUP(BeginDocumentSourceRegistration,
("default"),
("EndDocumentSourceRegistration"))
-// Any remaining work on the parserMap should be done before finishing DocumentSource Registration.
-MONGO_INITIALIZER_WITH_PREREQUISITES(EndDocumentSourceRegistration,
- ("BeginDocumentSourceRegistration"))
-(InitializerContext*) {
- auto searchStageNames = {
- "$vectorSearch"_sd, "$search"_sd, "$searchMeta"_sd, "$listSearchIndexes"_sd};
- for (auto stageName : searchStageNames) {
- auto searchIt = parserMap.find(stageName);
- // If the stage has not been registered at this point, register a parser that errors
- // with a useful error message on parsing a search stage.
- if (searchIt == parserMap.end()) {
- LiteParsedDocumentSource::registerParser(stageName.toString(),
- throwOnParseLite,
- AllowedWithApiStrict::kAlways,
- AllowedWithClientType::kAny);
- DocumentSource::registerParser(stageName.toString(), throwOnParse, boost::none);
- }
- }
-}
+MONGO_INITIALIZER_GROUP(EndDocumentSourceRegistration, ("BeginDocumentSourceRegistration"), ())
} // namespace mongo