Skip to content

Commit 1f1f897

Browse files
committed
fix(populate): allow deselecting discriminator key when populating
Fix #3230 Re: #13760 Re: #13679
1 parent 2d65e00 commit 1f1f897

File tree

6 files changed

+34
-18
lines changed

6 files changed

+34
-18
lines changed

lib/helpers/projection/isExclusive.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ module.exports = function isExclusive(projection) {
1212
}
1313

1414
const keys = Object.keys(projection);
15-
let ki = keys.length;
1615
let exclude = null;
1716

18-
if (ki === 1 && keys[0] === '_id') {
17+
if (keys.length === 1 && keys[0] === '_id') {
1918
exclude = !projection._id;
2019
} else {
21-
while (ki--) {
20+
for (let ki = 0; ki < keys.length; ++ki) {
2221
// Does this projection explicitly define inclusion/exclusion?
2322
// Explicitly avoid `$meta` and `$slice`
2423
const key = keys[ki];

lib/model.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4408,7 +4408,7 @@ function populate(model, docs, options, callback) {
44084408
select = select.replace(excludeIdRegGlobal, ' ');
44094409
} else {
44104410
// preserve original select conditions by copying
4411-
select = utils.object.shallowCopy(select);
4411+
select = { ...select };
44124412
delete select._id;
44134413
}
44144414
}

lib/query.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4932,16 +4932,14 @@ Query.prototype._castFields = function _castFields(fields) {
49324932
elemMatchKeys,
49334933
keys,
49344934
key,
4935-
out,
4936-
i;
4935+
out;
49374936

49384937
if (fields) {
49394938
keys = Object.keys(fields);
49404939
elemMatchKeys = [];
4941-
i = keys.length;
49424940

49434941
// collect $elemMatch args
4944-
while (i--) {
4942+
for (let i = 0; i < keys.length; ++i) {
49454943
key = keys[i];
49464944
if (fields[key].$elemMatch) {
49474945
selected || (selected = {});
@@ -4960,8 +4958,7 @@ Query.prototype._castFields = function _castFields(fields) {
49604958
}
49614959

49624960
// apply the casted field args
4963-
i = elemMatchKeys.length;
4964-
while (i--) {
4961+
for (let i = 0; i < elemMatchKeys.length; ++i) {
49654962
key = elemMatchKeys[i];
49664963
fields[key] = out[key];
49674964
}

lib/queryhelpers.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,12 @@ exports.applyPaths = function applyPaths(fields, schema) {
180180
if (!isDefiningProjection(field)) {
181181
continue;
182182
}
183-
// `_id: 1, name: 0` is a mixed inclusive/exclusive projection in
184-
// MongoDB 4.0 and earlier, but not in later versions.
185183
if (keys[keyIndex] === '_id' && keys.length > 1) {
186184
continue;
187185
}
186+
if (keys[keyIndex] === schema.options.discriminatorKey && keys.length > 1 && field != null && !field) {
187+
continue;
188+
}
188189
exclude = !field;
189190
break;
190191
}

lib/utils.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -687,12 +687,6 @@ exports.object.vals = function vals(o) {
687687
return ret;
688688
};
689689

690-
/**
691-
* @see exports.options
692-
*/
693-
694-
exports.object.shallowCopy = exports.options;
695-
696690
const hop = Object.prototype.hasOwnProperty;
697691

698692
/**

test/model.populate.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10834,4 +10834,29 @@ describe('model: populate:', function() {
1083410834
[id1.toString(), id2.toString(), id3.toString(), id4.toString()]
1083510835
);
1083610836
});
10837+
10838+
it('allows deselecting discriminator key when populating (gh-3230) (gh-13760) (gh-13679)', async function() {
10839+
const Test = db.model(
10840+
'Test',
10841+
Schema({ name: String, arr: [{ testRef: { type: 'ObjectId', ref: 'Test2' } }] })
10842+
);
10843+
10844+
const schema = Schema({ name: String });
10845+
const Test2 = db.model('Test2', schema);
10846+
const D = Test2.discriminator('D', Schema({ prop: String }));
10847+
10848+
10849+
await Test.deleteMany({});
10850+
await Test2.deleteMany({});
10851+
const { _id } = await D.create({ name: 'foo', prop: 'bar' });
10852+
const test = await Test.create({ name: 'test', arr: [{ testRef: _id }] });
10853+
10854+
const doc = await Test
10855+
.findById(test._id)
10856+
.populate('arr.testRef', { name: 1, prop: 1, _id: 0, __t: 0 });
10857+
assert.deepStrictEqual(
10858+
doc.toObject().arr[0].testRef,
10859+
{ name: 'foo', prop: 'bar' }
10860+
);
10861+
});
1083710862
});

0 commit comments

Comments
 (0)