skip to content

CRUD & Query Operators

The everyday read and write API — find, insert, update, delete — with their operator vocabulary, upserts and bulk writes. Interviewers ask because fluency with query and update operators is the fastest way to spot genuine MongoDB use.

part ofMongoDBoverview, primer and where to startread it →
on this pageshow

questions

23

In a MongoDB find() projection, can you mix included and excluded fields?

level: juniorimportance: must knowfreq 72%

answer

  1. A projection has exactly one mode
  2. Include-list or exclude-list, never both
  3. One field is allowed to break the rule
  4. _id comes back unless you say otherwise
  5. {title: 1, _id: 0} is the sanctioned mix

basics

~10 s

No. 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 s

A `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
javascript
// 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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context

open as a page

In MongoDB, what does the find filter { tags: "red" } match when tags holds an array?

level: juniorimportance: must knowfreq 78%

basics

~20 s

It matches documents where tags is exactly the string "red" and documents where tags is an array containing "red" as an element. MongoDB applies the equality predicate to the field's value and to each array element.

open as a page

In MongoDB, what is the difference between updateOne with $set and replaceOne?

level: juniorimportance: must knowfreq 78%

basics

~20 s

updateOne with $set changes only the named fields and leaves every other field intact. replaceOne swaps the whole document for the one you pass, so any field the replacement omits disappears. _id cannot be changed either way.

open as a page

Why can $elemMatch return different results from listing the same two conditions on an array field?

level: middleimportance: must knowfreq 72%

basics

~10 s

Two conditions written side by side may be satisfied by different array elements; $elemMatch requires one single element to satisfy all of them at once. On a non-array field the two forms are equivalent.

open as a page

Which MongoDB write operations are atomic without opening an explicit transaction?

level: middleimportance: must knowfreq 78%

basics

~20 s

Every write that touches a single document is atomic in MongoDB, no matter how many fields, embedded documents or array elements it changes. Atomicity stops at the document boundary: a write spanning several documents needs an explicit transaction to be all-or-nothing.

open as a page

Why does the MongoDB driver's withTransaction() helper exist instead of calling commitTransaction() yourself?

level: middleimportance: must knowfreq 60%

basics

~20 s

MongoDB transactions can fail in two retryable ways: the whole transaction can abort transiently, or a commit can return an unknown outcome. withTransaction() runs your callback, commits, and applies both retry loops correctly, which hand-written code almost always gets wrong.

open as a page

How do the positional $ operator, $[] and arrayFilters differ when updating arrays?

level: middleimportance: must knowfreq 68%

basics

~20 s

The positional $ updates only the first array element matched by the query filter. $[] updates every element unconditionally. $[identifier] with arrayFilters updates exactly the elements matching the given condition, and is the only form that can update several specific elements.

open as a page

What does upsert: true do in updateOne, and what does $setOnInsert add?

level: middleimportance: must knowfreq 64%

basics

~20 s

With upsert: true, a matching document is updated normally; if none matches, MongoDB inserts a new document built from the filter's equality conditions plus the update's operators. $setOnInsert supplies fields that apply only on that insert and are ignored when a document matched.

open as a page

Why does MongoDB's skip(200000).limit(20) get slower the deeper the page number goes?

level: seniorimportance: must knowfreq 74%

basics

~20 s

skip() is not a seek: the server walks and discards every skipped entry before returning anything, so cost grows with the offset. Range (keyset) paging replaces it with a filter on the last seen sort key, which the index seeks to directly.

open as a page

What is the difference between the $push and $addToSet update operators?

level: juniorimportance: should knowfreq 62%

basics

~10 s

$push always appends the value to the array, so duplicates accumulate. $addToSet appends only if an equal value is not already present, giving set semantics. Both create the array when the field is missing.

open as a page

How do the $slice and $elemMatch projection operators limit which array elements are returned?

level: middleimportance: should knowfreq 52%

basics

~20 s

$slice returns a positional window of an array: a count from the front, a negative count from the back, or a [skip, limit] pair. $elemMatch returns only the first array element matching a condition, dropping the field entirely when nothing matches.

open as a page

What does cursor.batchSize() change about how a MongoDB find() returns documents?

level: middleimportance: should knowfreq 44%

basics

~20 s

batchSize() sets how many documents the server puts in each network batch, not how many the query returns in total. It tunes round trips against per-batch memory; limit() is what caps the total number of documents.

open as a page

What does the filter { "items.price": { $gt: 100 } } match when items is an array of subdocuments?

level: middleimportance: should knowfreq 58%

basics

~20 s

It matches any document where at least one element of the items array has a price greater than 100. A dotted path traverses embedded documents and, when it crosses an array, applies the rest of the path to every element.

open as a page

How do you distinguish a missing field from one explicitly set to null in a MongoDB query?

level: middleimportance: should knowfreq 55%

basics

~20 s

A filter of { field: null } matches both cases. Use { field: { $exists: false } } for missing only, { field: { $type: "null" } } for an explicit null only, and { field: { $exists: true, $ne: null } } for present and non-null.

open as a page

In MongoDB, why does { status: { $ne: "active" } } also return documents with no status field?

level: middleimportance: should knowfreq 48%

basics

~10 s

A document without the field has no value equal to "active", so the negated predicate is satisfied. Negation operators match absent fields by design; add $exists: true when you mean present-but-different.

open as a page

Which MongoDB write operations does retryWrites=true actually retry after a failover?

level: middleimportance: should knowfreq 55%

basics

~10 s

Only single-document writes: insertOne, insertMany, updateOne, replaceOne, deleteOne, the findOneAndX family, and bulkWrite made only of those. Multi-document commands such as updateMany and deleteMany are never retried, and neither are unacknowledged writes.

open as a page

When would you use findOneAndUpdate instead of updateOne in MongoDB?

level: middleimportance: should knowfreq 55%

basics

~20 s

Use findOneAndUpdate when you need the document back as part of the write — either its pre-update state or the post-update state via returnDocument: "after" — or when a sort must decide which single document is modified. updateOne only returns counts.

open as a page

A batch job iterating a MongoDB cursor fails with CursorNotFound — what causes that and how do you avoid it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

The server closed the cursor before the job asked for the next batch — usually the 10-minute idle-cursor timeout, an expired logical session, or a primary stepdown. Fix it by not holding a long-lived cursor: page with a resumable range query instead.

open as a page

How do you safely build a MongoDB $regex filter from user-supplied search text?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Treat the input as a literal string: verify it is a string, escape every regular-expression metacharacter, anchor the pattern where possible, and cap its length. Never splice a raw client-supplied object into the filter document.

open as a page

How do read concern and write concern apply inside a MongoDB multi-document transaction?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Both are set once for the whole transaction, not per operation. The read concern governs every read in it, the write concern is applied only at commit, and reads must come from the primary. Individual operations may not override them.

open as a page

What happens to a MongoDB transaction that runs longer than transactionLifetimeLimitSeconds?

level: seniorimportance: should knowfreq 42%

basics

~10 s

The server aborts it. transactionLifetimeLimitSeconds defaults to 60, and a periodic cleanup aborts any transaction older than that; subsequent operations and the commit then fail. The fix is smaller transactions, not a bigger limit.

open as a page

A bulkWrite of 10,000 operations fails partway with a duplicate-key error — what has been applied, and how does ordered versus unordered change it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Everything already applied stays applied — a bulkWrite is not atomic across its operations. Ordered (the default) executes in array order and stops at the first error, leaving the tail untouched. Unordered continues past failures and reports all errors together.

open as a page

How would you decide whether a sharded workload should use cross-shard MongoDB transactions or be remodelled to avoid them?

level: principalimportance: should knowfreq 33%

basics

~20 s

Measure how many transactions actually span shards and what they cost. A transaction touching one shard is cheap; one touching several commits through two-phase commit with prepared state and held locks. Prefer a shard key that colocates the transactional unit.

open as a page