Course outline

MongoDB / Basic: document foundations / Lesson 6

Inserts, updates, deletes and atomic filters

Make writes express their own business preconditions. Distinguish update operators, replacement, upsert, acknowledgment and retry identity.

About 4 min readContent reviewed
On this page

Lesson goals

  • Use scoped CRUD and conditional arithmetic updates.
  • Explain document atomicity and duplicate-request semantics.

One document is the default atomic boundary

A MongoDB write modifying one document is atomic even when several fields change. This does not mean updateMany is atomic across every matching document. A preliminary application read followed by an unconditional absolute replacement can overwrite a concurrent change. Put expected state or version in the update filter, and use operators such as $inc when arithmetic is intended. The application must inspect matched and modified outcomes under an acknowledged write concern rather than treating a returned response as proof of the desired business event.

Keep the sufficient-stock rule inside the update

Assume item 4 has quantity nine in the isolated synthetic inventory. The command subtracts three only if quantity is at least three, giving quantity six when one document matches. If quantity is two, matchedCount is zero and no reservation succeeds. The example’s expected result assumes acknowledged success and a known fixture; write concern and network uncertainty are discussed later. A missing item also produces no match, so distinguish it from insufficient stock only when the API contract requires that additional decision.

javascript Illustrative mongosh; results are reasoned, not measured.
db.getSiblingDB('quay_lab').inventory.updateOne(
  { _id: 4, quantity: { $gte: 3 } },
  { $inc: { quantity: -3 } }
);
Expected output
Expected with quantity initially 9: matchedCount = 1, modifiedCount = 1; quantity becomes 6.

The stock predicate and arithmetic change are evaluated as one single-document update.

Replacement discards omitted ordinary fields

updateOne with $set changes selected fields, while replaceOne replaces the document body subject to _id rules; omitted fields are not preserved automatically. Upsert inserts a document if the filter finds none, which is not the same as “ignore every error.” Concurrent upserts need a suitable unique identity to avoid duplicate logical records. A unique index protects the chosen key but does not make an incremented account balance idempotent for a retried payment event. Define reference-row identity and event identity separately.

Use optimistic state checks deliberately

Assume booking 101 is pending with version two. The filter requires that exact version and state before marking it dispatched and incrementing version to three. A competing writer that already advanced the version makes this attempt match nothing. The application should re-read and reconsider the decision rather than removing the version check. This is optimistic concurrency, not a transaction spanning external systems. An outbox or another reviewed integration design is needed when dispatch also triggers a message or downstream effect.

javascript Illustrative mongosh; results are reasoned, not measured.
db.getSiblingDB('quay_lab').bookings.updateOne(
  { _id: 101, state: 'pending', version: 2 },
  { $set: { state: 'dispatched' }, $inc: { version: 1 } }
);

Expected after one successful matching update: state dispatched and version 3. A stale version returns no match.

Bulk completion is not rollback of earlier writes

Ordered bulk writes stop at a qualifying error after earlier successful operations have already taken effect. Unordered bulk work can continue with other operations and does not promise execution order. Neither behavior is an automatic all-or-nothing batch transaction. DELETE filters deserve the same exact scope review as UPDATE, especially tenant boundaries and empty filters. Network errors can leave an uncertain outcome; use driver-supported retry behavior only under its documented constraints and persist idempotency keys for business events that must not apply twice.

Common errors and fixes

A reservation overwrites another writer’s quantity

Why it happens
An absolute value came from a stale application read.
How to fix it
Use a conditional operator update or reviewed version filter.
Prevent it next time
Put invariants and expected state inside the atomic filter.

Fields disappear after replaceOne

Why it happens
Replacement was mistaken for a partial $set update.
How to fix it
Use update operators or supply the complete intended replacement body.
Prevent it next time
Review omitted-field behavior before choosing a write method.

A failed ordered bulk leaves partial effects

Why it happens
Stop-on-error was assumed to roll back earlier operations.
How to fix it
Reconcile known outcomes and use a transaction only when its supported contract is required.
Prevent it next time
Test partial failures and idempotent recovery paths.

Check your understanding

1. Inventory is two and the request needs three. What is the correct next decision after the conditional update?

Show worked solution

No document matches and no reservation succeeds. Return the defined insufficient-stock or missing-item outcome after any necessary distinction, without weakening the condition. A subsequent smaller request may succeed through the same atomic pattern. Do not turn a zero-match response into an unconditional decrement, because that discards the invariant the filter was designed to protect.

2. A payment request is retried after a lost response. Why is upsert on accountId alone insufficient?

Show worked solution

The account key identifies the account, not the individual payment event, so another increment can apply the same payment twice. Store a unique request or payment identifier with its recorded result and coordinate the effect under the required transaction or single-document model. On duplicate event identity, return the known outcome rather than applying a second financial change.

Key takeaways

  • Document atomicity does not imply batch or external-system atomicity.
  • Conditional filters, acknowledgment and event identity belong in write design.

Official references