Skip to content

Commit

Permalink
Merge branch 'master' into 8.4
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed May 13, 2024
2 parents bf2f35d + 3526799 commit c144bf0
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
14 changes: 10 additions & 4 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -2382,13 +2382,19 @@ Query.prototype.merge = function(source) {
}

opts.omit = {};
if (this._conditions && this._conditions.$and && source.$and) {
if (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);
}
if (this._conditions && this._conditions.$or && source.$or) {
if (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);
}

// plain object
Expand Down
11 changes: 9 additions & 2 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const isObject = require('./helpers/isObject');
const isMongooseArray = require('./types/array/isMongooseArray');
const isMongooseDocumentArray = require('./types/documentArray/isMongooseDocumentArray');
const isBsonType = require('./helpers/isBsonType');
const isPOJO = require('./helpers/isPOJO');
const getFunctionName = require('./helpers/getFunctionName');
const isMongooseObject = require('./helpers/isMongooseObject');
const promiseOrCallback = require('./helpers/promiseOrCallback');
Expand Down Expand Up @@ -264,7 +265,13 @@ exports.merge = function merge(to, from, options, path) {
continue;
}
if (to[key] == null) {
to[key] = from[key];
if (isPOJO(from[key])) {
to[key] = { ...from[key] };
} else if (Array.isArray(from[key])) {
to[key] = [...from[key]];
} else {
to[key] = from[key];
}
} else if (exports.isObject(from[key])) {
if (!exports.isObject(to[key])) {
to[key] = {};
Expand Down Expand Up @@ -501,7 +508,7 @@ exports.populate = function populate(path, select, model, match, options, subPop
if (path instanceof PopulateOptions) {
// If reusing old populate docs, avoid reusing `_docs` because that may
// lead to bugs and memory leaks. See gh-11641
path._docs = [];
path._docs = {};
path._childDocs = [];
return [path];
}
Expand Down
37 changes: 37 additions & 0 deletions test/query.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4054,6 +4054,25 @@ describe('Query', function() {
});
});

it('shallow clones $and, $or if merging with empty filter (gh-14567) (gh-12944)', function() {
const TestModel = db.model(
'Test',
Schema({ name: String, age: Number, active: Boolean })
);

let originalQuery = { $and: [{ active: true }] };
let q = TestModel.countDocuments(originalQuery)
.and([{ age: { $gte: 18 } }]);
assert.deepStrictEqual(originalQuery, { $and: [{ active: true }] });
assert.deepStrictEqual(q.getFilter(), { $and: [{ active: true }, { age: { $gte: 18 } }] });

originalQuery = { $or: [{ active: true }] };
q = TestModel.countDocuments(originalQuery)
.or([{ age: { $gte: 18 } }]);
assert.deepStrictEqual(originalQuery, { $or: [{ active: true }] });
assert.deepStrictEqual(q.getFilter(), { $or: [{ active: true }, { age: { $gte: 18 } }] });
});

it('should avoid sending empty session to MongoDB server (gh-13052)', async function() {
const m = new mongoose.Mongoose();

Expand Down Expand Up @@ -4236,4 +4255,22 @@ describe('Query', function() {
q.sort({}, { override: true });
assert.deepStrictEqual(q.getOptions().sort, {});
});

it('avoids mutating user-provided query selectors (gh-14567)', async function() {
const TestModel = db.model(
'Test',
Schema({ name: String, age: Number, active: Boolean })
);

await TestModel.create({ name: 'John', age: 21 });
await TestModel.create({ name: 'Bob', age: 35 });

const adultQuery = { age: { $gte: 18 } };

const docs = await TestModel.find(adultQuery).where('age').lte(25);
assert.equal(docs.length, 1);
assert.equal(docs[0].name, 'John');

assert.deepStrictEqual(adultQuery, { age: { $gte: 18 } });
});
});

0 comments on commit c144bf0

Please sign in to comment.