CRUD advanced.
Array updates
// Add unique
db.coll.updateOne({ _id }, { $addToSet: { tags: "x" } })
db.coll.updateOne({ _id }, { $addToSet: { tags: { $each: ["a", "b"] } } })
// Push
db.coll.updateOne({ _id }, { $push: { items: { ts: new Date() } } })
db.coll.updateOne({ _id }, { $push: { items: { $each: [...], $slice: -100, $sort: { ts: -1 } } } })
// Remove
db.coll.updateOne({ _id }, { $pull: { tags: "x" } })
db.coll.updateOne({ _id }, { $pull: { items: { ts: { $lt: cutoff } } } })
db.coll.updateOne({ _id }, { $pullAll: { tags: ["a", "b"] } })
db.coll.updateOne({ _id }, { $pop: { items: -1 } }) // -1=first, 1=last
Update array element
// By position
db.coll.updateOne({ _id }, { $set: { "items.0.done": true } })
// By match ($)
db.coll.updateOne({ _id, "items.id": "abc" }, { $set: { "items.$.done": true } })
// All matching ($[])
db.coll.updateOne({ _id }, { $set: { "items.$[].done": true } })
// Filtered ($[<id>])
db.coll.updateOne(
{ _id },
{ $set: { "items.$[item].done": true } },
{ arrayFilters: [{ "item.priority": "high" }] }
)
findOneAndUpdate (atomic)
const doc = db.counters.findOneAndUpdate(
{ _id: "users" },
{ $inc: { value: 1 } },
{ upsert: true, returnDocument: "after" }
)
Returns the doc; atomic increment.
findOneAndDelete
const job = db.jobs.findOneAndDelete(
{ status: "ready" },
{ sort: { priority: -1 } }
)
For job queues.
Upsert
db.users.updateOne(
{ email: "..." },
{ $set: { name: "Alice", updated: new Date() }, $setOnInsert: { created: new Date() } },
{ upsert: true }
)
Bulk write
db.coll.bulkWrite([
{ insertOne: { document: {...} } },
{ updateOne: { filter: {...}, update: { $set: {...} } } },
{ updateMany: { filter: {...}, update: {...} } },
{ deleteOne: { filter: {...} } },
{ replaceOne: { filter: {...}, replacement: {...} } },
], { ordered: false })
ordered: false = parallel; faster but errors don’t stop.
Cursor
const cursor = db.coll.find({}).batchSize(1000)
while (cursor.hasNext()) {
const doc = cursor.next()
// process
}
Or in driver: .toArray() for small, iterate for large.
Pagination
// Offset (slow for big skip)
db.coll.find().skip(1000).limit(20)
// Cursor-based (fast)
db.coll.find({ _id: { $gt: last_id } }).sort({ _id: 1 }).limit(20)
Prefer cursor.
Transactions
const session = db.getMongo().startSession()
session.startTransaction()
try {
db.accounts.updateOne({ _id: a }, { $inc: { balance: -100 } }, { session })
db.accounts.updateOne({ _id: b }, { $inc: { balance: 100 } }, { session })
session.commitTransaction()
} catch (e) {
session.abortTransaction()
} finally {
session.endSession()
}
Replica set / sharded cluster required.
Read / write concern
db.coll.insertOne(
{ ... },
{ writeConcern: { w: "majority", j: true } } // majority + journal
)
db.coll.find({}, {}).readConcern("snapshot")
Change streams
const stream = db.coll.watch([
{ $match: { operationType: "insert" } }
])
for (const change of stream) {
process(change)
}
CDC pattern. Requires replica set.
Projection
// Include
db.users.find({}, { name: 1, email: 1, _id: 0 })
// Exclude
db.users.find({}, { password: 0 })
// Array slice
db.posts.find({}, { comments: { $slice: 5 } })
db.posts.find({}, { comments: { $slice: [10, 5] } }) // skip 10, take 5
Common mistakes
_idexcluded but you still want it (include explicitly).- Forgetting
arrayFilterssyntax. - Bulk write without
ordered: false→ all-or-nothing slow. - Transaction in standalone (requires RS).
- Big skip pagination (slow).
Read this next
If you want my MongoDB recipes, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .