Skip to content

Commit

Permalink
Merge pull request #13588 from Automattic/vkarpov15/gh-13575
Browse files Browse the repository at this point in the history
fix(populate): correctly set `populatedModelSymbol` on documents populated using `Model.populate()`
  • Loading branch information
vkarpov15 authored Jul 9, 2023
2 parents b336ed8 + 22b1e25 commit 1a998e2
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 1 deletion.
1 change: 1 addition & 0 deletions lib/helpers/populate/getModelsMapForPopulate.js
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ function addModelNamesToMap(model, map, available, modelNames, options, data, re
// Used internally for checking what model was used to populate this
// path.
options[populateModelSymbol] = Model;
currentOptions[populateModelSymbol] = Model;
available[modelName] = {
model: Model,
options: currentOptions,
Expand Down
2 changes: 1 addition & 1 deletion lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -4267,7 +4267,7 @@ const excludeIdReg = /\s?-_id\s?/;
const excludeIdRegGlobal = /\s?-_id\s?/g;

function populate(model, docs, options, callback) {
const populateOptions = { ...options };
const populateOptions = options;
if (options.strictPopulate == null) {
if (options._localModel != null && options._localModel.schema._userProvidedOptions.strictPopulate != null) {
populateOptions.strictPopulate = options._localModel.schema._userProvidedOptions.strictPopulate;
Expand Down
3 changes: 3 additions & 0 deletions lib/types/array/methods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ const methods = {
if (populated && value !== null && value !== undefined) {
// cast to the populated Models schema
Model = populated.options[populateModelSymbol];
if (Model == null) {
throw new MongooseError('No populated model found for path `' + this[arrayPathSymbol] + '`. This is likely a bug in Mongoose, please report an issue on github.com/Automattic/mongoose.');
}

// only objects are permitted so we can safely assume that
// non-objects are to be interpreted as _id
Expand Down
35 changes: 35 additions & 0 deletions test/model.populate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10504,4 +10504,39 @@ describe('model: populate:', function() {
});
assert.deepStrictEqual(doc.children.map(c => c.name).sort(), ['Chad', 'Luke']);
});

it('allows pushing to model populated in a query cursor (gh-13575)', async function() {
const Company = db.model('Company', Schema({ name: String }));
const User = db.model(
'User',
Schema({ name: String, companies: [{ type: Schema.Types.ObjectId, ref: 'Company' }] })
);

await User.deleteMany({});
await Company.insertMany([{ name: 'Company 1' }, { name: 'Company 2' }]);
const company = await Company.findOne({ name: 'Company 1' });
const company2 = await Company.findOne({ name: 'Company 2' });

await User.insertMany([{ name: 'User 1', companies: [company.id] }]);

await User.find()
.populate('companies')
.cursor()
.eachAsync(async(user) => {
if (!user.populated('companies')) {
await user.populate('companies');
}

user.companies.push(company2);

await user.save();
});

const user = await User.findOne();
assert.equal(user.name, 'User 1');
assert.deepEqual(
user.toObject().companies.sort().map(v => v.toString()),
[company._id.toString(), company2._id.toString()]
);
});
});

0 comments on commit 1a998e2

Please sign in to comment.