findOneAndUpdate(), findOneAndReplace(), and findOneAndDelete()

MongoDB provides find-and-modify operations that select a document, perform a modification, and return a document according to the operation's options.

These methods are useful when an application needs to retrieve the document involved in a write operation without issuing a separate read that could observe a later state.

findOneAndUpdate()

db.inventory.findOneAndUpdate(
  { sku: "KEYBOARD-01" },
  { $inc: { stock: -1 } },
  { returnDocument: "after" }
);

The operation decrements stock and requests the document after the update.

The exact return shape and option names depend on the interface. In modern mongosh, the CRUD methods use documented options such as returnDocument. Node.js driver APIs have version-specific details, so check the documentation for the installed driver.

Return before or after

Depending on the interface, you can request the document before or after the modification. If the operation returns the document before the change, the caller sees the pre-update state. If it returns the document after the change, the caller sees the post-update state.

Upsert

Find-and-update operations can support upsert behavior. If no document matches and upsert is enabled, a new document may be inserted according to the operation's rules.

findOneAndReplace()

This method combines selection with document replacement. As with replaceOne(), the replacement removes unspecified non-_id fields.

findOneAndDelete()

This operation deletes one matching document and returns the deleted document according to the method's semantics.

When these methods are useful

  • Reserving an inventory item by conditionally modifying stock.
  • Claiming a queued job.
  • Changing a document and returning the updated state.
  • Deleting a record while capturing its prior contents.

Atomic inventory update

Suppose an item should be sold only when stock is greater than zero. A conditional update can combine the check and decrement in one document-level atomic operation:

db.inventory.findOneAndUpdate(
  {
    sku: "KEYBOARD-01",
    stock: { $gt: 0 }
  },
  {
    $inc: { stock: -1 }
  },
  {
    returnDocument: "after"
  }
);

The filter prevents the decrement from applying when the stock is already zero or negative. If no document matches, the result indicates that no eligible document was updated, with the exact return shape depending on the interface.

Conditionally decrement inventory
db.inventoryDemo.insertOne({
  sku: "KEYBOARD-01",
  stock: 2
});

const updated = db.inventoryDemo.findOneAndUpdate(
  {
    sku: "KEYBOARD-01",
    stock: { $gt: 0 }
  },
  {
    $inc: { stock: -1 }
  },
  {
    returnDocument: "after"
  }
);

printjson(updated);
💡 The filter checks that stock is positive as part of the same atomic document update that decrements it. This avoids a separate read-then-write race in which two requests could both observe the same available stock.
Key Takeaways
  • Find-and-modify operations combine document selection with modification.
  • The returned document can represent the state before or after an update.
  • Find-and-replace and find-and-delete operations have distinct semantics.
  • Conditional single-document updates can prevent certain read-then-write race conditions.
  • Return shapes and options should be checked against the shell or driver version in use.
Knowledge Check

Why is filtering on `stock: { $gt: 0 }` while decrementing stock safer than reading stock first and decrementing in a separate operation?