diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js index 36cff19e8e4..78422033079 100644 --- a/lib/helpers/query/castUpdate.js +++ b/lib/helpers/query/castUpdate.js @@ -14,6 +14,24 @@ const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol; const setDottedPath = require('../path/setDottedPath'); const utils = require('../../utils'); +const mongodbUpdateOperators = new Set([ + '$currentDate', + '$inc', + '$min', + '$max', + '$mul', + '$rename', + '$set', + '$setOnInsert', + '$unset', + '$addToSet', + '$pop', + '$pull', + '$push', + '$pullAll', + '$bit' +]); + /** * Casts an update op based on the given schema * @@ -58,7 +76,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) { while (i--) { const op = ops[i]; // if overwrite is set, don't do any of the special $set stuff - if (op[0] !== '$' && !overwrite) { + if (!mongodbUpdateOperators.has(op) && !overwrite) { // fix up $set sugar if (!ret.$set) { if (obj.$set) { @@ -88,7 +106,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) { if (val && typeof val === 'object' && !Buffer.isBuffer(val) && - (!overwrite || hasDollarKey)) { + (!overwrite || mongodbUpdateOperators.has(op))) { walkUpdatePath(schema, val, op, options, context, filter); } else if (overwrite && ret && typeof ret === 'object') { walkUpdatePath(schema, ret, '$set', options, context, filter); @@ -540,7 +558,7 @@ function castUpdateVal(schema, val, op, $conditional, context, path) { return Boolean(val); } - if (/^\$/.test($conditional)) { + if (mongodbUpdateOperators.has($conditional)) { return schema.castForQuery( $conditional, val, diff --git a/test/model.updateOne.test.js b/test/model.updateOne.test.js index 99810fb2c77..12ce5723a7e 100644 --- a/test/model.updateOne.test.js +++ b/test/model.updateOne.test.js @@ -3051,6 +3051,23 @@ describe('model: updateOne: ', function() { await Person.updateOne({ name: 'Anakin' }, { name: 'The Chosen One' }).orFail(); }, { message: 'No document found for query "{ name: \'Anakin\' }" on model "gh-11620"' }); }); + it('updateOne with top level key that starts with $ (gh-13786)', async function() { + const [major] = await start.mongodVersion(); + if (major < 5) { + return this.skip(); + } + + const schema = new mongoose.Schema({ + $myKey: String + }); + + const Test = db.model('Test', schema); + + const _id = new mongoose.Types.ObjectId(); + await Test.updateOne({ _id }, { $myKey: 'gh13786' }, { upsert: true }); + const doc = await Test.findById(_id); + assert.equal(doc.$myKey, 'gh13786'); + }); }); async function delay(ms) {