Skip to content

Commit

Permalink
Merge branch 'master' into gh-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Feb 25, 2025
2 parents eff9490 + b22e9a8 commit a4f252d
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 69 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
8.10.2 / 2025-02-25
===================
* fix(model+connection): return MongoDB BulkWriteResult instance even if no valid ops #15266 #15265
* fix(debug): avoid printing trusted symbol in debug output #15267 #15263
* types: make type inference logic resilient to no Buffer type due to missing @types/node #15261

8.10.1 / 2025-02-14
===================
* perf(document): only call undoReset() 1x/document #15257 #15255
Expand Down
16 changes: 9 additions & 7 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const CreateCollectionsError = require('./error/createCollectionsError');
const castBulkWrite = require('./helpers/model/castBulkWrite');
const { modelSymbol } = require('./helpers/symbols');
const isPromise = require('./helpers/isPromise');
const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');

const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
Expand Down Expand Up @@ -559,24 +560,27 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
'bulkWrite'
);
}
return getDefaultBulkwriteResult();
const BulkWriteResult = this.base.driver.get().BulkWriteResult;
const res = new BulkWriteResult(getDefaultBulkwriteResult(), false);
return decorateBulkWriteResult(res, validationErrors, results);
}

let error;
[res, error] = await this.client.bulkWrite(validOps, options).
then(res => ([res, null])).
catch(err => ([null, err]));

for (let i = 0; i < validOpIndexes.length; ++i) {
results[validOpIndexes[i]] = null;
}
if (error) {
if (validationErrors.length > 0) {
decorateBulkWriteResult(error, validationErrors, results);
error.mongoose = error.mongoose || {};
error.mongoose.validationErrors = validationErrors;
}
}

