diff options
author | Dan Larkin-York <dan.larkin-york@mongodb.com> | 2021-07-06 20:29:04 +0000 |
---|---|---|
committer | Dan Larkin-York <dan.larkin-york@mongodb.com> | 2021-07-14 16:01:26 +0000 |
commit | 3b9982be740bc360691f46ed534427065763c16f (patch) | |
tree | ac06cd2626aac50f2658cb85b47328c098130eb3 | |
parent | 3e8b8bbf8fd4243d9b7e81fbb569d492da05628a (diff) | |
download | mongo-3b9982be740bc360691f46ed534427065763c16f.tar.gz |
SERVER-58171 Changing time-series granularity does not update view definition
-rw-r--r-- | jstests/core/timeseries/bucket_granularity.js | 118 | ||||
-rw-r--r-- | jstests/core/timeseries/bucket_timestamp_rounding.js | 30 | ||||
-rw-r--r-- | src/mongo/db/catalog/coll_mod.cpp | 54 | ||||
-rw-r--r-- | src/mongo/db/catalog/create_collection.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/commands/dbcommands.cpp | 70 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_options.cpp | 80 | ||||
-rw-r--r-- | src/mongo/db/timeseries/timeseries_options.h | 5 |
7 files changed, 255 insertions, 119 deletions
diff --git a/jstests/core/timeseries/bucket_granularity.js b/jstests/core/timeseries/bucket_granularity.js index b14f56e4e7f..62a6594e359 100644 --- a/jstests/core/timeseries/bucket_granularity.js +++ b/jstests/core/timeseries/bucket_granularity.js @@ -19,6 +19,17 @@ (function() { +function verifyViewPipeline(coll) { + const cProps = + db.runCommand({listCollections: 1, filter: {name: coll.getName()}}).cursor.firstBatch[0]; + const cSeconds = cProps.options.timeseries.bucketMaxSpanSeconds; + const vProps = db.system.views.find({_id: `${db.getName()}.${coll.getName()}`}).toArray()[0]; + const vSeconds = vProps.pipeline[0].$_internalUnpackBucket.bucketMaxSpanSeconds; + assert.eq(cSeconds, + vSeconds, + `expected view pipeline 'bucketMaxSpanSeconds' to match timeseries options`); +} + (function testSeconds() { let coll = db.granularitySeconds; coll.drop(); @@ -88,6 +99,7 @@ assert.commandWorked(db.createCollection( coll.getName(), {timeseries: {timeField: 't', granularity: 'seconds'}})); + verifyViewPipeline(coll); // All measurements land in the same bucket. assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:00:00.000Z")})); @@ -102,21 +114,64 @@ assert.commandWorked(coll.insert({t: ISODate("2021-04-22T21:00:00.000Z")})); assert.eq(2, db.system.buckets.granularitySecondsToMinutes.find().itcount()); - // TODO SERVER-58171: Re-enable these tests: // Now let's bump to minutes and make sure we get the expected behavior - // assert.commandWorked( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); + verifyViewPipeline(coll); // All measurements land in the same bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:00:00.000Z")})); - // assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:22:02.000Z")})); - // assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:59:59.999Z")})); - // assert.eq(2, db.system.buckets.granularitySecondsToMinutes.find().itcount()); + assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:00:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:22:02.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:59:59.999Z")})); + assert.eq(2, db.system.buckets.granularitySecondsToMinutes.find().itcount()); // Expect bucket max span to be one day. A new measurement outside of this range should create // a new bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-04-23T21:00:00.000Z")})); - // assert.eq(3, db.system.buckets.granularitySecondsToMinutes.find().itcount()); + assert.commandWorked(coll.insert({t: ISODate("2021-04-23T21:00:00.000Z")})); + assert.eq(3, db.system.buckets.granularitySecondsToMinutes.find().itcount()); + + // Make sure when we query, we use the new bucket max span to make sure we get all matches + assert.eq(4, coll.find({t: {$gt: ISODate("2021-04-23T19:00:00.000Z")}}).itcount()); +})(); + +(function testIncreasingSecondsToHours() { + let coll = db.granularitySecondsToHours; + coll.drop(); + + assert.commandWorked(db.createCollection( + coll.getName(), {timeseries: {timeField: 't', granularity: 'seconds'}})); + verifyViewPipeline(coll); + + // All measurements land in the same bucket. + assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:00:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:00:03.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:00:59.999Z")})); + assert.eq(1, db.system.buckets.granularitySecondsToHours.find().itcount()); + + // Expect bucket max span to be one hour. A new measurement outside of this range should create + // a new bucket. + assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:59:59.999Z")})); + assert.eq(1, db.system.buckets.granularitySecondsToHours.find().itcount()); + assert.commandWorked(coll.insert({t: ISODate("2021-04-22T21:00:00.000Z")})); + assert.eq(2, db.system.buckets.granularitySecondsToHours.find().itcount()); + + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); + verifyViewPipeline(coll); + + // All measurements land in the same bucket. + assert.commandWorked(coll.insert({t: ISODate("2021-05-22T00:00:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-05-22T18:11:03.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-05-22T20:59:59.999Z")})); + assert.eq(2, db.system.buckets.granularitySecondsToHours.find().itcount()); + + // Expect bucket max span to be 30 days. A new measurement outside of this range should create + // a new bucket. + assert.commandWorked(coll.insert({t: ISODate("2021-05-22T21:00:00.001Z")})); + assert.eq(3, db.system.buckets.granularitySecondsToHours.find().itcount()); + + // Make sure when we query, we use the new bucket max span to make sure we get all matches + assert.eq(4, coll.find({t: {$gt: ISODate("2021-05-21T00:00:00.000Z")}}).itcount()); })(); (function testIncreasingMinutesToHours() { @@ -125,6 +180,7 @@ assert.commandWorked(db.createCollection( coll.getName(), {timeseries: {timeField: 't', granularity: 'minutes'}})); + verifyViewPipeline(coll); // All measurements land in the same bucket. assert.commandWorked(coll.insert({t: ISODate("2021-04-22T20:00:00.000Z")})); @@ -139,43 +195,45 @@ assert.commandWorked(coll.insert({t: ISODate("2021-04-23T20:00:00.000Z")})); assert.eq(2, db.system.buckets.granularityMinutesToHours.find().itcount()); - // TODO SERVER-58171: Re-enable these tests: - // assert.commandWorked( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); + verifyViewPipeline(coll); // All measurements land in the same bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-05-23T00:00:00.000Z")})); - // assert.commandWorked(coll.insert({t: ISODate("2021-05-23T18:11:03.000Z")})); - // assert.commandWorked(coll.insert({t: ISODate("2021-05-23T19:59:59.999Z")})); - // assert.eq(2, db.system.buckets.granularityMinutesToHours.find().itcount()); + assert.commandWorked(coll.insert({t: ISODate("2021-05-23T00:00:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-05-23T18:11:03.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-05-23T19:59:59.999Z")})); + assert.eq(2, db.system.buckets.granularityMinutesToHours.find().itcount()); // Expect bucket max span to be 30 days. A new measurement outside of this range should create // a new bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-05-23T20:00:00.001Z")})); - // assert.eq(3, db.system.buckets.granularityMinutesToHours.find().itcount()); + assert.commandWorked(coll.insert({t: ISODate("2021-05-23T20:00:00.001Z")})); + assert.eq(3, db.system.buckets.granularityMinutesToHours.find().itcount()); + + // Make sure when we query, we use the new bucket max span to make sure we get all matches + assert.eq(4, coll.find({t: {$gt: ISODate("2021-05-22T00:00:00.000Z")}}).itcount()); })(); (function testReducingGranularityFails() { - let coll = db.granularityMinutesToHours; + let coll = db.reducingGranularityFails; coll.drop(); - // TODO SERVER-58171: Re-enable these tests: - // assert.commandWorked(db.createCollection( - // coll.getName(), {timeseries: {timeField: 't', granularity: 'minutes'}})); + assert.commandWorked(db.createCollection( + coll.getName(), {timeseries: {timeField: 't', granularity: 'minutes'}})); // Decreasing minutes -> seconds shouldn't work. - // assert.commandFailed( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'seconds'}})); + assert.commandFailed( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'seconds'}})); // Increasing minutes -> hours should work fine. - // assert.commandWorked( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); // Decreasing hours -> minutes shouldn't work. - // assert.commandFailed( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); + assert.commandFailed( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); // Decreasing hours -> seconds shouldn't work either. - // assert.commandFailed( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'seconds'}})); + assert.commandFailed( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'seconds'}})); })(); })(); diff --git a/jstests/core/timeseries/bucket_timestamp_rounding.js b/jstests/core/timeseries/bucket_timestamp_rounding.js index 62d584a365b..d3bcf8ef33c 100644 --- a/jstests/core/timeseries/bucket_timestamp_rounding.js +++ b/jstests/core/timeseries/bucket_timestamp_rounding.js @@ -86,19 +86,18 @@ assert.eq(1, buckets.length); assert.eq(buckets[0].control.min.t, ISODate("2021-04-22T20:10:00.000Z")); - // TODO SERVER-58171: Re-enable these tests // Now let's bump to minutes and make sure we get the expected behavior - // assert.commandWorked( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'minutes'}})); // Open a new bucket and ensure min time is rounded down to nearest hour. - // assert.commandWorked(coll.insert({t: ISODate("2021-04-24T20:10:14.134Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-24T20:10:14.134Z")})); // And that going backwards, but after the rounding point, doesn't open another new bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-04-24T20:05:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-04-24T20:05:00.000Z")})); - // buckets = db.system.buckets.granularitySecondsToMinutes.find().toArray(); - // assert.eq(2, buckets.length); - // assert.eq(buckets[1].control.min.t, ISODate("2021-04-24T20:00:00.000Z")); + buckets = db.system.buckets.granularitySecondsToMinutes.find().toArray(); + assert.eq(2, buckets.length); + assert.eq(buckets[1].control.min.t, ISODate("2021-04-24T20:00:00.000Z")); })(); (function testMinutesToHours() { @@ -117,18 +116,17 @@ assert.eq(1, buckets.length); assert.eq(buckets[0].control.min.t, ISODate("2021-04-22T20:00:00.000Z")); - // TODO SERVER-58171: Re-enable these tests // Now let's bump to minutes and make sure we get the expected behavior - // assert.commandWorked( - // db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); + assert.commandWorked( + db.runCommand({collMod: coll.getName(), timeseries: {granularity: 'hours'}})); // Open a new bucket and ensure min time is rounded down to nearest day. - // assert.commandWorked(coll.insert({t: ISODate("2021-06-24T20:10:14.134Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-06-24T20:10:14.134Z")})); // And that going backwards, but after the rounding point, doesn't open another new bucket. - // assert.commandWorked(coll.insert({t: ISODate("2021-06-24T10:00:00.000Z")})); + assert.commandWorked(coll.insert({t: ISODate("2021-06-24T10:00:00.000Z")})); - // buckets = db.system.buckets.granularityMinutesToHours.find().toArray(); - // assert.eq(2, buckets.length); - // assert.eq(buckets[1].control.min.t, ISODate("2021-06-24T00:00:00.000Z")); + buckets = db.system.buckets.granularityMinutesToHours.find().toArray(); + assert.eq(2, buckets.length); + assert.eq(buckets[1].control.min.t, ISODate("2021-06-24T00:00:00.000Z")); })(); })(); diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index 08019ffa486..04ef2969437 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -114,35 +114,6 @@ struct CollModRequest { bool recordPreImages = false; }; -bool isValidTimeseriesGranularityTransition(BucketGranularityEnum current, - BucketGranularityEnum target) { - bool validTransition = true; - if (current == target) { - return validTransition; - } - - switch (current) { - case BucketGranularityEnum::Seconds: { - if (target != BucketGranularityEnum::Minutes) { - validTransition = false; - } - break; - } - case BucketGranularityEnum::Minutes: { - if (target != BucketGranularityEnum::Hours) { - validTransition = false; - } - break; - } - case BucketGranularityEnum::Hours: { - validTransition = false; - break; - } - } - - return validTransition; -} - StatusWith<CollModRequest> parseCollModRequest(OperationContext* opCtx, const NamespaceString& nss, const CollectionPtr& coll, @@ -368,12 +339,6 @@ StatusWith<CollModRequest> parseCollModRequest(OperationContext* opCtx, << fieldName); } - if (e.Obj().hasField("granularity")) { - // TODO: SERVER-58171 Re-enable this feature - return Status(ErrorCodes::InvalidOptions, - "Changing timeseries 'granularity' is not allowed"); - } - cmr.timeseries = e; } else { if (isView) { @@ -651,21 +616,10 @@ Status _collModInternal(OperationContext* opCtx, } if (ts.isABSONObj()) { - TimeseriesOptions newOptions = *oldCollOptions.timeseries; - bool changed = false; - - if (ts.Obj().hasField("granularity")) { - BSONElement granularityElem = ts.Obj().getField("granularity"); - BucketGranularityEnum target = BucketGranularity_parse( - IDLParserErrorContext("BucketGranularity"), granularityElem.valueStringData()); - if (target != oldCollOptions.timeseries->getGranularity()) { - newOptions.setGranularity(target); - newOptions.setBucketMaxSpanSeconds( - timeseries::getMaxSpanSecondsFromGranularity(target)); - changed = true; - } - } - + auto res = timeseries::applyTimeseriesOptionsModifications(*oldCollOptions.timeseries, + ts.Obj()); + uassertStatusOK(res); + auto [newOptions, changed] = res.getValue(); if (changed) { coll.getWritableCollection()->setTimeseriesOptions(opCtx, newOptions); } diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index b759d4b92f5..1cc3c7b8a0d 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -383,21 +383,8 @@ Status _createTimeseries(OperationContext* opCtx, CollectionOptions viewOptions; viewOptions.viewOn = bucketsNs.coll().toString(); viewOptions.collation = options.collation; - - if (options.timeseries->getMetaField()) { - viewOptions.pipeline = BSON_ARRAY(BSON( - "$_internalUnpackBucket" - << BSON("timeField" << options.timeseries->getTimeField() << "metaField" - << *options.timeseries->getMetaField() << "bucketMaxSpanSeconds" - << *options.timeseries->getBucketMaxSpanSeconds() << "exclude" - << BSONArray()))); - } else { - viewOptions.pipeline = BSON_ARRAY(BSON( - "$_internalUnpackBucket" - << BSON("timeField" << options.timeseries->getTimeField() << "bucketMaxSpanSeconds" - << *options.timeseries->getBucketMaxSpanSeconds() << "exclude" - << BSONArray()))); - } + constexpr bool asArray = true; + viewOptions.pipeline = timeseries::generateViewPipeline(*options.timeseries, asArray); // Create the time-series view. auto status = db->userCreateNS(opCtx, ns, viewOptions); diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index 888ead550e0..7293ed47275 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -110,8 +110,8 @@ namespace { * Returns a CollMod on the underlying buckets collection of the time-series collection. * Returns null if 'origCmd' is not for a time-series collection. */ -std::unique_ptr<CollMod> makeTimeseriesCollModCommand(OperationContext* opCtx, - const CollMod& origCmd) { +std::unique_ptr<CollMod> makeTimeseriesBucketsCollModCommand(OperationContext* opCtx, + const CollMod& origCmd) { const auto& origNs = origCmd.getNamespace(); auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, origNs); @@ -149,6 +149,43 @@ std::unique_ptr<CollMod> makeTimeseriesCollModCommand(OperationContext* opCtx, return cmd; } +/** + * Returns a CollMod on the view definition of the time-series collection. + * Returns null if 'origCmd' is not for a time-series collection or if the view definition need not + * be changed. + */ +std::unique_ptr<CollMod> makeTimeseriesViewCollModCommand(OperationContext* opCtx, + const CollMod& origCmd) { + const auto& ns = origCmd.getNamespace(); + + auto timeseriesOptions = timeseries::getTimeseriesOptions(opCtx, ns); + + // Return early with null if we are not working with a time-series collection. + if (!timeseriesOptions) { + return {}; + } + + auto& tsMod = origCmd.getTimeseries(); + if (tsMod) { + auto res = + timeseries::applyTimeseriesOptionsModifications(*timeseriesOptions, tsMod->toBSON()); + if (res.isOK()) { + auto& [newOptions, changed] = res.getValue(); + if (changed) { + auto cmd = std::make_unique<CollMod>(ns); + constexpr bool asArray = false; + std::vector<BSONObj> pipeline = { + timeseries::generateViewPipeline(newOptions, asArray)}; + cmd->setPipeline(std::move(pipeline)); + return cmd; + } + } + } + + return {}; +} + + class CmdDropDatabase : public DropDatabaseCmdVersion1Gen<CmdDropDatabase> { public: std::string help() const final { @@ -579,12 +616,29 @@ public: // time-series collection, which are important for maintaining the view-bucket // relationship. // - // 'timeseriesCmd' is null if the request namespace does not refer to a time-series - // collection. Otherwise, transforms the user time-series index request to one on the - // underlying bucket. - auto timeseriesCmd = makeTimeseriesCollModCommand(opCtx, requestParser.request()); - if (timeseriesCmd) { - cmd = timeseriesCmd.get(); + // 'timeseriesBucketsCmd' is null if the request namespace does not refer to a time-series + // collection. Otherwise, transforms the user time-series collMod request to one on the + // underlying bucket collection. + auto timeseriesBucketsCmd = + makeTimeseriesBucketsCollModCommand(opCtx, requestParser.request()); + if (timeseriesBucketsCmd) { + // We additionally create a special, limited collMod command for the view definition + // itself if the pipeline needs to be updated to reflect changed timeseries options. + // This operation is completed first. In the case that we get a partial update where + // only one of the two collMod operations fully completes (e.g. replication rollback), + // having the view pipeline update without updating the timeseries options on the + // buckets collection will result in sub-optimal performance, but correct behavior. + // If the timeseries options were updated without updating the view pipeline, we could + // end up with incorrect query behavior (namely data missing from some queries). + auto timeseriesViewCmd = + makeTimeseriesViewCollModCommand(opCtx, requestParser.request()); + if (timeseriesViewCmd) { + uassertStatusOK(collMod(opCtx, + timeseriesViewCmd->getNamespace(), + timeseriesViewCmd->toBSON(BSONObj()), + &result)); + } + cmd = timeseriesBucketsCmd.get(); } uassertStatusOK(collMod(opCtx, cmd->getNamespace(), cmd->toBSON(BSONObj()), &result)); diff --git a/src/mongo/db/timeseries/timeseries_options.cpp b/src/mongo/db/timeseries/timeseries_options.cpp index 6ba0a5b7480..00921c7d34d 100644 --- a/src/mongo/db/timeseries/timeseries_options.cpp +++ b/src/mongo/db/timeseries/timeseries_options.cpp @@ -37,6 +37,45 @@ namespace mongo { namespace timeseries { +namespace { + +BSONObj wrapInArrayIf(bool doWrap, BSONObj&& obj) { + if (doWrap) { + return (::mongo::BSONArrayBuilder() << std::move(obj)).arr(); + } + return std::move(obj); +} + + +bool isValidTimeseriesGranularityTransition(BucketGranularityEnum current, + BucketGranularityEnum target) { + bool validTransition = true; + if (current == target) { + return validTransition; + } + + switch (current) { + case BucketGranularityEnum::Seconds: { + // Both minutes and hours are allowed. + break; + } + case BucketGranularityEnum::Minutes: { + if (target != BucketGranularityEnum::Hours) { + validTransition = false; + } + break; + } + case BucketGranularityEnum::Hours: { + validTransition = false; + break; + } + } + + return validTransition; +} + +} // namespace + boost::optional<TimeseriesOptions> getTimeseriesOptions(OperationContext* opCtx, const NamespaceString& nss) { auto bucketsNs = nss.makeTimeseriesBucketsNamespace(); @@ -79,5 +118,46 @@ int getBucketRoundingSecondsFromGranularity(BucketGranularityEnum granularity) { MONGO_UNREACHABLE; } +StatusWith<std::pair<TimeseriesOptions, bool>> applyTimeseriesOptionsModifications( + const TimeseriesOptions& currentOptions, const BSONObj& mod) { + TimeseriesOptions newOptions = currentOptions; + bool changed = false; + + if (mod.hasField("granularity")) { + BSONElement granularityElem = mod.getField("granularity"); + BucketGranularityEnum target = BucketGranularity_parse( + IDLParserErrorContext("BucketGranularity"), granularityElem.valueStringData()); + if (target != currentOptions.getGranularity()) { + if (!isValidTimeseriesGranularityTransition(currentOptions.getGranularity(), target)) { + return Status{ErrorCodes::InvalidOptions, + "Invalid transition for timeseries.granularity. Can only transition " + "from 'seconds' to 'minutes' or 'minutes' to 'hours'."}; + } + newOptions.setGranularity(target); + newOptions.setBucketMaxSpanSeconds( + timeseries::getMaxSpanSecondsFromGranularity(target)); + changed = true; + } + } + + return std::make_pair(newOptions, changed); +} + +BSONObj generateViewPipeline(const TimeseriesOptions& options, bool asArray) { + if (options.getMetaField()) { + return wrapInArrayIf( + asArray, + BSON("$_internalUnpackBucket" << BSON( + "timeField" << options.getTimeField() << "metaField" << *options.getMetaField() + << "bucketMaxSpanSeconds" << *options.getBucketMaxSpanSeconds() + << "exclude" << BSONArray()))); + } + return wrapInArrayIf( + asArray, + BSON("$_internalUnpackBucket" << BSON( + "timeField" << options.getTimeField() << "bucketMaxSpanSeconds" + << *options.getBucketMaxSpanSeconds() << "exclude" << BSONArray()))); +} + } // namespace timeseries } // namespace mongo diff --git a/src/mongo/db/timeseries/timeseries_options.h b/src/mongo/db/timeseries/timeseries_options.h index e3a605dde73..56361cfbb93 100644 --- a/src/mongo/db/timeseries/timeseries_options.h +++ b/src/mongo/db/timeseries/timeseries_options.h @@ -58,5 +58,10 @@ int getMaxSpanSecondsFromGranularity(BucketGranularityEnum granularity); */ int getBucketRoundingSecondsFromGranularity(BucketGranularityEnum granularity); +StatusWith<std::pair<TimeseriesOptions, bool>> applyTimeseriesOptionsModifications( + const TimeseriesOptions& current, const BSONObj& mod); + +BSONObj generateViewPipeline(const TimeseriesOptions& options, bool asArray); + } // namespace timeseries } // namespace mongo |