Skip to content

Commit f49c751

Browse files
committed
feat: add index and doc to insertMany validation errors
1 parent 4786c93 commit f49c751

File tree

3 files changed

+80
-0
lines changed

3 files changed

+80
-0
lines changed

lib/model.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3042,6 +3042,9 @@ Model.$__insertMany = function(arr, options, callback) {
30423042
const results = ordered ? null : new Array(arr.length);
30433043
const toExecute = arr.map((doc, index) =>
30443044
callback => {
3045+
// Store original document for error reporting
3046+
const originalDoc = arr[index];
3047+
30453048
// If option `lean` is set to true bypass validation and hydration
30463049
if (lean) {
30473050
// we have to execute callback at the nextTick to be compatible
@@ -3076,6 +3079,9 @@ Model.$__insertMany = function(arr, options, callback) {
30763079
() => { callback(null, doc); },
30773080
error => {
30783081
if (ordered === false) {
3082+
// Add index and doc to validation error for better debugging
3083+
error.index = index;
3084+
error.doc = doc.toObject ? doc.toObject() : originalDoc;
30793085
validationErrors.push(error);
30803086
validationErrorsToOriginalOrder.set(error, index);
30813087
results[index] = error;

test/model.insertMany.test.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,74 @@ describe('insertMany()', function() {
429429
assert.ok(!err.mongoose.validationErrors[1].errors['name']);
430430
});
431431

432+
it('insertMany() validation errors include index and doc properties with ordered false and rawResult', async function() {
433+
const schema = new Schema({
434+
title: { type: String, required: true },
435+
requiredField: { type: String, required: true }
436+
});
437+
const User = db.model('User', schema);
438+
439+
const arr = [
440+
{ title: 'title1', requiredField: 'field1' },
441+
{ title: 'title2' }, // Missing requiredField
442+
{ requiredField: 'field3' }, // Missing title
443+
{ title: 'title4', requiredField: 'field4' }
444+
];
445+
const opts = { ordered: false, rawResult: true };
446+
const res = await User.insertMany(arr, opts);
447+
448+
assert.equal(res.insertedCount, 2);
449+
assert.equal(res.mongoose.validationErrors.length, 2);
450+
451+
// Check first validation error (index 1)
452+
const error1 = res.mongoose.validationErrors[0];
453+
assert.equal(error1.index, 1);
454+
assert.ok(error1.doc);
455+
assert.equal(error1.doc.title, 'title2');
456+
assert.ok(error1.errors['requiredField']);
457+
458+
// Check second validation error (index 2)
459+
const error2 = res.mongoose.validationErrors[1];
460+
assert.equal(error2.index, 2);
461+
assert.ok(error2.doc);
462+
assert.equal(error2.doc.requiredField, 'field3');
463+
assert.ok(error2.errors['title']);
464+
});
465+
466+
it('insertMany() validation errors include index and doc properties with throwOnValidationError', async function() {
467+
const schema = new Schema({
468+
title: { type: String, required: true },
469+
requiredField: { type: String, required: true }
470+
});
471+
const User = db.model('User', schema);
472+
473+
const arr = [
474+
{ title: 'title1', requiredField: 'field1' },
475+
{ title: 'title2' }, // Missing requiredField
476+
{ requiredField: 'field3' }, // Missing title
477+
{ title: 'title4', requiredField: 'field4' }
478+
];
479+
const opts = { ordered: false, rawResult: true, throwOnValidationError: true };
480+
const err = await User.insertMany(arr, opts).then(() => null, err => err);
481+
482+
assert.ok(err);
483+
assert.equal(err.validationErrors.length, 2);
484+
485+
// Check first validation error (index 1)
486+
const error1 = err.validationErrors[0];
487+
assert.equal(error1.index, 1);
488+
assert.ok(error1.doc);
489+
assert.equal(error1.doc.title, 'title2');
490+
assert.ok(error1.errors['requiredField']);
491+
492+
// Check second validation error (index 2)
493+
const error2 = err.validationErrors[1];
494+
assert.equal(error2.index, 2);
495+
assert.ok(error2.doc);
496+
assert.equal(error2.doc.requiredField, 'field3');
497+
assert.ok(error2.errors['title']);
498+
});
499+
432500
it('insertMany() populate option (gh-9720)', async function() {
433501
const schema = new Schema({
434502
name: { type: String, required: true }

types/error.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ declare module 'mongoose' {
9090

9191
errors: { [path: string]: ValidatorError | CastError };
9292
addError: (path: string, error: ValidatorError | CastError) => void;
93+
94+
/** Index of the document in insertMany() that failed validation (only set for unordered insertMany) */
95+
index?: number;
96+
97+
/** Document that failed validation (only set for unordered insertMany) */
98+
doc?: any;
9399

94100
constructor(instance?: MongooseError);
95101
}

0 commit comments

Comments
 (0)