diff --git a/lib/query.js b/lib/query.js index 5db5c0e7ef7..c4a413fad1f 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2416,13 +2416,23 @@ Query.prototype.merge = function(source) { } opts.omit = {}; - if (this._conditions && this._conditions.$and && source.$and) { + if (Array.isArray(source.$and)) { opts.omit['$and'] = true; - this._conditions.$and = this._conditions.$and.concat(source.$and); + if (!this._conditions) { + this._conditions = {}; + } + this._conditions.$and = (this._conditions.$and || []).concat( + source.$and.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el) + ); } - if (this._conditions && this._conditions.$or && source.$or) { + if (Array.isArray(source.$or)) { opts.omit['$or'] = true; - this._conditions.$or = this._conditions.$or.concat(source.$or); + if (!this._conditions) { + this._conditions = {}; + } + this._conditions.$or = (this._conditions.$or || []).concat( + source.$or.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el) + ); } // plain object diff --git a/lib/utils.js b/lib/utils.js index 512b7728a3e..0172ac3e433 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -294,6 +294,8 @@ exports.merge = function merge(to, from, options, path) { to[key] = from[key]; } } + + return to; }; /** diff --git a/test/query.test.js b/test/query.test.js index 42354434d90..61e70674f44 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -4214,4 +4214,19 @@ describe('Query', function() { assert.strictEqual(doc.account.owner, undefined); assert.strictEqual(doc.account.taxIds, undefined); }); + + it('avoids mutating $or, $and elements when casting (gh-14610)', async function() { + const personSchema = new mongoose.Schema({ + name: String, + age: Number + }); + const Person = db.model('Person', personSchema); + + const filter = [{ name: 'Me', age: '20' }, { name: 'You', age: '50' }]; + await Person.find({ $or: filter }); + assert.deepStrictEqual(filter, [{ name: 'Me', age: '20' }, { name: 'You', age: '50' }]); + + await Person.find({ $and: filter }); + assert.deepStrictEqual(filter, [{ name: 'Me', age: '20' }, { name: 'You', age: '50' }]); + }); });