Skip to content

Commit

Permalink
fix(model): allow passing validateBeforeSave option to bulkSave() to …
Browse files Browse the repository at this point in the history
…skip validation

Fix #15156
  • Loading branch information
vkarpov15 committed Jan 6, 2025
1 parent bf0140a commit 0991ae6
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 9 deletions.
12 changes: 5 additions & 7 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
14 changes: 12 additions & 2 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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' } },
Expand Down
1 change: 1 addition & 0 deletions types/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ declare module 'mongoose' {
interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions {
timestamps?: boolean;
session?: ClientSession;
validateBeforeSave?: boolean;
}

/**
Expand Down

0 comments on commit 0991ae6

Please sign in to comment.