In a MongoDB find() projection, can you mix included and excluded fields?
answer
- A projection has exactly one mode
- Include-list or exclude-list, never both
- One field is allowed to break the rule
- _id comes back unless you say otherwise
- {title: 1, _id: 0} is the sanctioned mix
basics
~10 sNo. A find() projection is either an inclusion list or an exclusion list, and mixing the two raises an error. The single exception is _id, which may be excluded inside an otherwise inclusive projection.
solid answer
~40 sA `find()` projection document maps field names to `1`/`true` (include) or `0`/`false` (exclude). You must pick one mode: `{title: 1, author: 1}` returns only those fields, `{internalNotes: 0}` returns everything else. Writing `{title: 1, author: 0}` fails with a projection error because the server cannot tell what to do with the fields you named neither way. The one legal mix is `_id`: it is returned by default, so `{title: 1, _id: 0}` is valid and common. In an exclusion projection, all unnamed fields — including `_id` — still come back. Dot notation projects sub-fields (`{"address.city": 1}`), and the same single-mode rule applies to them.
code
javascript · 11 lines// inclusion: only the named fields (plus _id)
db.books.find({}, { title: 1, author: 1 })
// inclusion with _id turned off - the one legal mix
db.books.find({}, { title: 1, _id: 0 })
// exclusion: everything except the named fields
db.books.find({}, { draftNotes: 0 })
// error: cannot mix inclusion and exclusion
db.books.find({}, { title: 1, draftNotes: 0 })go deeper
Memorise the two forms and the one exception: an all-1s projection, an all-0s projection, and _id which you may set to 0 inside an inclusion projection. Be ready to write both on a whiteboard.
Explain why the mixing rule exists — the two modes imply opposite defaults for unnamed fields — and know that any non-zero number means include, so -1 is not an exclusion.
Show where projection actually saves cost: network and driver deserialization, not storage reads, unless the query is covered by an index. Mention _id: 0 as a prerequisite for most covered queries.
Frame projection as part of an API contract: a stable minimal shape returned to clients limits accidental data exposure and keeps payloads predictable as documents grow new fields.
## What a projection is The second argument to `find()` (and the `projection` option on the find command) is a document that tells the server which fields of each matching document to send back. Reducing the returned fields cuts network bytes, driver deserialization cost, and application memory — it does **not** change which documents match, which is the filter's job. ## The two modes A projection operates in exactly one of two modes: - **Inclusion**: every named field maps to a truthy value (`1`, `true`, or any non-zero number). Only the named fields are returned. - **Exclusion**: every named field maps to `0` or `false`. Every field *except* the named ones is returned. ```javascript db.books.find({}, { title: 1, author: 1 }) // _id, title, author db.books.find({}, { draftNotes: 0 }) // everything but draftNotes ``` Mixing the modes — `{ title: 1, draftNotes: 0 }` — is rejected by the server with a projection error, because the two modes imply contradictory defaults for the fields you did not mention. Inclusion says "default is drop"; exclusion says "default is keep". There is no coherent answer for the unnamed fields, so MongoDB refuses rather than guessing. Note that the value only has to be truthy or falsy: `{ title: -1 }` is an **inclusion**, not an exclusion, because `-1` is non-zero. This surprises people who expect `-1` to mean "remove", by analogy with sort direction. ## The _id exception `_id` is returned by default in an inclusion projection even though you did not ask for it — it is the document's identity, and drivers and application code rely on it. Because it is the one field with an implicit default, it is also the one field you may override in the other direction: ```javascript db.books.find({}, { title: 1, _id: 0 }) // just title ``` This is legal and is the standard way to get a clean, minimal shape for an API response. The reverse — `{ draftNotes: 0, _id: 1 }` — is pointless (exclusion already returns `_id`) and is not the same kind of sanctioned exception; keep exclusion projections pure. ## Nested fields and dot notation Projections reach into embedded documents with dot notation: ```javascript db.users.find({}, { "address.city": 1, "address.zip": 1, _id: 0 }) ``` The result preserves the nesting: each document comes back as `{ address: { city: ..., zip: ... } }`, not with flattened `"address.city"` keys. The single-mode rule still binds — you cannot include `address.city` and exclude `address.street` in the same projection. If you want "all of address except one field", use an exclusion projection naming that one field: `{ "address.street": 0 }`. An equivalent nested-document form exists (`{ address: { city: 1 } }`), but dot notation is the idiom and behaves more predictably when arrays are involved. ## Arrays and the operator forms Besides plain `1`/`0`, a projection may use the array projection operators `$slice`, `$elemMatch`, and the positional `$` on an array field, which return a subset of that array's elements rather than the whole array. These do not count as inclusion or exclusion for the mode rule in the way plain values do, but the safest habit is to keep them inside an inclusion projection and let the plain fields set the mode. ## Projections do not remove work from the server A common misreading is that projecting fewer fields makes the query cheaper on the server. In general the server still has to read the whole document from storage, then strip fields before sending. The saving is in network and client-side cost. The exception is a **covered query**, where every field the query needs is present in an index, and the document is never fetched at all — that requires `_id: 0` in the projection unless `_id` is part of the index. ## Aggregation-style projections Recent MongoDB versions (4.4 and later) also accept aggregation expressions in a `find()` projection, so you can compute a value, for example `{ fullName: { $concat: ["$first", " ", "$last"] } }`. Adding such a computed field puts the projection in inclusion mode, since you are describing what to emit rather than what to strip. ## What to say in an interview State the rule (one mode per projection), name the `_id` exception, show `{ title: 1, _id: 0 }`, and add that projection saves network and client cost rather than server read cost unless the query is covered.
- Why is {title: 1, _id: 0} allowed when mixing inclusion and exclusion normally fails?Because `_id` is the only field with an implicit default. An inclusion projection would return it even though you did not name it, so MongoDB lets you override that single default explicitly. Every other field's presence follows purely from the projection's mode, so naming one in the opposite direction would be contradictory.
- Does projecting fewer fields reduce the work the server does to read the documents?Usually not. The server still reads the full document from storage and then strips fields before sending, so the saving is network bandwidth and client-side deserialization. The exception is a covered query, where every field needed is in the index and the document is never fetched — that normally requires excluding `_id` unless it is part of the index.
- How do you project two fields out of an embedded address sub-document?Use dot notation: `{ "address.city": 1, "address.zip": 1, _id: 0 }`. The result keeps the nesting, coming back as `{ address: { city, zip } }` rather than with flattened keys. The one-mode rule still applies, so you cannot include one address sub-field and exclude another in the same projection.
saying these in an interview costs you the question
- Claims any field can be freely mixed with 0 and 1
- Thinks {field: -1} excludes the field
- Assumes _id disappears when it is not listed
- Says projection makes the server read less data
- Expects dotted keys to come back flattened in results