Skip to content

Commit

Permalink
fix(model): deep clone bulkWrite() updateOne arguments to avoid mut…
Browse files Browse the repository at this point in the history
…ating documents in update

Fix #14164
  • Loading branch information
vkarpov15 committed Dec 27, 2023
1 parent 3e99324 commit 4cc757a
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 25 deletions.
1 change: 0 additions & 1 deletion lib/drivers/node-mongodb-native/collection.js
Expand Up @@ -94,7 +94,6 @@ function iter(i) {
NativeCollection.prototype[i] = function() {
const collection = this._getCollection();
const args = Array.from(arguments);
console.log('what is args', args, args[0][0] ? args[0][0].updateOne : '');
const _this = this;
const globalDebug = _this &&
_this.conn &&
Expand Down
32 changes: 15 additions & 17 deletions lib/helpers/model/castBulkWrite.js
Expand Up @@ -6,6 +6,7 @@ const applyTimestampsToChildren = require('../update/applyTimestampsToChildren')
const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
const cast = require('../../cast');
const castUpdate = require('../query/castUpdate');
const clone = require('../clone');
const decorateUpdateWithVersionKey = require('../update/decorateUpdateWithVersionKey');
const { inspect } = require('util');
const setDefaultsOnInsert = require('../setDefaultsOnInsert');
Expand Down Expand Up @@ -64,22 +65,32 @@ module.exports = function castBulkWrite(originalModel, op, options) {
const schema = model.schema;
const strict = options.strict != null ? options.strict : model.schema.options.strict;

const update = clone(op['updateOne']['update']);

_addDiscriminatorToObject(schema, op['updateOne']['filter']);


if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
const createdAt = model.schema.$timestamps.createdAt;
const updatedAt = model.schema.$timestamps.updatedAt;
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {});
}

if (op['updateOne'].timestamps !== false) {
applyTimestampsToChildren(now, update, model.schema);
}

const shouldSetDefaultsOnInsert = op['updateOne'].setDefaultsOnInsert == null ?
globalSetDefaultsOnInsert :
op['updateOne'].setDefaultsOnInsert;
if (shouldSetDefaultsOnInsert !== false) {
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, op['updateOne']['update'], {
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, update, {
setDefaultsOnInsert: true,
upsert: op['updateOne'].upsert
});
}

decorateUpdateWithVersionKey(
op['updateOne']['update'],
update,
op['updateOne'],
model.schema.options.versionKey
);
Expand All @@ -88,23 +99,10 @@ module.exports = function castBulkWrite(originalModel, op, options) {
strict: strict,
upsert: op['updateOne'].upsert
});
console.log('what is op before', op);
// problem is here
op['updateOne']['update'] = castUpdate(model.schema, op['updateOne']['update'], {
op['updateOne']['update'] = castUpdate(model.schema, update, {
strict: strict,
upsert: op['updateOne'].upsert
}, model, op['updateOne']['filter']);
console.log('what is op after', op);

if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
const createdAt = model.schema.$timestamps.createdAt;
const updatedAt = model.schema.$timestamps.updatedAt;
applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateOne']['update'], {});
}

if (op['updateOne'].timestamps !== false) {
applyTimestampsToChildren(now, op['updateOne']['update'], model.schema);
}
} catch (error) {
return callback(error, null);
}
Expand Down
6 changes: 5 additions & 1 deletion lib/helpers/query/castUpdate.js
Expand Up @@ -13,6 +13,7 @@ const moveImmutableProperties = require('../update/moveImmutableProperties');
const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
const setDottedPath = require('../path/setDottedPath');
const utils = require('../../utils');
const { internalToObjectOptions } = require('../../options');

const mongodbUpdateOperators = new Set([
'$currentDate',
Expand Down Expand Up @@ -99,7 +100,10 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
const op = ops[i];
val = ret[op];
hasDollarKey = hasDollarKey || op.startsWith('$');
console.log('what is val', val);
if (val != null && val.$__) {
val = val.toObject(internalToObjectOptions);
ret[op] = val;
}
if (val &&
typeof val === 'object' &&
!Buffer.isBuffer(val) &&
Expand Down
8 changes: 4 additions & 4 deletions test/model.test.js
Expand Up @@ -6410,28 +6410,28 @@ describe('Model', function() {
});
timeDoc.properties.color = 'Red';
const beforeSet = {};
Object.assign(beforeSet, timeDoc);
Object.assign(beforeSet, timeDoc.toObject());
await Time.bulkWrite([{
updateOne: {
filter: { _id: timeDoc._id },
update: { $set: timeDoc }
}
}]);
assert.deepStrictEqual(beforeSet._doc, timeDoc)
assert.deepStrictEqual(beforeSet, timeDoc.toObject());

const timelessDoc = await Timeless.create({
name: 'Timeless Test'
});
timelessDoc.properties.color = 'Red';
const timelessObj = {};
Object.assign(timelessObj, timelessDoc);
Object.assign(timelessObj, timelessDoc.toObject());
await Timeless.bulkWrite([{
updateOne: {
filter: { _id: timelessDoc._id },
update: { $set: timelessDoc }
}
}]);
assert.deepStrictEqual(timelessObj, timelessDoc);
assert.deepStrictEqual(timelessObj, timelessDoc.toObject());
});
});

Expand Down
2 changes: 0 additions & 2 deletions test/schema.select.test.js
Expand Up @@ -15,12 +15,10 @@ describe('schema select option', function() {

before(function() {
db = start();
mongoose.set('debug', true);
});

after(async function() {
await db.close();
mongoose.set('debug', false);
});

beforeEach(() => db.deleteModel(/.*/));
Expand Down

0 comments on commit 4cc757a

Please sign in to comment.