Skip to content

Commit d36d134

Browse files
authored
Merge pull request #14173 from Automattic/vkarpov15/handle-nested-projections
fix(document): avoid treating nested projection as inclusive when applying defaults
2 parents 2a78e25 + 95d917b commit d36d134

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

lib/helpers/document/applyDefaults.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const isNestedProjection = require('../projection/isNestedProjection');
4+
35
module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip) {
46
const paths = Object.keys(doc.$__schema.paths);
57
const plen = paths.length;
@@ -32,7 +34,7 @@ module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildre
3234
}
3335
} else if (exclude === false && fields && !included) {
3436
const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
35-
if (curPath in fields || (j === len - 1 && hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
37+
if ((curPath in fields && !isNestedProjection(fields[curPath])) || (j === len - 1 && hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
3638
included = true;
3739
} else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) {
3840
break;

lib/helpers/projection/hasIncludedChildren.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = function hasIncludedChildren(fields) {
2121
const keys = Object.keys(fields);
2222

2323
for (const key of keys) {
24+
2425
if (key.indexOf('.') === -1) {
2526
hasIncludedChildren[key] = 1;
2627
continue;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
module.exports = function isNestedProjection(val) {
4+
if (val == null || typeof val !== 'object') {
5+
return false;
6+
}
7+
return val.$slice == null && val.$elemMatch == null && val.$meta == null && val.$ == null;
8+
};

test/query.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4383,4 +4383,38 @@ describe('Query', function() {
43834383
await Error.find().sort('-');
43844384
}, { message: 'Invalid field "" passed to sort()' });
43854385
});
4386+
4387+
it('does not apply sibling path defaults if using nested projection (gh-14115)', async function() {
4388+
const version = await start.mongodVersion();
4389+
if (version[0] < 5) {
4390+
return this.skip();
4391+
}
4392+
4393+
const userSchema = new mongoose.Schema({
4394+
name: String,
4395+
account: {
4396+
amount: Number,
4397+
owner: { type: String, default: () => 'OWNER' },
4398+
taxIds: [Number]
4399+
}
4400+
});
4401+
const User = db.model('User', userSchema);
4402+
4403+
const { _id } = await User.create({
4404+
name: 'test',
4405+
account: {
4406+
amount: 25,
4407+
owner: 'test',
4408+
taxIds: [42]
4409+
}
4410+
});
4411+
4412+
const doc = await User
4413+
.findOne({ _id }, { name: 1, account: { amount: 1 } })
4414+
.orFail();
4415+
assert.strictEqual(doc.name, 'test');
4416+
assert.strictEqual(doc.account.amount, 25);
4417+
assert.strictEqual(doc.account.owner, undefined);
4418+
assert.strictEqual(doc.account.taxIds, undefined);
4419+
});
43864420
});

0 commit comments

Comments
 (0)