Skip to content

Commit 07e36aa

Browse files
authored
Merge pull request #12193 from Automattic/vkarpov15/gh-12143
fix(model+query): handle populate with lean transform that deletes `_id`
2 parents 7ed781c + 16bec60 commit 07e36aa

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

lib/model.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4599,6 +4599,17 @@ function populate(model, docs, options, callback) {
45994599
mod.options.options.sort || void 0;
46004600
assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
46014601

4602+
// Lean transform may delete `_id`, which would cause assignment
4603+
// to fail. So delay running lean transform until _after_
4604+
// `_assign()`
4605+
if (mod.options &&
4606+
mod.options.options &&
4607+
mod.options.options.lean &&
4608+
mod.options.options.lean.transform) {
4609+
mod.options.options._leanTransform = mod.options.options.lean.transform;
4610+
mod.options.options.lean = true;
4611+
}
4612+
46024613
if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
46034614
// Ensure that we set to 0 or empty array even
46044615
// if we don't actually execute a query to make sure there's a value
@@ -4673,6 +4684,14 @@ function populate(model, docs, options, callback) {
46734684
for (const arr of params) {
46744685
removeDeselectedForeignField(arr[0].foreignField, arr[0].options, vals);
46754686
}
4687+
for (const arr of params) {
4688+
const mod = arr[0];
4689+
if (mod.options && mod.options.options && mod.options.options._leanTransform) {
4690+
for (const doc of vals) {
4691+
mod.options.options._leanTransform(doc);
4692+
}
4693+
}
4694+
}
46764695
callback();
46774696
}
46784697
}

test/query.test.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,7 +3992,7 @@ describe('Query', function() {
39923992
});
39933993
});
39943994

3995-
it('allows a transform option for lean on a query gh-10423', async function() {
3995+
it('allows a transform option for lean on a query (gh-10423)', async function() {
39963996
const arraySchema = new mongoose.Schema({
39973997
sub: String
39983998
});
@@ -4004,7 +4004,7 @@ describe('Query', function() {
40044004
foo: [arraySchema],
40054005
otherName: subDoc
40064006
});
4007-
const Test = db.model('gh10423', testSchema);
4007+
const Test = db.model('Test', testSchema);
40084008
await Test.create({ name: 'foo', foo: [{ sub: 'Test' }, { sub: 'Testerson' }], otherName: { nickName: 'Bar' } });
40094009

40104010
const result = await Test.find().lean({
@@ -4030,6 +4030,40 @@ describe('Query', function() {
40304030
assert.strictEqual(single.foo[0]._id, undefined);
40314031
});
40324032

4033+
it('handles a lean transform that deletes _id with populate (gh-12143) (gh-10423)', async function() {
4034+
const testSchema = Schema({
4035+
name: String,
4036+
user: {
4037+
type: mongoose.Types.ObjectId,
4038+
ref: 'User'
4039+
}
4040+
});
4041+
4042+
const userSchema = Schema({
4043+
name: String
4044+
});
4045+
4046+
const Test = db.model('Test', testSchema);
4047+
const User = db.model('User', userSchema);
4048+
4049+
const user = await User.create({ name: 'John Smith' });
4050+
let test = await Test.create({ name: 'test', user });
4051+
4052+
test = await Test.findById(test).populate('user').lean({
4053+
transform: (doc) => {
4054+
delete doc._id;
4055+
delete doc.__v;
4056+
return doc;
4057+
}
4058+
});
4059+
4060+
assert.ok(test);
4061+
assert.deepStrictEqual(test, {
4062+
name: 'test',
4063+
user: { name: 'John Smith' }
4064+
});
4065+
});
4066+
40334067
it('skips applying default projections over slice projections (gh-11940)', async function() {
40344068
const commentSchema = new mongoose.Schema({
40354069
comment: String

0 commit comments

Comments
 (0)