From 0991ae63e0c76e41b02e1152c6f554fb5fce4fc2 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 6 Jan 2025 14:23:18 -0500 Subject: [PATCH] fix(model): allow passing validateBeforeSave option to bulkSave() to skip validation Fix #15156 --- lib/model.js | 12 +++++------- test/document.test.js | 14 ++++++++++++-- types/models.d.ts | 1 + 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/model.js b/lib/model.js index 67ccfb83a6..a8ff0e94bc 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3436,6 +3436,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) { * @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). * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option) + * @param {Boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents * @return {BulkWriteResult} the return value from `bulkWrite()` */ Model.bulkSave = async function bulkSave(documents, options) { @@ -3455,15 +3456,13 @@ Model.bulkSave = async function bulkSave(documents, options) { } } - await Promise.all(documents.map(buildPreSavePromise)); + await Promise.all(documents.map(doc => buildPreSavePromise(doc, options))); const writeOperations = this.buildBulkWriteOperations(documents, { skipValidation: true, timestamps: options.timestamps }); - - const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, options).then( + const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, { skipValidation: true, ...options }).then( (res) => ({ bulkWriteResult: res, bulkWriteError: null }), (err) => ({ bulkWriteResult: null, bulkWriteError: err }) ); - // If not a MongoBulkWriteError, treat this as all documents failed to save. if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) { throw bulkWriteError; @@ -3491,7 +3490,6 @@ Model.bulkSave = async function bulkSave(documents, options) { successfulDocuments.push(document); } } - await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document))); if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) { @@ -3501,9 +3499,9 @@ Model.bulkSave = async function bulkSave(documents, options) { return bulkWriteResult; }; -function buildPreSavePromise(document) { +function buildPreSavePromise(document, options) { return new Promise((resolve, reject) => { - document.schema.s.hooks.execPre('save', document, (err) => { + document.schema.s.hooks.execPre('save', document, [options], (err) => { if (err) { reject(err); return; diff --git a/test/document.test.js b/test/document.test.js index 4317795223..7a2e1e607a 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -625,12 +625,12 @@ describe('document', function() { assert.equal(doc.val, 'test2'); }); - it('allows you to skip validation on save (gh-2981)', function() { + it('allows you to skip validation on save (gh-2981)', async function() { const schema = new Schema({ name: { type: String, required: true } }); const MyModel = db.model('Test', schema); const doc = new MyModel(); - return doc.save({ validateBeforeSave: false }); + await doc.save({ validateBeforeSave: false }); }); it('doesnt use custom toObject options on save', async function() { @@ -12752,6 +12752,16 @@ describe('document', function() { assert.equal(updatedPerson?._age, 61); }); + it('bulkSave() allows skipping validation with validateBeforeSave (gh-15156)', async() => { + const schema = new Schema({ name: { type: String, required: true } }); + const MyModel = db.model('Test', schema); + + const doc = new MyModel(); + await MyModel.bulkSave([doc], { validateBeforeSave: false }); + + assert.ok(await MyModel.exists({ _id: doc._id })); + }); + it('handles default embedded discriminator values (gh-13835)', async function() { const childAbstractSchema = new Schema( { kind: { type: Schema.Types.String, enum: ['concreteKind'], required: true, default: 'concreteKind' } }, diff --git a/types/models.d.ts b/types/models.d.ts index 99028762f2..fde1ad2612 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -35,6 +35,7 @@ declare module 'mongoose' { interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions { timestamps?: boolean; session?: ClientSession; + validateBeforeSave?: boolean; } /**