Skip to content

Fundamentals & CRUD

MongoDB is a NoSQL, document-oriented database. Instead of storing data in rows and columns like SQL, it stores data as JSON-like documents.

SQL World → MongoDB World
─────────────────────────────────────
Database → Database
Table → Collection
Row → Document
Column → Field
Primary Key → _id (ObjectId)
JOIN → $lookup / Embedding

Why MongoDB?

  • Flexible schema — no rigid columns
  • Scales horizontally (sharding)
  • Great for hierarchical/nested data
  • Fast reads/writes for many use cases
  • Native JSON support — perfect for JavaScript/Node.js apps

A MongoDB server can hold multiple databases. Each database is isolated.

use myapp // switch to (or create) database called "myapp"
db // shows current database
show dbs // list all databases

A collection is like a table — a group of related documents. Collections are created automatically when you first insert data.

show collections // list collections in current db
db.createCollection("users") // explicitly create a collection
db.users.drop() // delete a collection

A document is a single record, stored as BSON (Binary JSON). Think of it like a JavaScript object.

{
"_id": ObjectId("64f1b2c3d4e5f6a7b8c9d0e1"),
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
"address": {
"city": "Mumbai",
"state": "Maharashtra"
},
"tags": ["admin", "verified"],
"createdAt": ISODate("2024-01-15T10:30:00Z")
}

FeatureJSONBSON
FormatText (human-readable)Binary (machine-readable)
SpeedSlower to parseFaster to parse
Data typesLimited (string, number, bool, array, object, null)Extended (Date, ObjectId, Binary, Decimal128, etc.)
SizeSmaller textSlightly larger binary
Used forAPI responses, config filesMongoDB internal storage

In practice: You write JSON, MongoDB stores BSON internally. You never deal with BSON directly.

// You write this (JSON-like):
db.users.insertOne({ name: "Bob", createdAt: new Date() })
// MongoDB stores it as BSON internally with full type info
// When you read it back, it looks like JSON again

Every document must have a unique _id field. If you don’t provide one, MongoDB auto-generates an ObjectId.

ObjectId Structure (12 bytes):
┌──────────────┬────────────┬──────────┬──────────────┐
│ 4 bytes │ 5 bytes │ 3 bytes │ Total: 12 │
│ Timestamp │ Random │ Counter │ │
│ (seconds) │ (unique) │ │ │
└──────────────┴────────────┴──────────┘
// Auto-generated ObjectId
{ _id: ObjectId("64f1b2c3d4e5f6a7b8c9d0e1") }
// You can also use a custom _id
{ _id: "user_alice_001", name: "Alice" }
{ _id: 42, name: "Bob" }
// Extract timestamp from ObjectId
const id = new ObjectId("64f1b2c3d4e5f6a7b8c9d0e1");
id.getTimestamp(); // 2023-09-01T... (when it was created)
// Search by ObjectId (must use ObjectId(), not plain string)
db.users.findOne({ _id: ObjectId("64f1b2c3d4e5f6a7b8c9d0e1") })

insertOne() — Insert a single document

db.users.insertOne({
name: "Alice Johnson",
email: "alice@example.com",
age: 28,
role: "admin",
createdAt: new Date()
})
// Result:
// { acknowledged: true, insertedId: ObjectId("...") }

insertMany() — Insert multiple documents at once

db.products.insertMany([
{
name: "Laptop Pro",
price: 75000,
category: "Electronics",
stock: 50,
tags: ["laptop", "computer"]
},
{
name: "Wireless Mouse",
price: 1200,
category: "Electronics",
stock: 200,
tags: ["mouse", "wireless"]
},
{
name: "Notebook A5",
price: 150,
category: "Stationery",
stock: 500,
tags: ["notebook", "writing"]
}
])
// Result:
// { acknowledged: true, insertedCount: 3, insertedIds: { 0: ..., 1: ..., 2: ... } }

Ordered vs Unordered insert:

// By default, insertMany stops on first error (ordered: true)
// To insert all valid docs even if some fail:
db.products.insertMany([ ...docs ], { ordered: false })