for (let i = 0; i < validOpIndexes.length; ++i) {
results[validOpIndexes[i]] = null;
}
if (validationErrors.length > 0) {
if (options.throwOnValidationError) {
throw new MongooseBulkWriteError(
Expand All @@ -586,9 +590,7 @@ Connection.prototype.bulkWrite = async function bulkWrite(ops, options) {
'bulkWrite'
);
} else {
res.mongoose = res.mongoose || {};
res.mongoose.validationErrors = validationErrors;
res.mongoose.results = results;
decorateBulkWriteResult(res, validationErrors, results);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/drivers/browser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ exports.Collection = function() {
exports.Connection = function() {
throw new Error('Cannot create a connection from browser library');
};
exports.BulkWriteResult = function() {};
5 changes: 5 additions & 0 deletions lib/drivers/node-mongodb-native/bulkWriteResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

const BulkWriteResult = require('mongodb/lib/bulk/common').BulkWriteResult;

module.exports = BulkWriteResult;
6 changes: 5 additions & 1 deletion lib/drivers/node-mongodb-native/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const internalToObjectOptions = require('../../options').internalToObjectOptions
const stream = require('stream');
const util = require('util');

const formatToObjectOptions = Object.freeze({ ...internalToObjectOptions, copyTrustedSymbol: false });

/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
*
Expand Down Expand Up @@ -384,7 +386,9 @@ function format(obj, sub, color, shell) {
}

const clone = require('../../helpers/clone');
let x = clone(obj, internalToObjectOptions);
// `sub` indicates `format()` was called recursively, so skip cloning because we already
// did a deep clone on the top-level object.
let x = sub ? obj : clone(obj, formatToObjectOptions);
const constructorName = getConstructorName(x);

if (constructorName === 'Binary') {
Expand Down
1 change: 1 addition & 0 deletions lib/drivers/node-mongodb-native/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@

'use strict';

exports.BulkWriteResult = require('./bulkWriteResult');
exports.Collection = require('./collection');
exports.Connection = require('./connection');
2 changes: 1 addition & 1 deletion lib/helpers/clone.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ function cloneObject(obj, options, isArrayChild) {
} else if (seen) {
seen.set(obj, ret);
}
if (trustedSymbol in obj) {
if (trustedSymbol in obj && options?.copyTrustedSymbol !== false) {
ret[trustedSymbol] = obj[trustedSymbol];
}

Expand Down
31 changes: 11 additions & 20 deletions lib/helpers/getDefaultBulkwriteResult.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
'use strict';

function getDefaultBulkwriteResult() {
return {
result: {
ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [],
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: []
},
insertedCount: 0,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: {},
n: 0
ok: 1,
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: [],
writeErrors: [],
insertedIds: [],
writeConcernErrors: []
};
}

Expand Down
8 changes: 8 additions & 0 deletions lib/helpers/model/decorateBulkWriteResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

module.exports = function decorateBulkWriteResult(resultOrError, validationErrors, results) {
resultOrError.mongoose = resultOrError.mongoose || {};
resultOrError.mongoose.validationErrors = validationErrors;
resultOrError.mongoose.results = results;
return resultOrError;
};
29 changes: 17 additions & 12 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const Document = require('./document');
const DocumentNotFoundError = require('./error/notFound');
const EventEmitter = require('events').EventEmitter;
const Kareem = require('kareem');
const { MongoBulkWriteError } = require('mongodb');
const MongooseBulkWriteError = require('./error/bulkWriteError');
const MongooseError = require('./error/index');
const ObjectParameterError = require('./error/objectParameter');
Expand Down Expand Up @@ -69,6 +68,7 @@ const utils = require('./utils');
const minimize = require('./helpers/minimize');
const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError');
const ObjectExpectedError = require('./error/objectExpected');
const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult');

const modelCollectionSymbol = Symbol('mongoose#Model#collection');
const modelDbSymbol = Symbol('mongoose#Model#db');
Expand Down Expand Up @@ -3399,7 +3399,11 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
const ordered = options.ordered == null ? true : options.ordered;

if (ops.length === 0) {
return getDefaultBulkwriteResult();
const BulkWriteResult = this.base.driver.get().BulkWriteResult;
const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
bulkWriteResult.n = 0;
decorateBulkWriteResult(bulkWriteResult, [], []);
return bulkWriteResult;
}

const validations = ops.map(op => castBulkWrite(this, op, options));
Expand Down Expand Up @@ -3470,18 +3474,24 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
'bulkWrite'
);
}
return getDefaultBulkwriteResult();
const BulkWriteResult = this.base.driver.get().BulkWriteResult;
const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false);
bulkWriteResult.result = getDefaultBulkwriteResult();
decorateBulkWriteResult(bulkWriteResult, validationErrors, results);
return bulkWriteResult;
}

let error;
[res, error] = await this.$__collection.bulkWrite(validOps, options).
then(res => ([res, null])).
catch(error => ([null, error]));

for (let i = 0; i < validOpIndexes.length; ++i) {
results[validOpIndexes[i]] = null;
}
if (error) {
if (validationErrors.length > 0) {
error.mongoose = error.mongoose || {};
error.mongoose.validationErrors = validationErrors;
decorateBulkWriteResult(error, validationErrors, results);
}

await new Promise((resolve, reject) => {
Expand All @@ -3495,9 +3505,6 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
});
}

for (let i = 0; i < validOpIndexes.length; ++i) {
results[validOpIndexes[i]] = null;
}
if (validationErrors.length > 0) {
if (options.throwOnValidationError) {
throw new MongooseBulkWriteError(
Expand All @@ -3507,9 +3514,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
'bulkWrite'
);
} else {
res.mongoose = res.mongoose || {};
res.mongoose.validationErrors = validationErrors;
res.mongoose.results = results;
decorateBulkWriteResult(res, validationErrors, results);
}
}
}
Expand Down Expand Up @@ -3575,7 +3580,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
(err) => ({ bulkWriteResult: null, bulkWriteError: err })
);
// If not a MongoBulkWriteError, treat this as all documents failed to save.
if (bulkWriteError != null && !(bulkWriteError instanceof MongoBulkWriteError)) {
if (bulkWriteError != null && bulkWriteError.name !== 'MongoBulkWriteError') {
throw bulkWriteError;
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
"version": "8.10.1",
"version": "8.10.2",
"author": "Guillermo Rauch <guillermo@learnboost.com>",
"keywords": [
"mongodb",
Expand Down
66 changes: 40 additions & 26 deletions test/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4121,7 +4121,7 @@ describe('Model', function() {
{ ordered: false, throwOnValidationError: true }
).then(() => null, err => err);
assert.ok(err);
assert.equal(err.name, 'MongooseBulkWriteError');
assert.equal(err.name, 'MongooseBulkWriteError', err.stack);
assert.equal(err.validationErrors[0].errors['num'].name, 'CastError');
});

Expand Down Expand Up @@ -6492,17 +6492,9 @@ describe('Model', function() {
assert.deepEqual(
res,
{
result: {
ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [],
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: []
mongoose: {
results: [],
validationErrors: []
},
insertedCount: 0,
matchedCount: 0,
Expand All @@ -6514,7 +6506,20 @@ describe('Model', function() {
n: 0
}
);

assert.deepEqual(res.result, {
ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [],
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: []
});

assert.equal(typeof res.getWriteErrorAt, 'function');
});

it('Model.bulkWrite(...) does not throw an error with upsert:true, setDefaultsOnInsert: true (gh-9157)', async function() {
Expand Down Expand Up @@ -6554,28 +6559,37 @@ describe('Model', function() {
assert.deepEqual(
res,
{
result: {
ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [],
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: []
},
insertedCount: 0,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds: {},
n: 0
n: 0,
mongoose: {
results: [],
validationErrors: []
}
}
);
assert.deepEqual(
res.result,
{
ok: 1,
writeErrors: [],
writeConcernErrors: [],
insertedIds: [],
nInserted: 0,
nUpserted: 0,
nMatched: 0,
nModified: 0,
nRemoved: 0,
upserted: []
}
);

assert.equal(typeof res.getWriteErrorAt, 'function');
});

it('allows calling `create()` after `bulkWrite()` (gh-9350)', async function() {
Expand Down
2 changes: 1 addition & 1 deletion types/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ declare module 'mongoose' {
bulkWrite<DocContents = TRawDocType>(
writes: Array<AnyBulkWriteOperation<DocContents extends Document ? any : (DocContents extends {} ? DocContents : any)>>,
options: MongooseBulkWriteOptions & { ordered: false }
): Promise<mongodb.BulkWriteResult & { mongoose?: { validationErrors: Error[] } }>;
): Promise<mongodb.BulkWriteResult & { mongoose?: { validationErrors: Error[], results: Array<Error | null> } }>;
bulkWrite<DocContents = TRawDocType>(
writes: Array<AnyBulkWriteOperation<DocContents extends Document ? any : (DocContents extends {} ? DocContents : any)>>,
options?: MongooseBulkWriteOptions
Expand Down

0 comments on commit a4f252d

Please sign in to comment.