From 1111ae215f2cbed91c374da3413744b61f0c4363 Mon Sep 17 00:00:00 2001 From: Sean Derrow Date: Sun, 28 Apr 2024 17:01:59 -0400 Subject: [PATCH 1/2] Improved timestamps option handling in bulkWrite --- lib/helpers/model/castBulkWrite.js | 33 ++++++++++++++++++++++++------ lib/model.js | 3 +++ types/models.d.ts | 12 ++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/helpers/model/castBulkWrite.js b/lib/helpers/model/castBulkWrite.js index f566764a2e6..01e3cdd36c4 100644 --- a/lib/helpers/model/castBulkWrite.js +++ b/lib/helpers/model/castBulkWrite.js @@ -29,7 +29,7 @@ module.exports = function castBulkWrite(originalModel, op, options) { const model = decideModelByObject(originalModel, op['insertOne']['document']); const doc = new model(op['insertOne']['document']); - if (model.schema.options.timestamps && options.timestamps !== false) { + if (model.schema.options.timestamps && getTimestampsOpt(op['insertOne'], options)) { doc.initializeTimestamps(); } if (options.session != null) { @@ -69,13 +69,15 @@ module.exports = function castBulkWrite(originalModel, op, options) { _addDiscriminatorToObject(schema, op['updateOne']['filter']); - if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) { + const doInitTimestamps = getTimestampsOpt(op['updateOne'], options); + + if (model.schema.$timestamps != null && doInitTimestamps) { const createdAt = model.schema.$timestamps.createdAt; const updatedAt = model.schema.$timestamps.updatedAt; applyTimestampsToUpdate(now, createdAt, updatedAt, update, {}); } - if (op['updateOne'].timestamps !== false) { + if (doInitTimestamps) { applyTimestampsToChildren(now, update, model.schema); } @@ -134,12 +136,14 @@ module.exports = function castBulkWrite(originalModel, op, options) { }); } - if (model.schema.$timestamps != null && op['updateMany'].timestamps !== false) { + const doInitTimestamps = getTimestampsOpt(op['updateMany'], options); + + if (model.schema.$timestamps != null && doInitTimestamps) { const createdAt = model.schema.$timestamps.createdAt; const updatedAt = model.schema.$timestamps.updatedAt; applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateMany']['update'], {}); } - if (op['updateMany'].timestamps !== false) { + if (doInitTimestamps) { applyTimestampsToChildren(now, op['updateMany']['update'], model.schema); } @@ -184,7 +188,7 @@ module.exports = function castBulkWrite(originalModel, op, options) { // set `skipId`, otherwise we get "_id field cannot be changed" const doc = new model(op['replaceOne']['replacement'], strict, true); - if (model.schema.options.timestamps) { + if (model.schema.options.timestamps && getTimestampsOpt(op['replaceOne'], options)) { doc.initializeTimestamps(); } if (options.session != null) { @@ -273,3 +277,20 @@ function decideModelByObject(model, object) { } return model; } + + +/** + * gets timestamps option for a given operation. If the option is set within an individual operation, use it. Otherwise, use the global timestamps option configured in the `bulkWrite` options. Overall default is `true`. + * @api private + */ + +function getTimestampsOpt(opCommand, options) { + const opLevelOpt = opCommand.timestamps; + const bulkLevelOpt = options.timestamps; + if (opLevelOpt != null) { + return opLevelOpt; + } else if (bulkLevelOpt != null) { + return bulkLevelOpt; + } + return true; +} diff --git a/lib/model.js b/lib/model.js index d07fe2b89fb..5e0a105c479 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3467,6 +3467,7 @@ function _setIsNew(doc, val) { * * @param {Array} ops * @param {Object} [ops.insertOne.document] The document to insert + * @param {Object} [ops.insertOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation * @param {Object} [ops.updateOne.filter] Update the first document that matches this filter * @param {Object} [ops.updateOne.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/) * @param {Boolean} [ops.updateOne.upsert=false] If true, insert a doc if none match @@ -3484,8 +3485,10 @@ function _setIsNew(doc, val) { * @param {Object} [ops.replaceOne.filter] Replace the first document that matches this filter * @param {Object} [ops.replaceOne.replacement] The replacement document * @param {Boolean} [ops.replaceOne.upsert=false] If true, insert a doc if no documents match `filter` + * @param {Object} [ops.replaceOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation * @param {Object} [options] * @param {Boolean} [options.ordered=true] If true, execute writes in order and stop at the first error. If false, execute writes in parallel and continue until all writes have either succeeded or errored. + * @param {Boolean} [options.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to any operations. Can be overridden at the operation-level. * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](https://mongoosejs.com/docs/transactions.html). * @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information. * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). diff --git a/types/models.d.ts b/types/models.d.ts index 65893729490..2bbb2663009 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -28,6 +28,8 @@ declare module 'mongoose' { skipValidation?: boolean; throwOnValidationError?: boolean; strict?: boolean | 'throw'; + /** When false, do not add timestamps to documents. Can be overridden at the operation level. */ + timestamps?: boolean; } interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions { @@ -179,7 +181,9 @@ declare module 'mongoose' { }; export interface InsertOneModel { - document: mongodb.OptionalId + document: mongodb.OptionalId; + /** When false, do not add timestamps. When true, overrides the `timestamps` option set in the `bulkWrite` options. */ + timestamps?: boolean; } export interface ReplaceOneModel { @@ -193,6 +197,8 @@ declare module 'mongoose' { hint?: mongodb.Hint; /** When true, creates a new document if no document matches the query. */ upsert?: boolean; + /** When false, do not add timestamps. When true, overrides the `timestamps` option set in the `bulkWrite` options. */ + timestamps?: boolean; } export interface UpdateOneModel { @@ -208,7 +214,7 @@ declare module 'mongoose' { hint?: mongodb.Hint; /** When true, creates a new document if no document matches the query. */ upsert?: boolean; - /** When false, do not add timestamps. */ + /** When false, do not add timestamps. When true, overrides the `timestamps` option set in the `bulkWrite` options. */ timestamps?: boolean; } @@ -225,7 +231,7 @@ declare module 'mongoose' { hint?: mongodb.Hint; /** When true, creates a new document if no document matches the query. */ upsert?: boolean; - /** When false, do not add timestamps. */ + /** When false, do not add timestamps. When true, overrides the `timestamps` option set in the `bulkWrite` options. */ timestamps?: boolean; } From f463240b11055e4641f9d67713a3559316bbd2c5 Mon Sep 17 00:00:00 2001 From: Sean Derrow Date: Sun, 28 Apr 2024 17:47:55 -0400 Subject: [PATCH 2/2] FIx lint --- lib/helpers/model/castBulkWrite.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/model/castBulkWrite.js b/lib/helpers/model/castBulkWrite.js index 01e3cdd36c4..1afb36987fa 100644 --- a/lib/helpers/model/castBulkWrite.js +++ b/lib/helpers/model/castBulkWrite.js @@ -70,7 +70,7 @@ module.exports = function castBulkWrite(originalModel, op, options) { _addDiscriminatorToObject(schema, op['updateOne']['filter']); const doInitTimestamps = getTimestampsOpt(op['updateOne'], options); - + if (model.schema.$timestamps != null && doInitTimestamps) { const createdAt = model.schema.$timestamps.createdAt; const updatedAt = model.schema.$timestamps.updatedAt;