find() — Returns all matching documents (returns a cursor)

// Get ALL documents in collection
db.users.find()
// Get all with formatting (MongoDB shell)
db.users.find().pretty()
// Find with filter
db.users.find({ role: "admin" })
// Find users aged 25
db.users.find({ age: 25 })
// Find in nested field
db.users.find({ "address.city": "Mumbai" })

findOne() — Returns the first matching document

// Find by email (common for login)
db.users.findOne({ email: "alice@example.com" })
// Find by _id
db.users.findOne({ _id: ObjectId("64f1b2c3d4e5f6a7b8c9d0e1") })

Real-world example — Task management app:

// Get all incomplete tasks assigned to a user
db.tasks.find({
assignedTo: "alice@example.com",
status: "pending"
})
// Get a specific task by ID
db.tasks.findOne({ _id: ObjectId("...") })

⚠️ Important: Always use update operators like $set. Without them, the document gets replaced entirely.

Common Update Operators:

OperatorWhat it does
$setSet a field value
$unsetRemove a field
$incIncrement a number
$pushAdd item to array
$pullRemove item from array
$addToSetAdd to array only if not already there
$renameRename a field

updateOne() — Updates the first matching document

// ✅ Correct — use $set
db.users.updateOne(
{ email: "alice@example.com" }, // filter
{ $set: { age: 29, role: "superadmin" } } // update
)
// ❌ Wrong — replaces entire document!
db.users.updateOne(
{ email: "alice@example.com" },
{ age: 29 } // DANGER: removes all other fields!
)
// Increment a field (e.g., login count)
db.users.updateOne(
{ email: "alice@example.com" },
{ $inc: { loginCount: 1 } }
)
// Add a tag to array
db.users.updateOne(
{ email: "alice@example.com" },
{ $push: { tags: "premium" } }
)
// Remove a field
db.users.updateOne(
{ email: "alice@example.com" },
{ $unset: { temporaryToken: "" } }
)

updateMany() — Updates all matching documents

// Mark all pending tasks older than a date as "overdue"
db.tasks.updateMany(
{ status: "pending", dueDate: { $lt: new Date() } },
{ $set: { status: "overdue" } }
)
// Deactivate all users with role "trial"
db.users.updateMany(
{ role: "trial" },
{ $set: { isActive: false } }
)

upsert — Update if exists, insert if not:

db.settings.updateOne(
{ userId: "alice123" },
{ $set: { theme: "dark", language: "en" } },
{ upsert: true } // creates doc if not found
)

findOneAndUpdate() — Returns the document (before or after update):

// Get the updated document back
const updated = db.users.findOneAndUpdate(
{ email: "alice@example.com" },
{ $set: { lastLogin: new Date() } },
{ returnDocument: "after" } // return new version
)

deleteOne() — Deletes the first matching document

// Delete a specific user
db.users.deleteOne({ email: "spam@example.com" })
// Delete by ID
db.users.deleteOne({ _id: ObjectId("64f1b2c3...") })

deleteMany() — Deletes all matching documents

// Delete all inactive users
db.users.deleteMany({ isActive: false })
// Delete all completed tasks older than 30 days
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
db.tasks.deleteMany({
status: "completed",
completedAt: { $lt: thirtyDaysAgo }
})
// Delete ALL documents in a collection (careful!)
db.logs.deleteMany({})

findOneAndDelete() — Returns the deleted document:

const deletedTask = db.tasks.findOneAndDelete({ _id: ObjectId("...") })
// Returns the doc before deletion — useful for confirmation

Operation Method Example
─────────────────────────────────────────────────────────────────
Create insertOne() db.users.insertOne({name:"Ali"})
Create Many insertMany() db.users.insertMany([{...},{...}])
Read All find() db.users.find({role:"admin"})
Read One findOne() db.users.findOne({email:"..."})
Update One updateOne() db.users.updateOne({...},{$set:{...}})
Update Many updateMany() db.users.updateMany({...},{$set:{...}})
Delete One deleteOne() db.users.deleteOne({_id: ObjectId(...)})
Delete Many deleteMany() db.users.deleteMany({isActive:false})