CRUD stands for Create, Read, Update, Delete — the four fundamental database operations. This lesson covers every method in depth with real examples using a blog posts collection.
Inserts a single document. Returns an object with acknowledged and insertedId.
use blog // Insert one document const result = db.posts.insertOne({ title: "Getting Started with MongoDB", slug: "getting-started-mongodb", content: "MongoDB is a document-oriented NoSQL database...", author: "alice", tags: ["mongodb", "nosql", "database"], published: true, views: 0, createdAt: new Date(), updatedAt: new Date() }) // result: // { acknowledged: true, insertedId: ObjectId("64a1b2c3d4e5f60718293a4b") } // You can also specify your own _id db.posts.insertOne({ _id: "custom-id-001", title: "Custom ID Post" })
Inserts an array of documents in a single operation. More efficient than multiple insertOne calls.
const result = db.posts.insertMany([ { title: "MongoDB Aggregation", author: "bob", tags: ["mongodb", "aggregation"], published: true, views: 150, createdAt: new Date("2024-01-10") }, { title: "Indexing Strategies", author: "carol", tags: ["mongodb", "performance"], published: false, views: 0, createdAt: new Date("2024-01-20") }, { title: "Schema Design Patterns", author: "alice", tags: ["mongodb", "design"], published: true, views: 320, createdAt: new Date("2024-02-01") } ]) // result: // { acknowledged: true, insertedIds: { '0': ObjectId(...), '1': ObjectId(...), '2': ObjectId(...) } } // By default, insertMany stops on first error. // Use ordered: false to continue inserting despite errors: db.posts.insertMany(docs, { ordered: false })
// Find ALL documents (returns a cursor) db.posts.find() // find() with a filter object db.posts.find({ author: "alice" }) // findOne() returns a single document (not a cursor) db.posts.findOne({ slug: "getting-started-mongodb" }) // Find by _id db.posts.findOne({ _id: ObjectId("64a1b2c3d4e5f60718293a4b") }) // Multiple conditions (implicit AND) db.posts.find({ author: "alice", published: true }) // Comparison operators db.posts.find({ views: { $gt: 100 } }) // views > 100 db.posts.find({ views: { $gte: 100 } }) // views >= 100 db.posts.find({ views: { $lt: 50 } }) // views < 50 db.posts.find({ views: { $gte: 50, $lte: 200 } }) // 50 <= views <= 200
// Include only specific fields (1 = include, _id included by default) db.posts.find({}, { title: 1, author: 1 }) // Exclude _id explicitly db.posts.find({}, { title: 1, author: 1, _id: 0 }) // Exclude specific fields (0 = exclude) db.posts.find({}, { content: 0, __v: 0 }) // WARNING: You cannot mix include and exclude // INVALID: db.posts.find({}, { title: 1, content: 0 }) // error! // VALID: _id is the only exception to this rule db.posts.find({}, { title: 1, _id: 0 })
// .sort() — 1 ascending, -1 descending db.posts.find().sort({ views: -1 }) // most viewed first db.posts.find().sort({ createdAt: -1 }) // newest first db.posts.find().sort({ author: 1, views: -1 }) // multi-field sort // .limit() — restrict number of results db.posts.find().limit(10) // .skip() — offset (for pagination) db.posts.find().skip(20).limit(10) // page 3 (10 per page) // .count() / countDocuments() db.posts.find({ published: true }).count() // deprecated in newer drivers db.posts.countDocuments({ published: true }) // preferred db.posts.estimatedDocumentCount() // fastest (uses metadata) // .toArray() — convert cursor to array const posts = db.posts.find({ published: true }).toArray() // Pagination helper function function paginate(collection, filter, page, perPage) { return collection .find(filter) .sort({ createdAt: -1 }) .skip((page - 1) * perPage) .limit(perPage) .toArray() } // paginate(db.posts, { published: true }, 2, 10) → page 2
// $set — set (or add) specific fields db.posts.updateOne( { slug: "getting-started-mongodb" }, // filter { $set: { views: 500, updatedAt: new Date() } } // update ) // Result: // { acknowledged: true, matchedCount: 1, modifiedCount: 1 } // $inc — increment (or decrement) a numeric field db.posts.updateOne( { slug: "getting-started-mongodb" }, { $inc: { views: 1 } } // add 1 to views ) db.posts.updateOne( { slug: "getting-started-mongodb" }, { $inc: { views: -5 } } // subtract 5 from views ) // $push — add an element to an array db.posts.updateOne( { slug: "getting-started-mongodb" }, { $push: { tags: "tutorial" } } ) // $pull — remove an element from an array db.posts.updateOne( { slug: "getting-started-mongodb" }, { $pull: { tags: "nosql" } } ) // $unset — remove a field entirely db.posts.updateOne( { slug: "getting-started-mongodb" }, { $unset: { legacyField: "" } } // value doesn't matter ) // $rename — rename a field db.posts.updateOne( { slug: "getting-started-mongodb" }, { $rename: { "oldFieldName": "newFieldName" } } )
// Add a new field to ALL documents db.posts.updateMany( {}, // empty filter = match all { $set: { featured: false } } ) // Set all unpublished posts to archived db.posts.updateMany( { published: false }, { $set: { status: "archived", archivedAt: new Date() } } ) // Increment views for all posts by a specific author db.posts.updateMany( { author: "alice" }, { $inc: { views: 10 } } ) // $addToSet — like $push but prevents duplicates db.posts.updateMany( { author: "alice" }, { $addToSet: { tags: "featured" } } // only adds if not already in array )
// updateOne() — MODIFIES specific fields, keeps the rest db.posts.updateOne( { _id: someId }, { $set: { title: "New Title" } } ) // Result: only title changes, all other fields remain // replaceOne() — REPLACES the entire document (except _id) db.posts.replaceOne( { _id: someId }, { title: "Completely New Document", author: "dave", createdAt: new Date() // ALL previous fields are gone (views, tags, etc.) } )
updateOne with operators ($set, etc.) when you want to change specific fields. Use replaceOne only when you want to completely overwrite a document.The upsert: true option creates the document if it doesn't exist, or updates it if it does. This is a powerful pattern for "ensure this document exists" scenarios.
// Upsert: update if exists, insert if not db.pageStats.updateOne( { slug: "getting-started-mongodb" }, { $inc: { views: 1 }, $setOnInsert: { // only set on INSERT, not UPDATE createdAt: new Date(), slug: "getting-started-mongodb" } }, { upsert: true } ) // First call: creates { slug, views:1, createdAt } // Subsequent calls: increments views only // Upsert a user by email db.users.updateOne( { email: "newuser@example.com" }, { $set: { lastLoginAt: new Date() }, $setOnInsert: { email: "newuser@example.com", role: "user", createdAt: new Date() } }, { upsert: true } )
These methods atomically find and modify a document, returning either the original or updated version in a single operation — useful for implementing queues, counters, or atomic state changes.
// findOneAndUpdate — returns the ORIGINAL doc by default const original = db.posts.findOneAndUpdate( { slug: "getting-started-mongodb" }, { $inc: { views: 1 } } ) // original.views is the OLD value // returnDocument: "after" to get the UPDATED doc const updated = db.posts.findOneAndUpdate( { slug: "getting-started-mongodb" }, { $inc: { views: 1 } }, { returnDocument: "after" } ) // updated.views is the NEW value // findOneAndUpdate with upsert db.counters.findOneAndUpdate( { name: "postId" }, { $inc: { seq: 1 } }, { upsert: true, returnDocument: "after" } ) // findOneAndDelete — returns the deleted document const deleted = db.tasks.findOneAndDelete( { status: "pending" }, { sort: { priority: -1 } } // delete highest priority pending task ) // deleted contains the task data for processing
// deleteOne — remove first matching document db.posts.deleteOne({ slug: "draft-post" }) // Result: // { acknowledged: true, deletedCount: 1 } // deleteMany — remove all matching documents db.posts.deleteMany({ published: false }) // Delete all documents (DANGEROUS — collection still exists) db.posts.deleteMany({}) // Drop the entire collection (removes collection + indexes) db.posts.drop() // Delete by _id db.posts.deleteOne({ _id: ObjectId("64a1b2c3d4e5f60718293a4b") }) // Delete old records (older than 30 days) const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) db.logs.deleteMany({ createdAt: { $lt: thirtyDaysAgo } })
find() with the same filter first to preview what will be deleted before running deleteMany().| Operator | Category | Description | Example |
|---|---|---|---|
$set | Field | Set field value (adds if missing) | {$set: {name: "Bob"}} |
$unset | Field | Remove a field from document | {$unset: {oldField: ""}} |
$rename | Field | Rename a field | {$rename: {old: "new"}} |
$inc | Numeric | Increment by n (negative = decrement) | {$inc: {count: 1}} |
$mul | Numeric | Multiply field value by n | {$mul: {price: 1.1}} |
$min | Comparison | Update only if new value is less | {$min: {score: 50}} |
$max | Comparison | Update only if new value is greater | {$max: {score: 100}} |
$currentDate | Date | Set field to current date/timestamp | {$currentDate: {updatedAt: true}} |
$setOnInsert | Field | Set fields only during upsert insert | {$setOnInsert: {createdAt: new Date()}} |
$push | Array | Add element to array | {$push: {tags: "new"}} |
$pull | Array | Remove matching elements from array | {$pull: {tags: "old"}} |
$pop | Array | Remove first (-1) or last (1) element | {$pop: {items: 1}} |
$addToSet | Array | Add to array only if unique | {$addToSet: {tags: "unique"}} |
$pullAll | Array | Remove all matching values | {$pullAll: {scores: [1,2,3]}} |
$each | Array modifier | Push multiple values | {$push: {tags: {$each: ["a","b"]}}} |
$sort | Array modifier | Sort array during $push | {$push: {scores: {$each:[], $sort:-1}}} |
$slice | Array modifier | Limit array size during $push | {$push: {log: {$each:[], $slice:-10}}} |
$bit | Bitwise | Bitwise AND / OR / XOR | {$bit: {flags: {and: 5}}} |
// ---- SETUP ---- use blog_exercise // ---- CREATE ---- db.posts.insertMany([ { title: "First Post", author: "alice", body: "Hello World!", tags: ["general"], likes: 0, published: false, createdAt: new Date() }, { title: "MongoDB Tips", author: "bob", body: "Use indexes on frequently queried fields.", tags: ["mongodb", "tips"], likes: 42, published: true, createdAt: new Date() } ]) // ---- READ ---- // All published posts, sorted newest first db.posts.find({ published: true }).sort({ createdAt: -1 }) // ---- UPDATE ---- // Publish alice's first post and set updated time db.posts.updateOne( { author: "alice", title: "First Post" }, { $set: { published: true, updatedAt: new Date() }, $addToSet: { tags: "featured" } } ) // Simulate a user liking bob's post db.posts.updateOne( { title: "MongoDB Tips" }, { $inc: { likes: 1 } } ) // ---- DELETE ---- // Remove all unpublished posts older than 7 days const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) db.posts.deleteMany({ published: false, createdAt: { $lt: cutoff } }) // ---- VERIFY ---- db.posts.find().sort({ createdAt: -1 })