diff --git a/lib/queryhelpers.js b/lib/queryhelpers.js index 62a8cb5b1f4..6948715a03c 100644 --- a/lib/queryhelpers.js +++ b/lib/queryhelpers.js @@ -163,6 +163,11 @@ exports.applyPaths = function applyPaths(fields, schema) { if (!isDefiningProjection(field)) { continue; } + // `_id: 1, name: 0` is a mixed inclusive/exclusive projection in + // MongoDB 4.0 and earlier, but not in later versions. + if (keys[keyIndex] === '_id' && keys.length > 1) { + continue; + } exclude = !field; break; } diff --git a/test/model.field.selection.test.js b/test/model.field.selection.test.js index 2a2352b7db2..2ae5c97312f 100644 --- a/test/model.field.selection.test.js +++ b/test/model.field.selection.test.js @@ -475,7 +475,6 @@ describe('model field selection', function() { db.deleteModel(/BlogPost/); const BlogPost = db.model('BlogPost', BlogPostSchema); - await BlogPost.create({ author: 'me', settings: { @@ -497,8 +496,6 @@ describe('model field selection', function() { }); it('when `select: true` in schema, works with $elemMatch in projection', async function() { - - const productSchema = new Schema({ attributes: { select: true, @@ -524,12 +521,10 @@ describe('model field selection', function() { }); it('selection specified in query overwrites option in schema', async function() { - const productSchema = new Schema({ name: { type: String, select: false } }); const Product = db.model('Product', productSchema); - await Product.create({ name: 'Computer' }); const product = await Product.findOne().select('name'); @@ -538,7 +533,6 @@ describe('model field selection', function() { }); it('selecting with `false` instead of `0` doesn\'t overwrite schema `select: false` (gh-8923)', async function() { - const userSchema = new Schema({ name: { type: String, select: false }, age: { type: Number } @@ -552,4 +546,21 @@ describe('model field selection', function() { assert.ok(!user.name); }); + + it('handles deselecting _id when other field has schema-level `select: false` (gh-12670)', async function() { + const schema = new mongoose.Schema({ + field1: { + type: String, + select: false + }, + field2: String + }); + const User = db.model('User', schema); + + await User.create({ field1: 'test1', field2: 'test2' }); + const doc = await User.findOne().select('field2 -_id'); + assert.ok(doc.field2); + assert.strictEqual(doc.field1, undefined); + assert.strictEqual(doc._id, undefined); + }); });