Skip to content

Commit 4cc757a

Browse files
committed
fix(model): deep clone bulkWrite() updateOne arguments to avoid mutating documents in update
Fix #14164
1 parent 3e99324 commit 4cc757a

File tree

5 files changed

+24
-25
lines changed

5 files changed

+24
-25
lines changed

lib/drivers/node-mongodb-native/collection.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ function iter(i) {
9494
NativeCollection.prototype[i] = function() {
9595
const collection = this._getCollection();
9696
const args = Array.from(arguments);
97-
console.log('what is args', args, args[0][0] ? args[0][0].updateOne : '');
9897
const _this = this;
9998
const globalDebug = _this &&
10099
_this.conn &&

lib/helpers/model/castBulkWrite.js

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const applyTimestampsToChildren = require('../update/applyTimestampsToChildren')
66
const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
77
const cast = require('../../cast');
88
const castUpdate = require('../query/castUpdate');
9+
const clone = require('../clone');
910
const decorateUpdateWithVersionKey = require('../update/decorateUpdateWithVersionKey');
1011
const { inspect } = require('util');
1112
const setDefaultsOnInsert = require('../setDefaultsOnInsert');
@@ -64,22 +65,32 @@ module.exports = function castBulkWrite(originalModel, op, options) {
6465
const schema = model.schema;
6566
const strict = options.strict != null ? options.strict : model.schema.options.strict;
6667

68+
const update = clone(op['updateOne']['update']);
69+
6770
_addDiscriminatorToObject(schema, op['updateOne']['filter']);
6871

69-
72+
if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
73+
const createdAt = model.schema.$timestamps.createdAt;
74+
const updatedAt = model.schema.$timestamps.updatedAt;
75+
applyTimestampsToUpdate(now, createdAt, updatedAt, update, {});
76+
}
77+
78+
if (op['updateOne'].timestamps !== false) {
79+
applyTimestampsToChildren(now, update, model.schema);
80+
}
7081

7182
const shouldSetDefaultsOnInsert = op['updateOne'].setDefaultsOnInsert == null ?
7283
globalSetDefaultsOnInsert :
7384
op['updateOne'].setDefaultsOnInsert;
7485
if (shouldSetDefaultsOnInsert !== false) {
75-
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, op['updateOne']['update'], {
86+
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, update, {
7687
setDefaultsOnInsert: true,
7788
upsert: op['updateOne'].upsert
7889
});
7990
}
8091

8192
decorateUpdateWithVersionKey(
82-
op['updateOne']['update'],
93+
update,
8394
op['updateOne'],
8495
model.schema.options.versionKey
8596
);
@@ -88,23 +99,10 @@ module.exports = function castBulkWrite(originalModel, op, options) {
8899
strict: strict,
89100
upsert: op['updateOne'].upsert
90101
});
91-
console.log('what is op before', op);
92-
// problem is here
93-
op['updateOne']['update'] = castUpdate(model.schema, op['updateOne']['update'], {
102+
op['updateOne']['update'] = castUpdate(model.schema, update, {
94103
strict: strict,
95104
upsert: op['updateOne'].upsert
96105
}, model, op['updateOne']['filter']);
97-
console.log('what is op after', op);
98-
99-
if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
100-
const createdAt = model.schema.$timestamps.createdAt;
101-
const updatedAt = model.schema.$timestamps.updatedAt;
102-
applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateOne']['update'], {});
103-
}
104-
105-
if (op['updateOne'].timestamps !== false) {
106-
applyTimestampsToChildren(now, op['updateOne']['update'], model.schema);
107-
}
108106
} catch (error) {
109107
return callback(error, null);
110108
}

lib/helpers/query/castUpdate.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const moveImmutableProperties = require('../update/moveImmutableProperties');
1313
const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
1414
const setDottedPath = require('../path/setDottedPath');
1515
const utils = require('../../utils');
16+
const { internalToObjectOptions } = require('../../options');
1617

1718
const mongodbUpdateOperators = new Set([
1819
'$currentDate',
@@ -99,7 +100,10 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
99100
const op = ops[i];
100101
val = ret[op];
101102
hasDollarKey = hasDollarKey || op.startsWith('$');
102-
console.log('what is val', val);
103+
if (val != null && val.$__) {
104+
val = val.toObject(internalToObjectOptions);
105+
ret[op] = val;
106+
}
103107
if (val &&
104108
typeof val === 'object' &&
105109
!Buffer.isBuffer(val) &&

test/model.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6410,28 +6410,28 @@ describe('Model', function() {
64106410
});
64116411
timeDoc.properties.color = 'Red';
64126412
const beforeSet = {};
6413-
Object.assign(beforeSet, timeDoc);
6413+
Object.assign(beforeSet, timeDoc.toObject());
64146414
await Time.bulkWrite([{
64156415
updateOne: {
64166416
filter: { _id: timeDoc._id },
64176417
update: { $set: timeDoc }
64186418
}
64196419
}]);
6420-
assert.deepStrictEqual(beforeSet._doc, timeDoc)
6420+
assert.deepStrictEqual(beforeSet, timeDoc.toObject());
64216421

64226422
const timelessDoc = await Timeless.create({
64236423
name: 'Timeless Test'
64246424
});
64256425
timelessDoc.properties.color = 'Red';
64266426
const timelessObj = {};
6427-
Object.assign(timelessObj, timelessDoc);
6427+
Object.assign(timelessObj, timelessDoc.toObject());
64286428
await Timeless.bulkWrite([{
64296429
updateOne: {
64306430
filter: { _id: timelessDoc._id },
64316431
update: { $set: timelessDoc }
64326432
}
64336433
}]);
6434-
assert.deepStrictEqual(timelessObj, timelessDoc);
6434+
assert.deepStrictEqual(timelessObj, timelessDoc.toObject());
64356435
});
64366436
});
64376437

test/schema.select.test.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ describe('schema select option', function() {
1515

1616
before(function() {
1717
db = start();
18-
mongoose.set('debug', true);
1918
});
2019

2120
after(async function() {
2221
await db.close();
23-
mongoose.set('debug', false);
2422
});
2523

2624
beforeEach(() => db.deleteModel(/.*/));

0 commit comments

Comments
 (0)