Skip to content

Commit 29de9c4

Browse files
authored
Merge pull request #13786 from Automattic/vkarpov15/handle-top-level-dollar-keys
feat: allow top level dollar keys with findOneAndUpdate(), update()
2 parents c1d5b76 + 0d956ce commit 29de9c4

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

lib/helpers/query/castUpdate.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
1414
const setDottedPath = require('../path/setDottedPath');
1515
const utils = require('../../utils');
1616

17+
const mongodbUpdateOperators = new Set([
18+
'$currentDate',
19+
'$inc',
20+
'$min',
21+
'$max',
22+
'$mul',
23+
'$rename',
24+
'$set',
25+
'$setOnInsert',
26+
'$unset',
27+
'$addToSet',
28+
'$pop',
29+
'$pull',
30+
'$push',
31+
'$pullAll',
32+
'$bit'
33+
]);
34+
1735
/**
1836
* Casts an update op based on the given schema
1937
*
@@ -58,7 +76,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
5876
while (i--) {
5977
const op = ops[i];
6078
// if overwrite is set, don't do any of the special $set stuff
61-
if (op[0] !== '$' && !overwrite) {
79+
if (!mongodbUpdateOperators.has(op) && !overwrite) {
6280
// fix up $set sugar
6381
if (!ret.$set) {
6482
if (obj.$set) {
@@ -88,7 +106,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
88106
if (val &&
89107
typeof val === 'object' &&
90108
!Buffer.isBuffer(val) &&
91-
(!overwrite || hasDollarKey)) {
109+
(!overwrite || mongodbUpdateOperators.has(op))) {
92110
walkUpdatePath(schema, val, op, options, context, filter);
93111
} else if (overwrite && ret && typeof ret === 'object') {
94112
walkUpdatePath(schema, ret, '$set', options, context, filter);
@@ -540,7 +558,7 @@ function castUpdateVal(schema, val, op, $conditional, context, path) {
540558
return Boolean(val);
541559
}
542560

543-
if (/^\$/.test($conditional)) {
561+
if (mongodbUpdateOperators.has($conditional)) {
544562
return schema.castForQuery(
545563
$conditional,
546564
val,

test/model.updateOne.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,6 +3051,23 @@ describe('model: updateOne: ', function() {
30513051
await Person.updateOne({ name: 'Anakin' }, { name: 'The Chosen One' }).orFail();
30523052
}, { message: 'No document found for query "{ name: \'Anakin\' }" on model "gh-11620"' });
30533053
});
3054+
it('updateOne with top level key that starts with $ (gh-13786)', async function() {
3055+
const [major] = await start.mongodVersion();
3056+
if (major < 5) {
3057+
return this.skip();
3058+
}
3059+
3060+
const schema = new mongoose.Schema({
3061+
$myKey: String
3062+
});
3063+
3064+
const Test = db.model('Test', schema);
3065+
3066+
const _id = new mongoose.Types.ObjectId();
3067+
await Test.updateOne({ _id }, { $myKey: 'gh13786' }, { upsert: true });
3068+
const doc = await Test.findById(_id);
3069+
assert.equal(doc.$myKey, 'gh13786');
3070+
});
30543071
});
30553072

30563073
async function delay(ms) {

0 commit comments

Comments
 (0)