Skip to content

Commit

Permalink
fix(query): clone PopulateOptions when setting _localModel to avoid s…
Browse files Browse the repository at this point in the history
…tate leaking between subpopulate instances

Fix #15026
  • Loading branch information
vkarpov15 committed Dec 9, 2024
1 parent 5eff288 commit f64a7d8
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 29 deletions.
5 changes: 3 additions & 2 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -4775,12 +4775,13 @@ Query.prototype._castUpdate = function _castUpdate(obj) {
*/

Query.prototype.populate = function() {
const args = Array.from(arguments);
// Bail when given no truthy arguments
if (!Array.from(arguments).some(Boolean)) {
if (!args.some(Boolean)) {
return this;
}

const res = utils.populate.apply(null, arguments);
const res = utils.populate.apply(null, args);

// Propagate readConcern and readPreference and lean from parent query,
// unless one already specified
Expand Down
38 changes: 11 additions & 27 deletions lib/queryHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Module dependencies
*/

const PopulateOptions = require('./options/populateOptions');
const checkEmbeddedDiscriminatorKeyProjection =
require('./helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection');
const get = require('./helpers/get');
Expand All @@ -13,32 +14,6 @@ const isDefiningProjection = require('./helpers/projection/isDefiningProjection'
const clone = require('./helpers/clone');
const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');

/**
* Prepare a set of path options for query population.
*
* @param {Query} query
* @param {Object} options
* @return {Array}
*/

exports.preparePopulationOptions = function preparePopulationOptions(query, options) {
const _populate = query.options.populate;
const pop = Object.keys(_populate).reduce((vals, key) => vals.concat([_populate[key]]), []);

// lean options should trickle through all queries
if (options.lean != null) {
pop
.filter(p => (p && p.options && p.options.lean) == null)
.forEach(makeLean(options.lean));
}

pop.forEach(opts => {
opts._localModel = query.model;
});

return pop;
};

/**
* Prepare a set of path options for query population. This is the MongooseQuery
* version
Expand Down Expand Up @@ -74,7 +49,16 @@ exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query,

const projection = query._fieldsForExec();
for (let i = 0; i < pop.length; ++i) {
pop[i] = { ...pop[i], _queryProjection: projection, _localModel: query.model };
if (pop[i] instanceof PopulateOptions) {
pop[i] = new PopulateOptions({
...pop[i],
_queryProjection: projection,
_localModel: query.model
});
} else {
pop[i]._queryProjection = projection;
pop[i]._localModel = query.model;
}
}

return pop;
Expand Down

0 comments on commit f64a7d8

Please sign in to comment.