Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(populate): correctly set populatedModelSymbol on documents populated using Model.populate() #13588

Merged
merged 1 commit into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -4264,7 +4264,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()]
);
});
});
Loading