What must an updateOne or deleteOne on a sharded collection include in its filter, and what changed in MongoDB 7.0?
answer
- A single-document write must land on exactly one shard
- Older versions refused the ambiguous filter outright
- A recent major version made it work but not cheap
- One option still demands the complete key
- The many-document variants were never restricted
basics
~20 sBefore MongoDB 7.0, a single-document update, delete or findAndModify on a sharded collection had to include the shard key or _id in its filter, or it errored. MongoDB 7.0 lifted that: the cluster now locates the document itself, at the cost of a broadcast. Upserts still need the full shard key.
solid answer
~50 sHistorically the rule was strict: on a sharded collection, an `updateOne`, `deleteOne`, `replaceOne` or `findAndModify` — anything with `multi: false` — had to include the **shard key or `_id`** in the query, otherwise the operation failed rather than guessing. The reason is that a single-document write must be applied on exactly one shard, and without a shard-key value the router cannot say which. **MongoDB 7.0 removed that requirement**: single-document updates, deletes and findAndModify may now filter on anything. The cost is honest — the cluster has to find a matching document across the shards first and then apply the write to it, so it is a broadcast plus an extra round trip rather than one targeted operation. An **upsert** still requires the full shard key in the filter, because if no document matches, the router must know where the new one belongs. `updateMany` and `deleteMany` have always been allowed to broadcast.
code
javascript · 12 lines// Shard key: { orgId: 1 }
// targeted single-document write - one shard, one round trip
db.orders.updateOne({ orgId: "acme", _id: oid }, { $set: { status: "SHIPPED" } })
// allowed from MongoDB 7.0, but broadcast plus a second round trip
db.orders.updateOne({ externalRef: "REF-99" }, { $set: { status: "SHIPPED" } })
// upsert still needs the full shard key so a new document can be placed
db.orders.updateOne({ orgId: "acme", externalRef: "REF-99" },
{ $set: { status: "NEW" } },
{ upsert: true })go deeper
Remember that a single-document write on a sharded collection has to end up on one shard, and that including the shard key in the filter is how you make that cheap and unambiguous.
Explain the old shard-key-or-_id requirement, what MongoDB 7.0 changed, and why an upsert is the case that still cannot be relaxed.
Show the operational instinct: audit updateOne and findAndModify call sites when adopting sharding, because on modern versions they now succeed silently at broadcast cost instead of failing loudly.
Own the key design so that natural upsert and idempotent-ingestion paths carry the full shard key, and set the standard that hot write paths must be targetable by contract.
## Why single-document writes are special A read that lacks the shard key is merely expensive: the router asks everyone and merges. A write with `multi: false` is different, because it must be applied to **exactly one** document on **exactly one** shard. Sending it to all shards would risk applying it more than once. That asymmetry is the root of every rule below. ## The historical rule For a long time MongoDB refused the ambiguous case outright. On a sharded collection, `updateOne`, `replaceOne`, `deleteOne` and `findAndModify` had to include the **shard key** or the **`_id`** field in the query filter, or the operation failed with an error about the query needing to contain the shard key. Teams worked around it by carrying the shard key into every write path, or by doing a find-then-write in application code — which is exactly the two-step the server later took over. Note what `_id` bought you there: it did not make the write *targeted*, since the router still could not map an `_id` to a shard, but it did guarantee that at most one document cluster-wide could match, which is enough to make the operation unambiguous. ## What MongoDB 7.0 changed MongoDB 7.0 added support for `updateOne`, `deleteOne` and `findAndModify` **without the shard key**. The filter may now name any field. Under the hood the cluster performs the work the application used to do by hand: it locates a matching document across the shards, then applies the write to that one document on the shard that owns it, with the appropriate protections so the write lands once. This is a usability win, not a performance one. The operation costs a cluster-wide search plus a second phase to apply the write, so it is strictly more expensive than the targeted form. Treat it as an escape hatch for occasional writes and admin paths, not as permission to stop carrying the shard key on hot write paths. ## Upserts are still stricter An upsert has a branch where **no document matches** and a new one is created. Creating a document requires knowing which chunk — and therefore which shard — it belongs to, and that requires a complete shard-key value. There is nothing to search for and nothing to infer. So an upsert on a sharded collection must supply the full shard key in the query filter, and by extension the shard-key fields must be present in the resulting document. Design upsert-based write paths (idempotent ingestion, counters, last-seen timestamps) with the shard key in the natural business key, or they will not work at all. ## Multi-document writes `updateMany` and `deleteMany` have no such restriction, in any version. They are allowed to affect any number of documents, so broadcasting is semantically fine: the router sends the operation to every shard that could hold matches, and each applies it locally. If the filter does contain the shard key, the same targeting rules as reads apply and the operation goes only to the relevant shards. Be aware that a broadcast multi-write is not atomic across shards — each shard's portion commits independently unless the operation runs inside a multi-document transaction. ## Practical guidance - Carry the shard key on every hot write path. If the value exists in the request context, thread it into the repository call; that is usually a one-line change that converts a cluster-wide operation into a single-shard one. - Design keys so that natural upsert paths include the full shard key. - When you must write without the key, make it a low-rate path, and know that you are paying a search plus an apply. - When reviewing a migration to a sharded collection, audit every `updateOne`/`findAndModify` call site: on pre-7.0 clusters these fail loudly, and on 7.0+ they succeed quietly at N times the cost, which is the more dangerous of the two failure modes.
- Why does an upsert still require the full shard key when a plain updateOne no longer does?Because of the no-match branch. If nothing matches, the server must create a document, and placing a new document requires a complete shard-key value to pick its chunk and shard. There is nothing to search for, so no amount of cluster-wide lookup can supply the answer.
- Before 7.0, why was filtering by _id accepted even though _id is not the shard key?`_id` guarantees at most one matching document cluster-wide, which makes a multi:false write unambiguous even though the router cannot say where it lives. The operation still broadcast to find the document; the rule was about correctness, not about targeting.
- Is an updateMany that broadcasts to several shards atomic?No. Each shard applies its portion independently, so another reader can observe some shards updated and others not. If you need all-or-nothing across shards, the operation has to run inside a multi-document transaction, which brings its own cost and limits.
saying these in an interview costs you the question
- Says updateOne without the shard key is still rejected in current versions
- Claims 7.0 made keyless single-document writes as cheap as targeted ones
- Thinks an upsert can infer placement from a partial shard key
- Believes a broadcast updateMany is atomic across shards
- Assumes filtering by _id makes a write targeted