🍃 NoSQL Database
MongoDB Complete Cheatsheet
CRUD, aggregation, indexes, schema design and Mongoose — complete MongoDB reference.
01
Core Concepts
▼
Document
JSON-like BSON object: {name:'Ali', age:22}. Unit of storage.
Collection
Group of documents (like a SQL table). Schema-flexible.
Database
Group of collections.
_id
Unique identifier added automatically. ObjectId by default.
Embedding
Store related data inside same document. Faster reads.
Referencing
Store ObjectId reference to another document (like SQL FK). More normalized.
BSON types
ObjectId, Date, NumberInt, NumberLong, Binary, ISODate, Decimal128
Flexible schema
No fixed schema — documents in same collection can have different fields.
MONGOConnect and select
mongosh # connect to localhost mongosh "mongodb://localhost:27017" # explicit mongosh "mongodb+srv://user:pass@cluster.mongodb.net/db" show dbs # list databases use bitwithbite # switch to database show collections # list collections db # show current database
02
CRUD Operations
▼
MONGOCRUD — Create, Read, Update, Delete
// INSERT
db.users.insertOne({ name: 'Ali', age: 22, role: 'student' })
db.users.insertMany([
{ name: 'Sara', age: 25 },
{ name: 'John', age: 30 }
])
// READ
db.users.find() // all documents
db.users.find({ age: 22 }) // where age = 22
db.users.findOne({ name: 'Ali' }) // first match
db.users.find({}, { name: 1, _id: 0 }) // projection: name only
db.users.find().sort({ age: -1 }) // sort descending
db.users.find().limit(10).skip(20) // pagination
db.users.countDocuments({ role: 'student' })
// UPDATE
db.users.updateOne(
{ name: 'Ali' },
{ $set: { age: 23 } } // $set operator
)
db.users.updateMany(
{ role: 'student' },
{ $inc: { points: 10 } } // increment
)
db.users.findOneAndUpdate(
{ name: 'Ali' },
{ $set: { age: 23 } },
{ returnDocument: 'after' } // return updated doc
)
// DELETE
db.users.deleteOne({ name: 'Ali' })
db.users.deleteMany({ age: { $lt: 18 } })
db.users.drop() // delete entire collection
03
Query Operators
▼
MONGOQuery operators
// Comparison
db.users.find({ age: { $gt: 18, $lt: 65 } }) // > 18 AND < 65
db.users.find({ age: { $gte: 18 } })
db.users.find({ role: { $in: ['admin', 'mod'] } })
db.users.find({ role: { $nin: ['banned'] } })
db.users.find({ age: { $ne: 18 } })
// Logical
db.users.find({ $and: [{ age: { $gt: 18 } }, { active: true }] })
db.users.find({ $or: [{ role: 'admin' }, { age: { $gt: 60 } }] })
db.users.find({ age: { $not: { $gt: 65 } } })
// Element
db.users.find({ email: { $exists: true } }) // has email field
db.users.find({ score: { $type: 'number' } })
// Array
db.users.find({ tags: 'javascript' }) // array contains value
db.users.find({ tags: { $all: ['js', 'py'] } }) // contains all
db.users.find({ tags: { $size: 3 } }) // array has 3 elements
db.users.find({ 'address.city': 'Lahore' }) // nested field dot notation
// Regex
db.users.find({ name: { $regex: /^Ali/i } })
db.users.find({ name: { $regex: 'ali', $options: 'i' } })
💡
Use dot notation for nested fields: db.users.find({'address.city': 'Lahore'})
04
Update Operators
▼
MONGOUpdate operators
// $set / $unset
db.users.updateOne({ _id: id }, {
$set: { name: 'New Name', age: 25 },
$unset: { oldField: '' } // remove field
})
// $inc / $mul
db.products.updateOne({ _id: id }, {
$inc: { stock: -1, sold: 1 }, // increment/decrement
$mul: { price: 1.1 } // multiply price by 1.1
})
// $push / $pull (arrays)
db.users.updateOne({ _id: id }, {
$push: { tags: 'javascript' }, // add to array
$addToSet: { tags: 'python' }, // add only if not exists
$pull: { tags: 'php' }, // remove from array
$pop: { log: 1 } // remove last (1) or first (-1)
})
// $push with $each and $slice
db.users.updateOne({ _id: id }, {
$push: { scores: {
$each: [85, 92, 78],
$sort: -1, // sort descending
$slice: 5 // keep only top 5
}}
})
// Upsert — update or insert
db.users.updateOne(
{ email: 'ali@test.com' },
{ $set: { name: 'Ali' } },
{ upsert: true } // creates if not exists
)
05
Aggregation Pipeline
▼
MONGOAggregation pipeline
db.orders.aggregate([
// Stage 1: Filter
{ $match: { status: 'completed', amount: { $gt: 100 } } },
// Stage 2: Group
{ $group: {
_id: '$customerId',
totalSpent: { $sum: '$amount' },
orderCount: { $count: {} },
avgOrder: { $avg: '$amount' },
lastOrder: { $max: '$date' }
}},
// Stage 3: Sort
{ $sort: { totalSpent: -1 } },
// Stage 4: Limit
{ $limit: 10 },
// Stage 5: Lookup (LEFT JOIN)
{ $lookup: {
from: 'customers', // collection to join
localField: '_id', // field from orders
foreignField: '_id', // field from customers
as: 'customerInfo' // output array field
}},
// Stage 6: Unwind (array to documents)
{ $unwind: '$customerInfo' },
// Stage 7: Project (select fields)
{ $project: {
totalSpent: 1,
name: '$customerInfo.name',
email: '$customerInfo.email'
}}
])
💡
Aggregation pipeline is MongoDB's answer to complex SQL queries. $match early to filter before expensive stages.
06
Indexes
▼
MONGOIndexes
// Create indexes
db.users.createIndex({ email: 1 }) // ascending
db.users.createIndex({ email: 1 }, { unique: true })
db.users.createIndex({ name: 1, age: -1 }) // compound
db.users.createIndex({ name: 'text', bio: 'text' }) // text search
db.locations.createIndex({ coordinates: '2dsphere' }) // geospatial
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }) // TTL
// View indexes
db.users.getIndexes()
// Drop index
db.users.dropIndex({ email: 1 })
// Explain — analyze query performance
db.users.find({ email: 'ali@test.com' }).explain('executionStats')
// Look for 'IXSCAN' (good) vs 'COLLSCAN' (bad)
// Index usage hints
db.users.find({ email: 'ali@test.com' }).hint({ email: 1 })
Single field
Fastest for equality: {email: 1}
Compound
Follows ESR rule: Equality, Sort, Range fields
Covered query
Query entirely satisfied by index — no document fetch
TTL index
Auto-deletes documents after expiry — great for sessions/caches
07
Schema Design
▼
MONGOSchema design patterns
// EMBEDDING (denormalized) — when data accessed together
{
_id: ObjectId(),
name: 'Ali',
address: { // embedded document
street: '123 Main',
city: 'Lahore',
country: 'Pakistan'
},
orders: [ // embedded array (limit: ~16MB per doc)
{ id: 1, amount: 500, date: ISODate() },
{ id: 2, amount: 300, date: ISODate() }
]
}
// REFERENCING (normalized) — when data reused or grows large
// users collection
{ _id: ObjectId('user123'), name: 'Ali' }
// orders collection
{ _id: ObjectId(), userId: ObjectId('user123'), amount: 500 }
// Hybrid: summary in parent, details in child
{ _id: 'user123', name: 'Ali',
orderCount: 15, totalSpent: 12000, // summary (denormalized)
recentOrders: [ // only last 5 embedded
{ id: 'ord5', amount: 300 }
]
}
Rule of thumb
Embed if: 1-to-few, read together, static. Reference if: 1-to-many, grows unbounded, accessed independently.
16MB limit
Max document size. Avoid unbounded arrays.
Bucket pattern
Group time-series data into documents: one doc per day/hour with array of readings.
08
Mongoose (Node.js)
▼
JSMongoose (Node.js ODM)
const mongoose = require('mongoose');
// Connect
await mongoose.connect('mongodb://localhost:27017/mydb');
// Schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true, trim: true },
email: { type: String, required: true, unique: true, lowercase: true },
password: { type: String, required: true, select: false },
role: { type: String, enum: ['user','admin'], default: 'user' },
age: { type: Number, min: 0, max: 120 },
tags: [String],
address: { city: String, country: String },
createdAt: { type: Date, default: Date.now }
}, { timestamps: true });
// Middleware (hooks)
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 12);
}
next();
});
const User = mongoose.model('User', userSchema);
// Queries
const users = await User.find({ role: 'admin' }).select('name email').limit(10);
const user = await User.findById(id);
const updated = await User.findByIdAndUpdate(id, { age: 23 }, { new: true });
await User.deleteOne({ _id: id });
// Populate (reference join)
await Order.find().populate('userId', 'name email');
09
Atlas & Deployment
▼
MONGOAtlas & production tips
# MongoDB Atlas CLI connect
mongosh "mongodb+srv://user:pass@cluster.mongodb.net/dbname"
# Atlas setup checklist:
# 1. Create cluster (free M0 available)
# 2. Database Access: create user with readWrite role
# 3. Network Access: add IP or 0.0.0.0/0 for all
# 4. Get connection string from 'Connect' button
# Environment variable (NEVER hardcode!)
MONGODB_URI=mongodb+srv://user:pass@cluster/db
# Connection with options (Node.js)
await mongoose.connect(process.env.MONGODB_URI, {
maxPoolSize: 10, // connection pool
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000,
});
# Backup
mongodump --uri="mongodb+srv://..." --out=./backup
mongorestore --uri="mongodb+srv://..." ./backup
# Performance checklist:
# Always index query fields
# Use projection to return only needed fields
# Use aggregation instead of fetching + processing in app
# Enable slow query logging in Atlas > Performance Advisor
10
Mini Quizzes
▼
❓ Quiz 1
When should you embed vs reference in MongoDB?
Embed when: data accessed together, 1-to-few relationship, data won't grow unboundedly. Reference when: 1-to-many, data reused by many documents, or the sub-data grows large.
❓ Quiz 2
What does the $lookup aggregation stage do?
$lookup performs a left join with another collection. It adds a new array field to each input document containing matching documents from the joined collection.