diff options
author | Hana Pearlman <hana.pearlman@mongodb.com> | 2021-04-05 17:43:26 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-04-20 12:47:22 +0000 |
commit | b4d072aec9f92014020c275a05860f425a9fc374 (patch) | |
tree | 755116ce296cffb9841a46dd9a227e3ced4f3d80 | |
parent | 6d02b78de375778c860b9e48df998ecdce2630e6 (diff) | |
download | mongo-b4d072aec9f92014020c275a05860f425a9fc374.tar.gz |
SERVER-55668: Allow rewrite to push exclusion meta projections past $unpackBucket
13 files changed, 342 insertions, 9 deletions
diff --git a/src/mongo/db/exec/SConscript b/src/mongo/db/exec/SConscript index 6a1ad9c6fdf..c0265d59013 100644 --- a/src/mongo/db/exec/SConscript +++ b/src/mongo/db/exec/SConscript @@ -86,6 +86,7 @@ env.Library( target='projection_executor', source=[ 'add_fields_projection_executor.cpp', + 'exclusion_projection_executor.cpp', 'inclusion_projection_executor.cpp', 'projection_executor_builder.cpp', 'projection_executor_utils.cpp', diff --git a/src/mongo/db/exec/exclusion_projection_executor.cpp b/src/mongo/db/exec/exclusion_projection_executor.cpp new file mode 100644 index 00000000000..9823ed1b125 --- /dev/null +++ b/src/mongo/db/exec/exclusion_projection_executor.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2021-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/exec/exclusion_projection_executor.h" + +namespace mongo::projection_executor { + +std::pair<BSONObj, bool> ExclusionNode::extractProjectOnFieldAndRename(const StringData& oldName, + const StringData& newName) { + BSONObjBuilder extractedExclusion; + + // Check for a projection directly on 'oldName'. For example, {oldName: 0}. + if (auto it = _projectedFields.find(oldName); it != _projectedFields.end()) { + extractedExclusion.append(newName, false); + _projectedFields.erase(it); + } + + // Check for a projection on subfields of 'oldName'. For example, {oldName: {a: 0, b: 0}}. + if (auto it = _children.find(oldName); it != _children.end()) { + extractedExclusion.append(newName, it->second->serialize(boost::none).toBson()); + _children.erase(it); + } + + if (auto it = std::find(_orderToProcessAdditionsAndChildren.begin(), + _orderToProcessAdditionsAndChildren.end(), + oldName); + it != _orderToProcessAdditionsAndChildren.end()) { + _orderToProcessAdditionsAndChildren.erase(it); + } + + return {extractedExclusion.obj(), _projectedFields.empty() && _children.empty()}; +} +} // namespace mongo::projection_executor diff --git a/src/mongo/db/exec/exclusion_projection_executor.h b/src/mongo/db/exec/exclusion_projection_executor.h index dd4854e39ea..a9c1fade72d 100644 --- a/src/mongo/db/exec/exclusion_projection_executor.h +++ b/src/mongo/db/exec/exclusion_projection_executor.h @@ -32,6 +32,7 @@ #include <memory> #include <string> +#include "mongo/bson/bsonobjbuilder.h" #include "mongo/db/exec/projection_executor.h" #include "mongo/db/exec/projection_node.h" @@ -63,6 +64,14 @@ public: } } + /** + * Removes and returns a BSONObj representing the part of this project that depends only on + * 'oldName'. Also returns a bool indicating whether this entire project is extracted. In the + * extracted $project, 'oldName' is renamed to 'newName'. 'oldName' should not be dotted. + */ + std::pair<BSONObj, bool> extractProjectOnFieldAndRename(const StringData& oldName, + const StringData& newName); + protected: std::unique_ptr<ProjectionNode> makeChild(const std::string& fieldName) const final { return std::make_unique<ExclusionNode>( @@ -149,6 +158,11 @@ public: return boost::none; } + std::pair<BSONObj, bool> extractProjectOnFieldAndRename(const StringData& oldName, + const StringData& newName) final { + return _root->extractProjectOnFieldAndRename(oldName, newName); + } + private: // The ExclusionNode tree does most of the execution work once constructed. std::unique_ptr<ExclusionNode> _root; diff --git a/src/mongo/db/exec/exclusion_projection_executor_test.cpp b/src/mongo/db/exec/exclusion_projection_executor_test.cpp index 9ddd696e861..81c63ad280f 100644 --- a/src/mongo/db/exec/exclusion_projection_executor_test.cpp +++ b/src/mongo/db/exec/exclusion_projection_executor_test.cpp @@ -38,6 +38,7 @@ #include "mongo/bson/bsonmisc.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/json.h" +#include "mongo/bson/unordered_fields_bsonobj_comparator.h" #include "mongo/db/exec/document_value/document.h" #include "mongo/db/exec/document_value/document_value_test_util.h" #include "mongo/db/exec/document_value/value.h" @@ -546,5 +547,73 @@ TEST(ExclusionProjectionExecutionTest, ShouldNotRetainNestedArraysIfNoRecursionN ASSERT_DOCUMENT_EQ(result, expectedResult); } + +// +// extractProjectOnFieldAndRename() tests. +// +TEST(ExclusionProjectionExecutionTest, ShouldExtractEntireProjectOnRootField) { + auto exclusion = makeExclusionProjectionWithDefaultPolicies(fromjson("{a: 0}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("a", "b"); + + ASSERT_TRUE(allExtracted); + ASSERT_BSONOBJ_EQ(fromjson("{b: false}"), extractedProj); +} + +TEST(ExclusionProjectionExecutionTest, ShouldExtractEntireProjectOnMultipleSubfields) { + auto exclusion = makeExclusionProjectionWithDefaultPolicies(fromjson("{'a.c': 0, 'a.d': 0}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("a", "b"); + + ASSERT_TRUE(allExtracted); + const UnorderedFieldsBSONObjComparator kComparator; + ASSERT_EQ(kComparator.compare(fromjson("{b: {c: false, d: false}}"), extractedProj), 0); +} + +TEST(ExclusionProjectionExecutionTest, ShouldExtractPartOfProjectOnRootField) { + auto exclusion = makeExclusionProjectionWithDefaultPolicies(fromjson("{a: 0, c: 0}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("a", "b"); + + ASSERT_FALSE(allExtracted); + ASSERT_BSONOBJ_EQ(fromjson("{b: false}"), extractedProj); + ASSERT_BSONOBJ_EQ(fromjson("{c: false, _id: true}"), + exclusion->serializeTransformation(boost::none).toBson()); +} + +TEST(ExclusionProjectionExecutionTest, ShouldExtractPartOfProjectOnSubfields) { + auto exclusion = + makeExclusionProjectionWithDefaultPolicies(fromjson("{'x.y': 0, 'x.z': 0, 'c.d': 0}}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("x", "w"); + + ASSERT_FALSE(allExtracted); + const UnorderedFieldsBSONObjComparator kComparator; + ASSERT_EQ(kComparator.compare(fromjson("{w: {y: false, z: false}}"), extractedProj), 0); + ASSERT_BSONOBJ_EQ(fromjson("{c: {d: false}, _id: true}"), + exclusion->serializeTransformation(boost::none).toBson()); +} + +TEST(ExclusionProjectionExecutionTest, ShouldCorrectlyLeaveRemainderWithExcludedId) { + auto exclusion = makeExclusionProjectionWithDefaultPolicies(fromjson("{a: 0, _id: 0}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("a", "b"); + + ASSERT_FALSE(allExtracted); + ASSERT_BSONOBJ_EQ(fromjson("{b: false}"), extractedProj); + ASSERT_BSONOBJ_EQ(fromjson("{_id: false}"), + exclusion->serializeTransformation(boost::none).toBson()); +} + +TEST(ExclusionProjectionExecutionTest, ShouldNotExtractWhenFieldIsNotPresent) { + auto exclusion = makeExclusionProjectionWithDefaultPolicies(fromjson("{a: {b: 0}}")); + + auto [extractedProj, allExtracted] = exclusion->extractProjectOnFieldAndRename("b", "c"); + + ASSERT_FALSE(allExtracted); + ASSERT_BSONOBJ_EQ(fromjson("{}"), extractedProj); + ASSERT_BSONOBJ_EQ(fromjson("{a: {b: false}, _id: true}"), + exclusion->serializeTransformation(boost::none).toBson()); +} } // namespace } // namespace mongo::projection_executor diff --git a/src/mongo/db/exec/inclusion_projection_executor.cpp b/src/mongo/db/exec/inclusion_projection_executor.cpp index 81ec31c02b2..e330f868ca6 100644 --- a/src/mongo/db/exec/inclusion_projection_executor.cpp +++ b/src/mongo/db/exec/inclusion_projection_executor.cpp @@ -66,12 +66,11 @@ void FastPathEligibleInclusionNode::_applyProjections(BSONObj bson, BSONObjBuild while (it.more() && nFieldsNeeded > 0) { const auto bsonElement{it.next()}; const auto fieldName{bsonElement.fieldNameStringData()}; - const absl::string_view fieldNameKey{fieldName.rawData(), fieldName.size()}; - if (_projectedFields.find(fieldNameKey) != _projectedFields.end()) { + if (_projectedFields.find(fieldName) != _projectedFields.end()) { bob->append(bsonElement); --nFieldsNeeded; - } else if (auto childIt = _children.find(fieldNameKey); childIt != _children.end()) { + } else if (auto childIt = _children.find(fieldName); childIt != _children.end()) { auto child = static_cast<FastPathEligibleInclusionNode*>(childIt->second.get()); if (bsonElement.type() == BSONType::Object) { diff --git a/src/mongo/db/exec/projection_node.cpp b/src/mongo/db/exec/projection_node.cpp index 0cee88a78f6..1ef569ecd75 100644 --- a/src/mongo/db/exec/projection_node.cpp +++ b/src/mongo/db/exec/projection_node.cpp @@ -140,13 +140,12 @@ void ProjectionNode::applyProjections(const Document& inputDoc, MutableDocument* while (it.more()) { auto fieldName = it.fieldName(); - absl::string_view fieldNameKey{fieldName.rawData(), fieldName.size()}; - if (_projectedFields.find(fieldNameKey) != _projectedFields.end()) { + if (_projectedFields.find(fieldName) != _projectedFields.end()) { outputProjectedField( fieldName, applyLeafProjectionToValue(it.next().second), outputDoc); ++projectedFields; - } else if (auto childIt = _children.find(fieldNameKey); childIt != _children.end()) { + } else if (auto childIt = _children.find(fieldName); childIt != _children.end()) { outputProjectedField( fieldName, childIt->second->applyProjectionsToValue(it.next().second), outputDoc); ++projectedFields; diff --git a/src/mongo/db/exec/projection_node.h b/src/mongo/db/exec/projection_node.h index e6a4feab973..2a587580330 100644 --- a/src/mongo/db/exec/projection_node.h +++ b/src/mongo/db/exec/projection_node.h @@ -160,9 +160,9 @@ protected: // Writes the given value to the output doc, replacing the existing value of 'field' if present. virtual void outputProjectedField(StringData field, Value val, MutableDocument* outDoc) const; - stdx::unordered_map<std::string, std::unique_ptr<ProjectionNode>> _children; - stdx::unordered_map<std::string, boost::intrusive_ptr<Expression>> _expressions; - stdx::unordered_set<std::string> _projectedFields; + StringMap<std::unique_ptr<ProjectionNode>> _children; + StringMap<boost::intrusive_ptr<Expression>> _expressions; + StringSet _projectedFields; ProjectionPolicies _policies; std::string _pathToNode; diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript index d470ef3a575..69ab349d19f 100644 --- a/src/mongo/db/pipeline/SConscript +++ b/src/mongo/db/pipeline/SConscript @@ -406,6 +406,7 @@ env.CppUnitTest( 'document_source_union_with_test.cpp', 'document_source_internal_unpack_bucket_test/extract_or_build_project_to_internalize_test.cpp', 'document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp', + 'document_source_internal_unpack_bucket_test/extract_project_for_pushdown_test.cpp', 'document_source_internal_unpack_bucket_test/group_reorder_test.cpp', 'document_source_internal_unpack_bucket_test/internalize_project_test.cpp', 'document_source_internal_unpack_bucket_test/pushdown_computed_meta_projections_test.cpp', diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp index b44a3b36d3b..fb53b7e951d 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp @@ -622,6 +622,22 @@ DocumentSourceInternalUnpackBucket::splitMatchOnMetaAndRename( return {nullptr, match}; } +std::pair<BSONObj, bool> DocumentSourceInternalUnpackBucket::extractProjectForPushDown( + DocumentSource* src) const { + if (auto nextProject = dynamic_cast<DocumentSourceSingleDocumentTransformation*>(src); + nextProject && _bucketUnpacker.bucketSpec().metaField) { + // If we have an exclusion project, we can push down just the parts on the metaField. + if (nextProject->getType() == TransformerInterface::TransformerType::kExclusionProjection) { + return nextProject->extractProjectOnFieldAndRename( + _bucketUnpacker.bucketSpec().metaField.get(), timeseries::kBucketMetaFieldName); + } + + // TODO: SERVER-55727 + } + + return {BSONObj{}, false}; +} + Pipeline::SourceContainer::iterator DocumentSourceInternalUnpackBucket::doOptimizeAt( Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container) { invariant(*itr == this); @@ -695,6 +711,27 @@ Pipeline::SourceContainer::iterator DocumentSourceInternalUnpackBucket::doOptimi } } + // TODO: SERVER-54766: replace this logic. + if (std::next(itr) == container->end()) { + return container->end(); + } + + // Attempt to push down a $project on the metaField past $_internalUnpackBucket. + if (auto [metaProject, deleteRemainder] = extractProjectForPushDown(std::next(itr)->get()); + !metaProject.isEmpty()) { + container->insert(itr, + DocumentSourceProject::createFromBson( + BSON("$project" << metaProject).firstElement(), getContext())); + + if (deleteRemainder) { + // We have pushed down the entire $project. Remove the old $project from the pipeline, + // then attempt to optimize this stage again. + container->erase(std::next(itr)); + return std::prev(itr) == container->begin() ? std::prev(itr) + : std::prev(std::prev(itr)); + } + } + // Attempt to extract computed meta projections from subsequent $project, $addFields, or $set // and push them before the $_internalunpackBucket. pushDownComputedMetaProjection(itr, container); diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h index af10e99b87e..066447c5b49 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h @@ -178,6 +178,14 @@ public: bool pushDownComputedMetaProjection(Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container); + /** + * If 'src' represents an exclusion $project, attempts to extract the parts of 'src' that are + * only on the metaField. Returns a BSONObj representing the extracted project and a bool + * indicating whether all of 'src' was extracted. In the extracted $project, the metaField is + * renamed from the user defined name to 'kBucketMetaFieldName'. + */ + std::pair<BSONObj, bool> extractProjectForPushDown(DocumentSource* src) const; + private: GetNextResult doGetNext() final; diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/extract_project_for_pushdown_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/extract_project_for_pushdown_test.cpp new file mode 100644 index 00000000000..73cefb13ad3 --- /dev/null +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/extract_project_for_pushdown_test.cpp @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2021-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/bson/unordered_fields_bsonobj_comparator.h" +#include "mongo/db/pipeline/aggregation_context_fixture.h" +#include "mongo/db/pipeline/document_source_internal_unpack_bucket.h" +#include "mongo/db/pipeline/document_source_project.h" +#include "mongo/db/pipeline/pipeline.h" +#include "mongo/db/query/util/make_data_structure.h" +#include "mongo/unittest/bson_test_util.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +using InternalUnpackBucketExtractProjectForPushdownTest = AggregationContextFixture; + +TEST_F(InternalUnpackBucketExtractProjectForPushdownTest, DoesNotExtractWhenNoMetaField) { + auto unpack = DocumentSourceInternalUnpackBucket::createFromBson( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'foo', bucketMaxSpanSeconds: " + "3600}}") + .firstElement(), + getExpCtx()); + auto project = DocumentSourceProject::createFromBson( + fromjson("{$project: {meta: 0}}").firstElement(), getExpCtx()); + + auto [extractedProj, deleteRemainder] = + dynamic_cast<DocumentSourceInternalUnpackBucket*>(unpack.get()) + ->extractProjectForPushDown(project.get()); + + ASSERT_TRUE(extractedProj.isEmpty()); + + ASSERT_FALSE(deleteRemainder); + std::vector<Value> serializedArray; + project->serializeToArray(serializedArray); + ASSERT_BSONOBJ_EQ(fromjson("{$project: {meta: false, _id: true}}"), + serializedArray[0].getDocument().toBson()); +} + +TEST_F(InternalUnpackBucketExtractProjectForPushdownTest, ExtractsEntireProjectOnMetaField) { + auto unpack = DocumentSourceInternalUnpackBucket::createFromBson( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'foo', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600}}") + .firstElement(), + getExpCtx()); + auto project = DocumentSourceProject::createFromBson( + fromjson("{$project: {myMeta: 0}}").firstElement(), getExpCtx()); + + auto [extractedProj, deleteRemainder] = + dynamic_cast<DocumentSourceInternalUnpackBucket*>(unpack.get()) + ->extractProjectForPushDown(project.get()); + + ASSERT_BSONOBJ_EQ(fromjson("{meta: false}"), extractedProj); + ASSERT_TRUE(deleteRemainder); +} + +TEST_F(InternalUnpackBucketExtractProjectForPushdownTest, ExtractsEntireProjectOnSubfieldsOfMeta) { + auto unpack = DocumentSourceInternalUnpackBucket::createFromBson( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'foo', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600}}") + .firstElement(), + getExpCtx()); + auto project = DocumentSourceProject::createFromBson( + fromjson("{$project: {myMeta: {a: 0, b: 0}}}").firstElement(), getExpCtx()); + + auto [extractedProj, deleteRemainder] = + dynamic_cast<DocumentSourceInternalUnpackBucket*>(unpack.get()) + ->extractProjectForPushDown(project.get()); + + const UnorderedFieldsBSONObjComparator kComparator; + ASSERT_EQ(kComparator.compare(fromjson("{meta: {a: false, b: false}}"), extractedProj), 0); + ASSERT_TRUE(deleteRemainder); +} + +TEST_F(InternalUnpackBucketExtractProjectForPushdownTest, ExtractsPartOfProjectOnMetaField) { + auto unpack = DocumentSourceInternalUnpackBucket::createFromBson( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'foo', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600}}") + .firstElement(), + getExpCtx()); + auto project = DocumentSourceProject::createFromBson( + fromjson("{$project: {a: 0, 'myMeta.a': 0, b: 0, 'myMeta.b': 0, c: 0}}").firstElement(), + getExpCtx()); + + auto [extractedProj, deleteRemainder] = + dynamic_cast<DocumentSourceInternalUnpackBucket*>(unpack.get()) + ->extractProjectForPushDown(project.get()); + + const UnorderedFieldsBSONObjComparator kComparator; + ASSERT_EQ(kComparator.compare(fromjson("{meta: {a: false, b: false}}"), extractedProj), 0); + + ASSERT_FALSE(deleteRemainder); + std::vector<Value> serializedArray; + project->serializeToArray(serializedArray); + ASSERT_EQ(kComparator.compare(fromjson("{$project: {_id: true, a: false, b: false, c: false}}"), + serializedArray[0].getDocument().toBson()), + 0); +} +} // namespace +} // namespace mongo diff --git a/src/mongo/db/pipeline/document_source_single_document_transformation.h b/src/mongo/db/pipeline/document_source_single_document_transformation.h index 158f9936fdc..8c7369e396d 100644 --- a/src/mongo/db/pipeline/document_source_single_document_transformation.h +++ b/src/mongo/db/pipeline/document_source_single_document_transformation.h @@ -117,6 +117,17 @@ public: return _parsedTransform->extractComputedProjections(oldName, newName, reservedNames); } + /** + * If this transformation is a project, removes and returns a BSONObj representing the part of + * this project that depends only on 'oldName'. Also returns a bool indicating whether this + * entire project is extracted. In the extracted $project, 'oldName' is renamed to 'newName'. + * 'oldName' should not be dotted. + */ + std::pair<BSONObj, bool> extractProjectOnFieldAndRename(const StringData& oldName, + const StringData& newName) { + return _parsedTransform->extractProjectOnFieldAndRename(oldName, newName); + } + protected: GetNextResult doGetNext() final; void doDispose() final; diff --git a/src/mongo/db/pipeline/transformer_interface.h b/src/mongo/db/pipeline/transformer_interface.h index 40ae3b0564e..6be98af2570 100644 --- a/src/mongo/db/pipeline/transformer_interface.h +++ b/src/mongo/db/pipeline/transformer_interface.h @@ -80,5 +80,10 @@ public: const std::set<StringData>& reservedNames) { return {BSONObj{}, false}; } + + virtual std::pair<BSONObj, bool> extractProjectOnFieldAndRename(const StringData& oldName, + const StringData& newName) { + return {BSONObj{}, false}; + } }; } // namespace mongo |