Skip to content

Commit d5e9176

Browse files
authored
Merge pull request #13843 from Automattic/vkarpov15/gh-13782
BREAKING CHANGE: apply minimize by default when updating document
2 parents 2348b7a + 8527c8d commit d5e9176

File tree

4 files changed

+70
-47
lines changed

4 files changed

+70
-47
lines changed

lib/document.js

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const isExclusive = require('./helpers/projection/isExclusive');
3333
const inspect = require('util').inspect;
3434
const internalToObjectOptions = require('./options').internalToObjectOptions;
3535
const markArraySubdocsPopulated = require('./helpers/populate/markArraySubdocsPopulated');
36+
const minimize = require('./helpers/minimize');
3637
const mpath = require('mpath');
3738
const queryhelpers = require('./queryHelpers');
3839
const utils = require('./utils');
@@ -3938,42 +3939,6 @@ Document.prototype.toObject = function(options) {
39383939
return this.$toObject(options);
39393940
};
39403941

3941-
/**
3942-
* Minimizes an object, removing undefined values and empty objects
3943-
*
3944-
* @param {Object} object to minimize
3945-
* @return {Object}
3946-
* @api private
3947-
*/
3948-
3949-
function minimize(obj) {
3950-
const keys = Object.keys(obj);
3951-
let i = keys.length;
3952-
let hasKeys;
3953-
let key;
3954-
let val;
3955-
3956-
while (i--) {
3957-
key = keys[i];
3958-
val = obj[key];
3959-
3960-
if (utils.isPOJO(val)) {
3961-
obj[key] = minimize(val);
3962-
}
3963-
3964-
if (undefined === obj[key]) {
3965-
delete obj[key];
3966-
continue;
3967-
}
3968-
3969-
hasKeys = true;
3970-
}
3971-
3972-
return hasKeys
3973-
? obj
3974-
: undefined;
3975-
}
3976-
39773942
/*!
39783943
* Applies virtuals properties to `json`.
39793944
*/

lib/helpers/minimize.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
const { isPOJO } = require('../utils');
4+
5+
module.exports = minimize;
6+
7+
/**
8+
* Minimizes an object, removing undefined values and empty objects
9+
*
10+
* @param {Object} object to minimize
11+
* @return {Object|undefined}
12+
* @api private
13+
*/
14+
15+
function minimize(obj) {
16+
const keys = Object.keys(obj);
17+
let i = keys.length;
18+
let hasKeys;
19+
let key;
20+
let val;
21+
22+
while (i--) {
23+
key = keys[i];
24+
val = obj[key];
25+
26+
if (isPOJO(val)) {
27+
obj[key] = minimize(val);
28+
}
29+
30+
if (undefined === obj[key]) {
31+
delete obj[key];
32+
continue;
33+
}
34+
35+
hasKeys = true;
36+
}
37+
38+
return hasKeys
39+
? obj
40+
: undefined;
41+
}

lib/model.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const STATES = require('./connectionState');
6666
const util = require('util');
6767
const utils = require('./utils');
6868
const MongooseBulkWriteError = require('./error/bulkWriteError');
69+
const minimize = require('./helpers/minimize');
6970

7071
const VERSION_WHERE = 1;
7172
const VERSION_INC = 2;
@@ -342,7 +343,19 @@ Model.prototype.$__handleSave = function(options, callback) {
342343
}
343344

344345
_applyCustomWhere(this, where);
345-
this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions).then(
346+
347+
const update = delta[1];
348+
if (this.$__schema.options.minimize) {
349+
minimize(update);
350+
// minimize might leave us with an empty object, which would
351+
// lead to MongoDB throwing a "Update document requires atomic operators" error
352+
if (Object.keys(update).length === 0) {
353+
handleEmptyUpdate.call(this);
354+
return;
355+
}
356+
}
357+
358+
this[modelCollectionSymbol].updateOne(where, update, saveOptions).then(
346359
ret => {
347360
ret.$where = where;
348361
callback(null, ret);
@@ -354,6 +367,17 @@ Model.prototype.$__handleSave = function(options, callback) {
354367
}
355368
);
356369
} else {
370+
handleEmptyUpdate.call(this);
371+
return;
372+
}
373+
374+
// store the modified paths before the document is reset
375+
this.$__.modifiedPaths = this.modifiedPaths();
376+
this.$__reset();
377+
378+
_setIsNew(this, false);
379+
380+
function handleEmptyUpdate() {
357381
const optionsWithCustomValues = Object.assign({}, options, saveOptions);
358382
const where = this.$__where();
359383
const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
@@ -370,14 +394,7 @@ Model.prototype.$__handleSave = function(options, callback) {
370394
callback(null, { $where: where, matchedCount });
371395
})
372396
.catch(callback);
373-
return;
374397
}
375-
376-
// store the modified paths before the document is reset
377-
this.$__.modifiedPaths = this.modifiedPaths();
378-
this.$__reset();
379-
380-
_setIsNew(this, false);
381398
};
382399

383400
/*!

test/document.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,7 +3243,7 @@ describe('document', function() {
32433243
field1: { type: Number, default: 1 }
32443244
}
32453245
}
3246-
});
3246+
}, { minimize: false });
32473247

32483248
const MyModel = db.model('Test', schema);
32493249

@@ -3262,7 +3262,7 @@ describe('document', function() {
32623262
field1: { type: Number, default: 1 }
32633263
}
32643264
}
3265-
});
3265+
}, { minimize: false });
32663266

32673267
const MyModel = db.model('Test', schema);
32683268

@@ -5002,7 +5002,7 @@ describe('document', function() {
50025002
}).
50035003
then(function(doc) {
50045004
doc.child = {};
5005-
return doc.save();
5005+
return Parent.updateOne({ _id: doc._id }, { $set: { child: {} } }, { minimize: false });
50065006
}).
50075007
then(function() {
50085008
return Parent.findOne();

0 commit comments

Comments
 (0)