FindAndModify Family
Find-and-modify operations atomically find a document and apply a change in a single step. This prevents race conditions when multiple clients compete for the same document — counters, job queues, and inventory locks.
Why find-and-modify?
Find-and-modify operations atomically find a document and apply a change in a single step. This prevents race conditions when multiple clients compete for the same document — counters, job queues, and inventory locks.
Real-life example: findOneAndUpdate is like a ticket counter that hands you the next number AND increments the counter in one motion — no two people get the same number.
- findOneAndUpdate — find and apply update operators.
- findOneAndReplace — find and replace entire document.
- findOneAndDelete — find and remove.
- Legacy findAndModify — deprecated, same family.
findOneAndUpdate and findOneAndReplace
findOneAndUpdate applies update operators. findOneAndReplace swaps the whole document. Both support upsert and returnDocument to control what gets returned.
Real-life example: returnDocument: 'after' is like seeing the receipt after the cashier updates your order — not the old version.
use rishtaara
// Auto-increment order ID (atomic)
const counter = db.counters.findOneAndUpdate(
{ _id: "order_seq" },
{ $inc: { seq: 1 } },
{
upsert: true,
returnDocument: "after" // "before" (default) or "after"
}
)
// counter.seq is the new value — use for orderNumber
// Claim next pending job from a queue
const job = db.jobs.findOneAndUpdate(
{ status: "pending" },
{
$set: { status: "processing", claimedAt: new Date() }
},
{
sort: { priority: -1, createdAt: 1 },
returnDocument: "after"
}
)// Replace entire config document
db.settings.findOneAndReplace(
{ key: "feature_flags" },
{
key: "feature_flags",
darkMode: true,
betaSearch: false,
updatedAt: new Date()
},
{ upsert: true, returnDocument: "after" }
)
// Prefer findOneAndUpdate with $set for partial changes
db.settings.findOneAndUpdate(
{ key: "feature_flags" },
{ $set: { betaSearch: true, updatedAt: new Date() } },
{ returnDocument: "after" }
)findOneAndDelete and legacy findAndModify
findOneAndDelete atomically finds and removes a document — useful for queue consumers and pop-from-stack patterns. The legacy findAndModify command still works but prefer the modern methods.
Real-life example: findOneAndDelete is like taking the top item off a stack — you get the item and it is removed in one action.
// Pop oldest unprocessed notification
const notification = db.notifications.findOneAndDelete(
{ userId: ObjectId("665a1b2c3d4e5f6789012345"), read: false },
{ sort: { createdAt: 1 } }
)
// Returns the deleted document (before deletion)
// Process and remove expired session
const session = db.sessions.findOneAndDelete({
token: "abc123xyz",
expiresAt: { $gt: new Date() }
})
if (!session) {
print("Session invalid or expired")
}// ❌ Legacy command (deprecated)
db.runCommand({
findAndModify: "counters",
query: { _id: "invoice_seq" },
update: { $inc: { seq: 1 } },
new: true, // old name for returnDocument: "after"
upsert: true
})
// ✅ Modern equivalent
db.counters.findOneAndUpdate(
{ _id: "invoice_seq" },
{ $inc: { seq: 1 } },
{ returnDocument: "after", upsert: true }
)returnDocument — before vs after
Choose returnDocument based on what your application logic needs. Counters usually want 'after'. Audit logs may want 'before' to record the previous state.
Real-life example: 'before' is like a before-photo at the gym. 'after' is the progress photo — most apps want to show the new state.
- Use sort with findOneAnd* when multiple documents match — controls which one is picked.
- findOneAnd* methods are atomic on a single document — safe for concurrent workers.
- In Mongoose: findOneAndUpdate maps to the same server command with { new: true } for 'after'.
db.products.insertOne({ name: "Test Item", stock: 100, price: 500 })
// BEFORE (default) — returns old document
const before = db.products.findOneAndUpdate(
{ name: "Test Item" },
{ $inc: { stock: -5 } },
{ returnDocument: "before" }
)
// before.stock === 100
// AFTER — returns updated document
const after = db.products.findOneAndUpdate(
{ name: "Test Item" },
{ $inc: { stock: -5 } },
{ returnDocument: "after" }
)
// after.stock === 95