🍃 NoSQL Database
MongoDB Complete Cheatsheet
CRUD, aggregation, indexes, schema design and Mongoose — complete MongoDB reference.
📖 10 sections
⏱ 22 min read
✅ Quizzes included
🌙 Dark mode
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.