diff options
author | Jacob Evans <jacob.evans@10gen.com> | 2021-04-20 23:02:58 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-04-21 03:28:32 +0000 |
commit | 384c41b1f89412b5843b8546b8ddf4fcb837eff5 (patch) | |
tree | 8e7843faf14c723ce0e9a966053763bbf22f99f5 | |
parent | 1eadaf115d098198845026aef15b0f24578b46b8 (diff) | |
download | mongo-384c41b1f89412b5843b8546b8ddf4fcb837eff5.tar.gz |
SERVER-55099 Update $_internalUnpackBucket serialization and parsing
8 files changed, 174 insertions, 62 deletions
diff --git a/src/mongo/db/exec/bucket_unpacker.h b/src/mongo/db/exec/bucket_unpacker.h index 9c505bc1682..fd159092efa 100644 --- a/src/mongo/db/exec/bucket_unpacker.h +++ b/src/mongo/db/exec/bucket_unpacker.h @@ -29,6 +29,7 @@ #pragma once +#include <algorithm> #include <set> #include "mongo/bson/bsonobj.h" @@ -182,7 +183,10 @@ private: */ inline bool eraseMetaFromFieldSetAndDetermineIncludeMeta(BucketUnpacker::Behavior unpackerBehavior, BucketSpec* bucketSpec) { - if (!bucketSpec->metaField) { + if (!bucketSpec->metaField || + std::find(bucketSpec->computedMetaProjFields.cbegin(), + bucketSpec->computedMetaProjFields.cend(), + *bucketSpec->metaField) != bucketSpec->computedMetaProjFields.cend()) { return false; } else if (auto itr = bucketSpec->fieldSet.find(*bucketSpec->metaField); itr != bucketSpec->fieldSet.end()) { 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 664028e21be..4eb1d2ac615 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp @@ -31,6 +31,9 @@ #include "mongo/platform/basic.h" +#include <algorithm> +#include <iterator> + #include "mongo/db/pipeline/document_source_internal_unpack_bucket.h" #include <string> @@ -287,6 +290,7 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceInternalUnpackBucket::createF auto hasIncludeExclude = false; std::vector<std::string> fields; auto bucketMaxSpanSeconds = 0; + std::vector<std::string> computedMetaProjFields; for (auto&& elem : specElem.embeddedObject()) { auto fieldName = elem.fieldNameStringData(); if (fieldName == "include" || fieldName == "exclude") { @@ -325,13 +329,28 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceInternalUnpackBucket::createF metaField.find('.') == std::string::npos); bucketSpec.metaField = std::move(metaField); } else if (fieldName == "bucketMaxSpanSeconds") { - uassert(5509900, + uassert(5510600, "bucketMaxSpanSeconds field must be an integer", elem.type() == BSONType::NumberInt); - uassert(5509901, + uassert(5510601, "bucketMaxSpanSeconds field must be greater than zero", elem._numberInt() > 0); bucketMaxSpanSeconds = elem._numberInt(); + } else if (fieldName == "computedMetaProjFields") { + uassert(5509900, + "computedMetaProjFields field must be an array", + elem.type() == BSONType::Array); + + for (auto&& elt : elem.embeddedObject()) { + uassert(5509901, + "computedMetaProjFields field element must be a string", + elt.type() == BSONType::String); + auto field = elt.valueStringData(); + uassert(5509902, + "computedMetaProjFields field element must be a single-element field path", + field.find('.') == std::string::npos); + bucketSpec.computedMetaProjFields.emplace_back(field); + } } else { uasserted(5346506, str::stream() @@ -343,7 +362,7 @@ boost::intrusive_ptr<DocumentSource> DocumentSourceInternalUnpackBucket::createF "The $_internalUnpackBucket stage requires a timeField parameter", specElem[timeseries::kTimeFieldName].ok()); - uassert(5509902, + uassert(5510602, "The $_internalUnpackBucket stage requires a bucketMaxSpanSeconds parameter", specElem["bucketMaxSpanSeconds"].ok()); @@ -361,13 +380,32 @@ void DocumentSourceInternalUnpackBucket::serializeToArray( for (auto&& field : spec.fieldSet) { fields.emplace_back(field); } - out.addField(behavior, Value{fields}); + if (((_bucketUnpacker.includeMetaField() && + _bucketUnpacker.behavior() == BucketUnpacker::Behavior::kInclude) || + (!_bucketUnpacker.includeMetaField() && + _bucketUnpacker.behavior() == BucketUnpacker::Behavior::kExclude && spec.metaField)) && + std::find(spec.computedMetaProjFields.cbegin(), + spec.computedMetaProjFields.cend(), + *spec.metaField) == spec.computedMetaProjFields.cend()) + fields.emplace_back(*spec.metaField); + + out.addField(behavior, Value{std::move(fields)}); out.addField(timeseries::kTimeFieldName, Value{spec.timeField}); if (spec.metaField) { out.addField(timeseries::kMetaFieldName, Value{*spec.metaField}); } out.addField("bucketMaxSpanSeconds", Value{_bucketMaxSpanSeconds}); + if (!spec.computedMetaProjFields.empty()) + out.addField("computedMetaProjFields", Value{[&] { + std::vector<Value> compFields; + std::transform(spec.computedMetaProjFields.cbegin(), + spec.computedMetaProjFields.cend(), + std::back_inserter(compFields), + [](auto&& projString) { return Value{projString}; }); + return compFields; + }()}); + if (!explain) { array.push_back(Value(DOC(getSourceName() << out.freeze()))); if (_sampleSize) { 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 c7dc7ccb0a4..171547cd4d8 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.h @@ -30,6 +30,7 @@ #pragma once #include <set> +#include <vector> #include "mongo/db/exec/bucket_unpacker.h" #include "mongo/db/pipeline/document_source.h" diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/internalize_project_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/internalize_project_test.cpp index 8151a279242..5366e6789ce 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/internalize_project_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/internalize_project_test.cpp @@ -157,8 +157,8 @@ TEST_F(InternalUnpackBucketInternalizeProjectTest, std::vector<Value> serializedArray; unpack->serializeToArray(serializedArray); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id'], timeField: 'time', " - "metaField: 'meta', bucketMaxSpanSeconds: 3600}}"), + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'meta'], timeField: " + "'time', metaField: 'meta', bucketMaxSpanSeconds: 3600}}"), serializedArray[0].getDocument().toBson()); ASSERT_TRUE(unpack->includeMetaField()); ASSERT_FALSE(unpack->includeTimeField()); @@ -177,7 +177,7 @@ TEST_F(InternalUnpackBucketInternalizeProjectTest, std::vector<Value> serializedArray; unpack->serializeToArray(serializedArray); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: ['myMeta'], timeField: 'time', " "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), serializedArray[0].getDocument().toBson()); ASSERT_FALSE(unpack->includeMetaField()); diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/optimize_pipeline_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/optimize_pipeline_test.cpp index 6f0978011c1..17077b428ed 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/optimize_pipeline_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/optimize_pipeline_test.cpp @@ -346,9 +346,10 @@ TEST_F(OptimizePipeline, ProjectThenMixedMatchPushedDown) { fromjson( "{$match: {$and: [{meta: {$eq: 'abc'}}, {'control.min.a': {$_internalExprLte: 4}}]}}"), stages[0].getDocument().toBson()); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'a', 'x'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - stages[1].getDocument().toBson()); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'a', 'x', 'myMeta'], timeField: " + "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + stages[1].getDocument().toBson()); ASSERT_BSONOBJ_EQ(fromjson("{$match: {a: {$lte: 4}}}"), stages[2].getDocument().toBson()); const UnorderedFieldsBSONObjComparator kComparator; ASSERT_EQ( @@ -401,9 +402,10 @@ TEST_F(OptimizePipeline, ComputedProjectThenMetaMatchPushedDown) { ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$match: {meta: {$gte: 'abc'}}}"), serialized[0]); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {y: '$meta'}}"), serialized[1]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'y'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[2]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'y'], timeField: 'time', metaField: " + "'myMeta', bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['y']}}"), + serialized[2]); } TEST_F(OptimizePipeline, ComputedProjectThenMetaMatchNotPushedDown) { @@ -423,11 +425,13 @@ TEST_F(OptimizePipeline, ComputedProjectThenMetaMatchNotPushedDown) { ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {myMeta: {$sum: ['$meta.a', '$meta.b']}}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson( + "{$_internalUnpackBucket: { include: ['_id', 'myMeta'], timeField: 'time', metaField: " + "'myMeta', bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['myMeta']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$match: {myMeta: {$gte: 'abc'}}}"), serialized[2]); -} +} // namespace TEST_F(OptimizePipeline, ComputedProjectThenMatchNotPushedDown) { auto pipeline = Pipeline::parse( @@ -445,9 +449,10 @@ TEST_F(OptimizePipeline, ComputedProjectThenMatchNotPushedDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {y: {$sum: ['$meta.a', '$meta.b']}}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'y'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'y'], timeField: 'time', metaField: " + "'myMeta', bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['y']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$match: {y: {$gt: 'abc'}}}"), serialized[2]); } @@ -466,9 +471,10 @@ TEST_F(OptimizePipeline, MetaSortThenProjectPushedDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$sort: {meta: -1}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'x'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'x', 'myMeta'], timeField: 'time', " + "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + serialized[1]); } TEST_F(OptimizePipeline, ProjectThenMetaSortPushedDown) { @@ -486,9 +492,10 @@ TEST_F(OptimizePipeline, ProjectThenMetaSortPushedDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$sort: {meta: -1}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'x'], timeField: " - "'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'x', 'myMeta'], timeField: 'time', " + "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + serialized[1]); } TEST_F(OptimizePipeline, ComputedProjectThenSortPushedDown) { @@ -507,9 +514,11 @@ TEST_F(OptimizePipeline, ComputedProjectThenSortPushedDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {myMeta: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id'], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson( + "{$_internalUnpackBucket: { include: ['_id', 'myMeta'], timeField: 'time', metaField: " + "'myMeta', bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['myMeta']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$sort: {myMeta: 1}}"), serialized[2]); } @@ -552,9 +561,11 @@ TEST_F(OptimizePipeline, ComputedProjectThenProjectPushDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {myMeta: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id'], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson( + "{$_internalUnpackBucket: { include: ['_id', 'myMeta'], timeField: 'time', metaField: " + "'myMeta', bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['myMeta']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$project: {myMeta: false, _id: true}}"), serialized[2]); } @@ -573,9 +584,10 @@ TEST_F(OptimizePipeline, AddFieldsThenSortPushedDown) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {myMeta: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['myMeta']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$sort: {myMeta: 1}}"), serialized[2]); } @@ -595,10 +607,10 @@ TEST_F(OptimizePipeline, PushDownAddFieldsAndInternalizeProjection) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {device: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ( - fromjson("{$_internalUnpackBucket: { include: ['_id', 'device', 'x'], " - "timeField: 'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'device', 'x'], " + "timeField: 'time', metaField: 'myMeta', bucketMaxSpanSeconds: " + "3600, computedMetaProjFields: ['device']}}"), + serialized[1]); } TEST_F(OptimizePipeline, PushDownAddFieldsDoNotInternalizeProjection) { @@ -619,8 +631,8 @@ TEST_F(OptimizePipeline, PushDownAddFieldsDoNotInternalizeProjection) { ASSERT_EQ(4u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {device: '$meta.a'}}"), serialized[0]); ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'device', 'x', 'y', " - "'z'], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + "'z'], timeField: 'time', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['device']}}"), serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {z: {$add : ['$x', '$y']}}}"), serialized[2]); const UnorderedFieldsBSONObjComparator kComparator; @@ -646,10 +658,10 @@ TEST_F(OptimizePipeline, InternalizeProjectAndPushdownAddFields) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {newMeta: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ( - fromjson("{$_internalUnpackBucket: { include: ['_id', 'newMeta', 'x', 'y'], " - "timeField: 'time', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[1]); + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'newMeta', 'x', 'y', " + "'myMeta'], timeField: 'time', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['newMeta']}}"), + serialized[1]); } TEST_F(OptimizePipeline, PushdownSortAndAddFields) { @@ -667,9 +679,10 @@ TEST_F(OptimizePipeline, PushdownSortAndAddFields) { ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {newMeta: '$meta.a'}}"), serialized[0]); ASSERT_BSONOBJ_EQ(fromjson("{$sort: {meta: -1}}"), serialized[1]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[2]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['newMeta']}}"), + serialized[2]); } TEST_F(OptimizePipeline, PushdownMatchAndAddFields) { @@ -689,9 +702,10 @@ TEST_F(OptimizePipeline, PushdownMatchAndAddFields) { ASSERT_EQ(4u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$match: {'meta.a': {$eq: 'abc'}}}"), serialized[0]); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {newMeta: '$meta.b'}}"), serialized[1]); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " - "metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[2]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', metaField: 'myMeta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['newMeta']}}"), + serialized[2]); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {z: {$add : ['$x', '$y']}}}"), serialized[3]); } } // namespace diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/pushdown_computed_meta_projections_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/pushdown_computed_meta_projections_test.cpp index 47d7de0b8c0..3f34f8cc345 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/pushdown_computed_meta_projections_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/pushdown_computed_meta_projections_test.cpp @@ -59,7 +59,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, auto serialized = pipeline->serializeToBson(); ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {newMeta: {$toUpper: ['$meta']}}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(unpackSpecObj, serialized[1]); + auto extraField = fromjson("{computedMetaProjFields: ['newMeta']}"); + ASSERT_BSONOBJ_EQ(BSON("$_internalUnpackBucket" << unpackSpecObj.firstElement().Obj().addField( + extraField.firstElement())), + serialized[1]); } TEST_F(InternalUnpackBucketPushdownProjectionsTest, OptimizeAddFieldsWithMetaProjectionDocument) { @@ -79,7 +82,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, OptimizeAddFieldsWithMetaPro ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {newMeta: {$concat: ['$meta.a', '$meta.b']}}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(unpackSpecObj, serialized[1]); + auto extraField = fromjson("{computedMetaProjFields: ['newMeta']}"); + ASSERT_BSONOBJ_EQ(BSON("$_internalUnpackBucket" << unpackSpecObj.firstElement().Obj().addField( + extraField.firstElement())), + serialized[1]); } TEST_F(InternalUnpackBucketPushdownProjectionsTest, OptimizeAddFieldsWith2MetaProjections) { @@ -99,7 +105,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, OptimizeAddFieldsWith2MetaPr ASSERT_EQ(2u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {device: '$meta.a', deviceType: '$meta.b'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(unpackSpecObj, serialized[1]); + auto extraField = fromjson("{computedMetaProjFields: ['device', 'deviceType']}"); + ASSERT_BSONOBJ_EQ(BSON("$_internalUnpackBucket" << unpackSpecObj.firstElement().Obj().addField( + extraField.firstElement())), + serialized[1]); } TEST_F(InternalUnpackBucketPushdownProjectionsTest, SplitAddFieldsWithMixedProjectionFields) { @@ -118,7 +127,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, SplitAddFieldsWithMixedProje auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {device: '$meta.a'}}"), serialized[0]); - ASSERT_BSONOBJ_EQ(unpackSpecObj, serialized[1]); + auto extraField = fromjson("{computedMetaProjFields: ['device']}"); + ASSERT_BSONOBJ_EQ(BSON("$_internalUnpackBucket" << unpackSpecObj.firstElement().Obj().addField( + extraField.firstElement())), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: {temp: {$add: ['$temperature', '$offset']}}}"), serialized[2]); } @@ -234,6 +246,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: { device: '$meta.a'}}"), serialized[0]); + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " + "metaField: 'myMeta', bucketMaxSpanSeconds: 3600, " + "computedMetaProjFields: ['device']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ(fromjson("{$project: {_id: true, device: true}}"), serialized[2]); } @@ -255,6 +271,10 @@ TEST_F(InternalUnpackBucketPushdownProjectionsTest, auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3u, serialized.size()); ASSERT_BSONOBJ_EQ(fromjson("{$addFields: { device: '$meta.a'}}"), serialized[0]); + ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { exclude: [], timeField: 'time', " + "metaField: 'myMeta', bucketMaxSpanSeconds: 3600, " + "computedMetaProjFields: ['device']}}"), + serialized[1]); ASSERT_BSONOBJ_EQ( fromjson("{$project: {_id: true, x: true, y : {z: true}, device: '$device'}}"), serialized[2]); diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/sample_reorder_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/sample_reorder_test.cpp index b1f1d3d30d4..0b26fce8604 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/sample_reorder_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/sample_reorder_test.cpp @@ -71,9 +71,10 @@ TEST_F(InternalUnpackBucketSampleReorderTest, SampleThenComputedProject) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3, serialized.size()); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'temp'], timeField: " - "'foo', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[0]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'temp', 'myMeta'], " + "timeField: 'foo', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + serialized[0]); ASSERT_BSONOBJ_EQ(sampleSpec, serialized[1]); const UnorderedFieldsBSONObjComparator kComparator; ASSERT_EQ(kComparator.compare(projectSpec, serialized[2]), 0); @@ -114,9 +115,10 @@ TEST_F(InternalUnpackBucketSampleReorderTest, ComputedProjectThenSample) { auto serialized = pipeline->serializeToBson(); ASSERT_EQ(3, serialized.size()); - ASSERT_BSONOBJ_EQ(fromjson("{$_internalUnpackBucket: { include: ['_id', 'temp'], timeField: " - "'foo', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), - serialized[0]); + ASSERT_BSONOBJ_EQ( + fromjson("{$_internalUnpackBucket: { include: ['_id', 'temp', 'myMeta'], timeField: " + "'foo', metaField: 'myMeta', bucketMaxSpanSeconds: 3600}}"), + serialized[0]); ASSERT_BSONOBJ_EQ(sampleSpec, serialized[1]); const UnorderedFieldsBSONObjComparator kComparator; ASSERT_EQ(kComparator.compare(projectSpec, serialized[2]), 0); diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp index 587ad434e19..126d4d57e48 100644 --- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp +++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/unpack_bucket_exec_test.cpp @@ -824,5 +824,38 @@ TEST_F(InternalUnpackBucketExecTest, ParserRejectsBothIncludeAndExcludeParameter AssertionException, 5408000); } + +TEST_F(InternalUnpackBucketExecTest, ParserRoundtripsIncludeMeta) { + auto bson = fromjson( + "{$_internalUnpackBucket: {include: ['steve', 'meta'], timeField: 'time', metaField: " + "'meta', bucketMaxSpanSeconds: 3600}}"); + auto array = std::vector<Value>{}; + DocumentSourceInternalUnpackBucket::createFromBson(bson.firstElement(), getExpCtx()) + ->serializeToArray(array); + ASSERT_BSONOBJ_EQ(array[0].getDocument().toBson(), bson); +} + +TEST_F(InternalUnpackBucketExecTest, ParserRoundtripsComputedMetaProjFields) { + auto bson = fromjson( + "{$_internalUnpackBucket: {exclude: [], timeField: 'time', metaField: 'meta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['a', 'b', 'c']}}"); + auto array = std::vector<Value>{}; + DocumentSourceInternalUnpackBucket::createFromBson(bson.firstElement(), getExpCtx()) + ->serializeToArray(array); + ASSERT_BSONOBJ_EQ(array[0].getDocument().toBson(), bson); +} + +TEST_F(InternalUnpackBucketExecTest, ParserRoundtripsComputedMetaProjFieldOverridingMeta) { + auto bson = fromjson( + "{$_internalUnpackBucket: {exclude: [], timeField: 'time', metaField: 'meta', " + "bucketMaxSpanSeconds: 3600, computedMetaProjFields: ['meta']}}"); + auto unpackBucket = + DocumentSourceInternalUnpackBucket::createFromBson(bson.firstElement(), getExpCtx()); + ASSERT_FALSE( + static_cast<DocumentSourceInternalUnpackBucket&>(*unpackBucket).includeMetaField()); + auto array = std::vector<Value>{}; + unpackBucket->serializeToArray(array); + ASSERT_BSONOBJ_EQ(array[0].getDocument().toBson(), bson); +} } // namespace } // namespace mongo |