MongoDB Cheatsheet

What is MongoDB MongoDB vs SQL Architecture BSON Format Installation Mongo Shell DBs & Collections Creating DB Creating Collection Data Types CRUD Operations Query Operators Projection Sorting Limit & Skip Aggregation Indexes Data Modeling Embedded vs References Arrays Validation Transactions Backup & Restore MongoDB Atlas Authentication Replica Sets Sharding Performance Mongoose CLI Commands

What is MongoDB

Intro

MongoDB is a popular NoSQL document database that stores data in flexible, JSON-like documents. It's designed for scalability and performance, making it ideal for modern applications.

MongoDB vs SQL Databases

Comparison
// MongoDB Document
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "email": "john@example.com",
  "address": {
    "street": "123 Main St",
    "city": "New York"
  },
  "hobbies": ["reading", "gaming"]
}

// SQL Tables
-- users table
-- addresses table  
-- hobbies table
-- user_hobbies junction table

MongoDB Architecture

Architecture
// Architecture Hierarchy
Database: "myapp"
  └── Collection: "users"
      └── Document: { "_id": ObjectId("..."), "name": "John" }
          └── Field: "name" = "John"

// Multiple collections in one database
use myapp
db.users.find()
db.products.find()
db.orders.find()

BSON Format

BSON

BSON (Binary JSON) is the binary format used by MongoDB to store documents. It extends JSON with additional data types.

// BSON Data Types
{
  "_id": ObjectId("507f1f77bcf86cd799439011"),
  "string": "Hello World",
  "number": 42,
  "boolean": true,
  "date": new Date("2023-01-01"),
  "array": [1, 2, 3],
  "object": { "key": "value" },
  "null": null,
  "regex": /pattern/i,
  "binary": BinData(0, "base64data"),
  "timestamp": Timestamp(1234567890, 1)
}

Installation & Setup

Installation
# Ubuntu/Debian
sudo apt-get install mongodb

# macOS with Homebrew
brew tap mongodb/brew
brew install mongodb-community

# Windows
# Download MSI installer from mongodb.com

# Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod

# Check status
sudo systemctl status mongod

# Connect to MongoDB
mongosh

Mongo Shell / MongoDB Compass

Tools
# Connect to MongoDB
mongosh
mongosh "mongodb://localhost:27017"
mongosh "mongodb://username:password@host:port/database"

# Basic shell commands
show dbs                    # List databases
use mydatabase             # Switch database
show collections           # List collections
help                       # Show help
exit                       # Exit shell

# MongoDB Compass
# Download from mongodb.com/try/download/compass
# Connect using connection string
mongodb://localhost:27017

Databases & Collections

Structure
// Database operations
show dbs                    // List all databases
use myapp                  // Switch to database
db                         // Show current database
db.dropDatabase()          // Delete current database

// Collection operations
show collections           // List collections
db.createCollection("users") // Create collection
db.users.drop()            // Delete collection
db.users.stats()           // Collection statistics

// Document structure
{
  "_id": ObjectId("..."),  // Unique identifier
  "field1": "value1",      // Field-value pair
  "field2": 123,
  "nested": {              // Nested document
    "subfield": "value"
  },
  "array": [1, 2, 3]       // Array field
}

Creating a Database

Database
// Create/switch to database
use myapp

// Database is created when you insert first document
db.users.insertOne({
  name: "John Doe",
  email: "john@example.com"
})

// List databases (will show myapp now)
show dbs

// Database naming rules
// - Cannot be empty string
// - Cannot contain: /, \, ., " ", $, \0
// - Cannot start with reserved words
// - Case sensitive

// Examples of valid names
use myapp
use my_app
use myApp123
use my-app

// Examples of invalid names
use my app    // Contains space
use my.app    // Contains dot
use $myapp    // Starts with $

Creating a Collection

Collection
// Method 1: Explicit creation
db.createCollection("users")

// Method 2: Implicit creation (insert document)
db.products.insertOne({
  name: "Laptop",
  price: 999.99
})

// Create collection with options
db.createCollection("logs", {
  capped: true,
  size: 1000000,  // 1MB
  max: 1000       // Max 1000 documents
})

// Collection naming rules
// - Cannot be empty string
// - Cannot contain: /, \, ., " ", $, \0
// - Cannot start with reserved words
// - Case sensitive

// Valid collection names
db.users
db.user_profiles
db.userProfiles
db.user-profiles

// Invalid collection names
db.user profiles  // Contains space
db.user.profiles  // Contains dot
db.user-profiles  // Starts with -

Data Types in MongoDB

Data Types
// MongoDB Data Types
{
  // String
  "name": "John Doe",
  "email": "john@example.com",
  
  // Numbers
  "age": 30,                    // Integer
  "salary": 75000.50,           // Double
  "decimal": NumberDecimal("123.456"), // Decimal128
  
  // Boolean
  "isActive": true,
  "verified": false,
  
  // Date
  "createdAt": new Date("2023-01-01"),
  "updatedAt": new Date(),
  
  // ObjectId
  "_id": ObjectId("507f1f77bcf86cd799439011"),
  "userId": ObjectId("507f1f77bcf86cd799439012"),
  
  // Array
  "hobbies": ["reading", "gaming", "coding"],
  "scores": [85, 92, 78, 96],
  
  // Object (Embedded Document)
  "address": {
    "street": "123 Main St",
    "city": "New York",
    "zipCode": "10001"
  },
  
  // Null
  "middleName": null,
  
  // Regular Expression
  "pattern": /^[A-Za-z]+$/,
  
  // Binary Data
  "binaryData": BinData(0, "base64encodeddata"),
  
  // Timestamp
  "timestamp": Timestamp(1234567890, 1)
}

CRUD Operations

CRUD
// CREATE
// Insert single document
db.users.insertOne({
  name: "John Doe",
  email: "john@example.com",
  age: 30
})

// Insert multiple documents
db.users.insertMany([
  { name: "Jane Smith", email: "jane@example.com", age: 25 },
  { name: "Bob Johnson", email: "bob@example.com", age: 35 }
])

// READ
// Find all documents
db.users.find()

// Find one document
db.users.findOne({ name: "John Doe" })

// Find with filter
db.users.find({ age: { $gt: 25 } })

// UPDATE
// Update one document
db.users.updateOne(
  { name: "John Doe" },
  { $set: { age: 31 } }
)

// Update multiple documents
db.users.updateMany(
  { age: { $lt: 30 } },
  { $inc: { age: 1 } }
)

// DELETE
// Delete one document
db.users.deleteOne({ name: "John Doe" })

// Delete multiple documents
db.users.deleteMany({ age: { $lt: 25 } })

Query Operators

Operators
// Comparison Operators
db.users.find({ age: { $gt: 25 } })        // Greater than
db.users.find({ age: { $gte: 25 } })       // Greater than or equal
db.users.find({ age: { $lt: 30 } })        // Less than
db.users.find({ age: { $lte: 30 } })       // Less than or equal
db.users.find({ age: { $ne: 25 } })        // Not equal
db.users.find({ age: { $in: [25, 30, 35] } }) // In array
db.users.find({ age: { $nin: [25, 30] } }) // Not in array

// Logical Operators
db.users.find({
  $and: [
    { age: { $gt: 25 } },
    { email: { $regex: /@gmail\.com$/ } }
  ]
})

db.users.find({
  $or: [
    { age: { $lt: 25 } },
    { age: { $gt: 35 } }
  ]
})

// Element Operators
db.users.find({ email: { $exists: true } }) // Field exists
db.users.find({ age: { $type: "number" } }) // Field type

// Evaluation Operators
db.users.find({ name: { $regex: /^J/ } })  // Starts with J
db.users.find({ name: { $regex: /n$/ } })  // Ends with n
db.users.find({ name: { $regex: /john/i } }) // Case insensitive

// Complex queries
db.users.find({
  $and: [
    { age: { $gte: 18, $lte: 65 } },
    { email: { $exists: true } },
    { $or: [
      { name: { $regex: /^J/ } },
      { name: { $regex: /^B/ } }
    ]}
  ]
})

Projection

Projection
// Include specific fields
db.users.find({}, { name: 1, email: 1 })
// Returns: { "_id": ..., "name": "...", "email": "..." }

// Exclude specific fields
db.users.find({}, { password: 0, secretKey: 0 })
// Returns all fields except password and secretKey

// Include with _id exclusion
db.users.find({}, { name: 1, email: 1, _id: 0 })
// Returns: { "name": "...", "email": "..." }

// Nested field projection
db.users.find({}, { "address.city": 1, "address.zipCode": 1 })

// Array element projection
db.users.find({}, { "hobbies.0": 1, "hobbies.1": 1 })

// Conditional projection
db.users.find({}, {
  name: 1,
  email: 1,
  age: {
    $cond: {
      if: { $gte: ["$age", 18] },
      then: "$age",
      else: "$$REMOVE"
    }
  }
})

// Common projection patterns
// Get only essential user info
db.users.find({}, { name: 1, email: 1, _id: 0 })

// Exclude sensitive data
db.users.find({}, { password: 0, ssn: 0, creditCard: 0 })

// Get user summary
db.users.find({}, { 
  name: 1, 
  age: 1, 
  "address.city": 1, 
  _id: 0 
})

Sorting

Sorting
// Basic sorting
db.users.find().sort({ name: 1 })        // Ascending by name
db.users.find().sort({ age: -1 })        // Descending by age

// Compound sorting
db.users.find().sort({ age: -1, name: 1 })
// Sort by age descending, then name ascending

// Sort with filter
db.users.find({ age: { $gt: 25 } })
  .sort({ name: 1 })

// Sort with projection
db.users.find({}, { name: 1, age: 1, _id: 0 })
  .sort({ age: -1 })

// Sort by nested fields
db.users.find().sort({ "address.city": 1 })

// Sort by array elements
db.users.find().sort({ "scores.0": -1 })

// Sort with limit (top 10 oldest users)
db.users.find()
  .sort({ age: -1 })
  .limit(10)

// Sort by date
db.users.find().sort({ createdAt: -1 })

// Sort by multiple criteria
db.users.find().sort({
  isActive: -1,    // Active users first
  age: -1,         // Then by age descending
  name: 1          // Then by name ascending
})

Limiting & Skipping

Pagination
// Limit results
db.users.find().limit(5)        // Get only 5 documents
db.users.find().limit(10)       // Get only 10 documents

// Skip documents
db.users.find().skip(5)         // Skip first 5 documents
db.users.find().skip(10)        // Skip first 10 documents

// Pagination (page 1: documents 1-10)
db.users.find()
  .sort({ name: 1 })
  .skip(0)
  .limit(10)

// Pagination (page 2: documents 11-20)
db.users.find()
  .sort({ name: 1 })
  .skip(10)
  .limit(10)

// Pagination (page 3: documents 21-30)
db.users.find()
  .sort({ name: 1 })
  .skip(20)
  .limit(10)

// Get top 5 users by age
db.users.find()
  .sort({ age: -1 })
  .limit(5)

// Skip inactive users, get next 10
db.users.find({ isActive: true })
  .sort({ createdAt: -1 })
  .skip(10)
  .limit(10)

// Pagination function
function getUsers(page, pageSize) {
  const skip = (page - 1) * pageSize;
  return db.users.find()
    .sort({ name: 1 })
    .skip(skip)
    .limit(pageSize);
}

// Usage
getUsers(1, 10);  // Page 1, 10 items per page
getUsers(2, 10);  // Page 2, 10 items per page

Aggregation Framework

Aggregation
// Basic aggregation
db.users.aggregate([
  { $match: { age: { $gte: 18 } } },
  { $group: { _id: "$city", count: { $sum: 1 } } },
  { $sort: { count: -1 } }
])

// Count users by age group
db.users.aggregate([
  {
    $group: {
      _id: {
        $cond: {
          if: { $lt: ["$age", 25] },
          then: "18-24",
          else: {
            $cond: {
              if: { $lt: ["$age", 35] },
              then: "25-34",
              else: "35+"
            }
          }
        }
      },
      count: { $sum: 1 },
      avgAge: { $avg: "$age" }
    }
  },
  { $sort: { _id: 1 } }
])

// Join with orders collection
db.users.aggregate([
  {
    $lookup: {
      from: "orders",
      localField: "_id",
      foreignField: "userId",
      as: "orders"
    }
  },
  {
    $project: {
      name: 1,
      email: 1,
      orderCount: { $size: "$orders" },
      totalSpent: { $sum: "$orders.amount" }
    }
  }
])

// Complex aggregation
db.users.aggregate([
  { $match: { isActive: true } },
  {
    $lookup: {
      from: "orders",
      localField: "_id",
      foreignField: "userId",
      as: "orders"
    }
  },
  {
    $addFields: {
      totalOrders: { $size: "$orders" },
      totalSpent: { $sum: "$orders.amount" }
    }
  },
  {
    $group: {
      _id: "$city",
      userCount: { $sum: 1 },
      avgOrders: { $avg: "$totalOrders" },
      totalRevenue: { $sum: "$totalSpent" }
    }
  },
  { $sort: { totalRevenue: -1 } }
])

Indexes

Indexes
// Create single field index
db.users.createIndex({ email: 1 })
db.users.createIndex({ age: -1 })

// Create compound index
db.users.createIndex({ lastName: 1, firstName: 1 })
db.users.createIndex({ city: 1, age: -1 })

// Create unique index
db.users.createIndex({ email: 1 }, { unique: true })

// Create sparse index (only on documents with field)
db.users.createIndex({ middleName: 1 }, { sparse: true })

// Create text index
db.users.createIndex({ name: "text", bio: "text" })

// Create geospatial index
db.users.createIndex({ location: "2dsphere" })

// List indexes
db.users.getIndexes()

// Drop index
db.users.dropIndex("email_1")
db.users.dropIndex({ email: 1 })

// Drop all indexes (except _id)
db.users.dropIndexes()

// Index options
db.users.createIndex(
  { email: 1 },
  {
    unique: true,
    sparse: true,
    background: true,
    name: "email_unique_index"
  }
)

// Compound index order matters
// Good for queries: { lastName: "Smith", firstName: "John" }
db.users.createIndex({ lastName: 1, firstName: 1 })

// Bad for queries: { firstName: "John" } (without lastName)
// Index won't be used efficiently

Data Modeling in MongoDB

Modeling
// Embedded approach (One-to-Few)
// User with embedded addresses
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "email": "john@example.com",
  "addresses": [
    {
      "type": "home",
      "street": "123 Main St",
      "city": "New York",
      "zipCode": "10001"
    },
    {
      "type": "work",
      "street": "456 Business Ave",
      "city": "New York",
      "zipCode": "10002"
    }
  ]
}

// Reference approach (One-to-Many)
// User document
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "email": "john@example.com"
}

// Orders collection
{
  "_id": ObjectId("..."),
  "userId": ObjectId("..."), // Reference to user
  "items": [...],
  "total": 150.00,
  "date": new Date()
}

// Hybrid approach
// User with some embedded data and references
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "email": "john@example.com",
  "profile": {              // Embedded (small, frequently accessed)
    "bio": "Software developer",
    "avatar": "url",
    "preferences": {...}
  },
  "orderIds": [             // References (large, less frequent)
    ObjectId("..."),
    ObjectId("...")
  ]
}

Embedded Documents vs References

Comparison
// EMBEDDED DOCUMENTS
// Pros: Atomic operations, single query
// Cons: Document size limit, duplication

// User with embedded profile
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "profile": {
    "bio": "Software developer",
    "avatar": "url",
    "social": {
      "twitter": "@johndoe",
      "linkedin": "linkedin.com/in/johndoe"
    }
  }
}

// Query embedded data
db.users.find({ "profile.social.twitter": "@johndoe" })

// Update embedded data
db.users.updateOne(
  { _id: ObjectId("...") },
  { $set: { "profile.bio": "Updated bio" } }
)

// REFERENCES
// Pros: Normalized data, independent updates
// Cons: Multiple queries, joins needed

// User document
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "profileId": ObjectId("...")
}

// Profile document
{
  "_id": ObjectId("..."),
  "bio": "Software developer",
  "avatar": "url",
  "social": {...}
}

// Query with lookup
db.users.aggregate([
  {
    $lookup: {
      from: "profiles",
      localField: "profileId",
      foreignField: "_id",
      as: "profile"
    }
  }
])

// WHEN TO USE EACH
// Use Embedded when:
// - Data is small and related
// - Data is frequently accessed together
// - Data doesn't change independently

// Use References when:
// - Data is large
// - Data is shared across documents
// - Data changes independently
// - Need to query data separately

Working with Arrays

Arrays
// Array query operators
// $elemMatch - match array elements
db.users.find({
  hobbies: { $elemMatch: { $in: ["reading", "gaming"] } }
})

// $size - match array length
db.users.find({ hobbies: { $size: 3 } })

// Array update operators
// $push - add to array
db.users.updateOne(
  { _id: ObjectId("...") },
  { $push: { hobbies: "swimming" } }
)

// $push with $each - add multiple elements
db.users.updateOne(
  { _id: ObjectId("...") },
  { $push: { hobbies: { $each: ["swimming", "cycling"] } } }
)

// $pull - remove from array
db.users.updateOne(
  { _id: ObjectId("...") },
  { $pull: { hobbies: "gaming" } }
)

// $addToSet - add if not exists
db.users.updateOne(
  { _id: ObjectId("...") },
  { $addToSet: { hobbies: "reading" } }
)

// Array position operators
// $ - update first matching element
db.users.updateOne(
  { "scores": { $gt: 80 } },
  { $set: { "scores.$": 85 } }
)

// Array with objects
{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "skills": [
    { "name": "JavaScript", "level": "expert" },
    { "name": "Python", "level": "intermediate" },
    { "name": "MongoDB", "level": "beginner" }
  ]
}

// Query array objects
db.users.find({
  "skills": {
    $elemMatch: {
      "name": "JavaScript",
      "level": "expert"
    }
  }
})

// Update array objects
db.users.updateOne(
  { "skills.name": "JavaScript" },
  { $set: { "skills.$.level": "master" } }
)

// Array aggregation
db.users.aggregate([
  { $unwind: "$hobbies" },
  { $group: { _id: "$hobbies", count: { $sum: 1 } } },
  { $sort: { count: -1 } }
])

Validation Rules & Schema Validation

Validation
// Create collection with validation
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email", "age"],
      properties: {
        name: {
          bsonType: "string",
          description: "Name must be a string"
        },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "Email must be valid format"
        },
        age: {
          bsonType: "int",
          minimum: 0,
          maximum: 120,
          description: "Age must be between 0 and 120"
        },
        phone: {
          bsonType: "string",
          pattern: "^\\+?[1-9]\\d{1,14}$",
          description: "Phone must be valid format"
        }
      }
    }
  },
  validationLevel: "strict",
  validationAction: "error"
})

// Add validation to existing collection
db.runCommand({
  collMod: "users",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email"],
      properties: {
        name: { bsonType: "string" },
        email: { bsonType: "string" }
      }
    }
  }
})

// Complex validation example
db.createCollection("products", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "category"],
      properties: {
        name: {
          bsonType: "string",
          minLength: 1,
          maxLength: 100
        },
        price: {
          bsonType: "double",
          minimum: 0
        },
        category: {
          enum: ["electronics", "clothing", "books", "food"]
        },
        tags: {
          bsonType: "array",
          items: { bsonType: "string" },
          maxItems: 10
        },
        inStock: {
          bsonType: "bool"
        }
      }
    }
  }
})

Transactions (Intro to ACID)

Transactions
// Basic transaction
const session = db.getMongo().startSession();

session.startTransaction();

try {
  // Transfer money between accounts
  db.accounts.updateOne(
    { _id: ObjectId("account1"), balance: { $gte: 100 } },
    { $inc: { balance: -100 } },
    { session }
  );
  
  db.accounts.updateOne(
    { _id: ObjectId("account2") },
    { $inc: { balance: 100 } },
    { session }
  );
  
  // Commit transaction
  await session.commitTransaction();
} catch (error) {
  // Abort transaction on error
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

// Transaction with options
const session = db.getMongo().startSession({
  defaultTransactionOptions: {
    readConcern: { level: "snapshot" },
    writeConcern: { w: "majority" },
    readPreference: "primary"
  }
});

// Multi-collection transaction
session.startTransaction();

try {
  // Update user
  db.users.updateOne(
    { _id: ObjectId("user1") },
    { $inc: { balance: -50 } },
    { session }
  );
  
  // Create order
  db.orders.insertOne({
    userId: ObjectId("user1"),
    amount: 50,
    items: ["product1", "product2"]
  }, { session });
  
  // Update inventory
  db.inventory.updateOne(
    { productId: "product1" },
    { $inc: { stock: -1 } },
    { session }
  );
  
  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

Backup & Restore

Backup
# Backup entire database
mongodump --db myapp --out /backup/path

# Backup specific collection
mongodump --db myapp --collection users --out /backup/path

# Backup with authentication
mongodump --uri "mongodb://username:password@host:port/myapp" --out /backup/path

# Restore entire database
mongorestore --db myapp /backup/path/myapp

# Restore specific collection
mongorestore --db myapp --collection users /backup/path/myapp/users.bson

# Restore with authentication
mongorestore --uri "mongodb://username:password@host:port/myapp" /backup/path/myapp

# Export to JSON
mongoexport --db myapp --collection users --out users.json

# Export to CSV
mongoexport --db myapp --collection users --type=csv --fields name,email,age --out users.csv

# Import from JSON
mongoimport --db myapp --collection users --file users.json

# Import from CSV
mongoimport --db myapp --collection users --type=csv --headerline --file users.csv

# Backup with compression
mongodump --db myapp --gzip --out /backup/path

# Restore compressed backup
mongorestore --gzip --db myapp /backup/path/myapp

# Backup with query filter
mongodump --db myapp --collection users --query '{"age": {"$gte": 18}}' --out /backup/path

# Automated backup script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backup/mongodb"
mongodump --db myapp --out "$BACKUP_DIR/backup_$DATE"
tar -czf "$BACKUP_DIR/backup_$DATE.tar.gz" "$BACKUP_DIR/backup_$DATE"
rm -rf "$BACKUP_DIR/backup_$DATE"

MongoDB Atlas (Cloud)

Atlas
// Atlas connection string
mongodb+srv://username:password@cluster.mongodb.net/database?retryWrites=true&w=majority

// Connect to Atlas
mongosh "mongodb+srv://username:password@cluster.mongodb.net/myapp"

// Atlas connection options
const uri = "mongodb+srv://username:password@cluster.mongodb.net/myapp?retryWrites=true&w=majority";

const client = new MongoClient(uri, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  maxPoolSize: 10,
  serverSelectionTimeoutMS: 5000,
  socketTimeoutMS: 45000
});

// Atlas features
// - Automatic backups
// - Point-in-time recovery
// - Global clusters
// - Auto-scaling
// - Monitoring and alerts
// - Data lake
// - Charts (visualization)

// Atlas CLI
atlas cluster create myCluster --provider AWS --region us-east-1 --tier M10

atlas cluster list

atlas cluster pause myCluster

atlas cluster resume myCluster

atlas cluster delete myCluster

// Atlas backup
atlas backup create --clusterName myCluster

atlas backup list --clusterName myCluster

atlas backup restore --clusterName myCluster --backupId backupId

Authentication & Role-Based Access Control

Auth
// Create user
use admin
db.createUser({
  user: "appuser",
  pwd: "securepassword",
  roles: [
    { role: "readWrite", db: "myapp" },
    { role: "read", db: "reports" }
  ]
})

// Create user in specific database
use myapp
db.createUser({
  user: "appuser",
  pwd: "securepassword",
  roles: ["readWrite"]
})

// Built-in roles
// read - Read data only
// readWrite - Read and write data
// dbAdmin - Database administration
// userAdmin - User management
// clusterAdmin - Cluster administration
// root - Superuser

// Create custom role
use admin
db.createRole({
  role: "appManager",
  privileges: [
    {
      resource: { db: "myapp", collection: "users" },
      actions: ["find", "insert", "update"]
    },
    {
      resource: { db: "myapp", collection: "orders" },
      actions: ["find", "insert", "update", "delete"]
    }
  ],
  roles: []
})

// Assign role to user
use admin
db.grantRolesToUser("appuser", [
  { role: "appManager", db: "admin" }
])

// List users
use admin
db.getUsers()

// List roles
use admin
db.getRoles()

// Drop user
use admin
db.dropUser("appuser")

// Drop role
use admin
db.dropRole("appManager")

// Authentication methods
// 1. Username/Password
mongosh --username appuser --password securepassword --authenticationDatabase admin

// 2. Connection string
mongodb://appuser:securepassword@localhost:27017/myapp?authSource=admin

// 3. Environment variables
export MONGO_USERNAME=appuser
export MONGO_PASSWORD=securepassword
mongosh --username $MONGO_USERNAME --password $MONGO_PASSWORD

Replica Sets (Intro)

Replica Sets
# Initialize replica set
mongod --replSet "rs0" --port 27017

# Connect to primary and initiate
mongosh
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})

# Start secondary nodes
mongod --replSet "rs0" --port 27018 --dbpath /data/db1
mongod --replSet "rs0" --port 27019 --dbpath /data/db2

# Check replica set status
rs.status()

# Check replica set configuration
rs.conf()

# Add member to replica set
rs.add("localhost:27020")

# Remove member from replica set
rs.remove("localhost:27020")

# Force reconfiguration
rs.reconfig({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019", priority: 0 }
  ]
})

# Connect to replica set
mongosh "mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0"

# Read preferences
// Primary (default)
db.users.find().readPref("primary")

// Secondary
db.users.find().readPref("secondary")

// Nearest
db.users.find().readPref("nearest")

// Primary preferred
db.users.find().readPref("primaryPreferred")

// Secondary preferred
db.users.find().readPref("secondaryPreferred")

Sharding (Intro)

Sharding
# Start config servers
mongod --configsvr --dbpath /data/configdb --port 27019
mongod --configsvr --dbpath /data/configdb2 --port 27020
mongod --configsvr --dbpath /data/configdb3 --port 27021

# Start shard servers
mongod --shardsvr --dbpath /data/shard1 --port 27017
mongod --shardsvr --dbpath /data/shard2 --port 27018

# Start mongos (query router)
mongos --configdb configReplSet/localhost:27019,localhost:27020,localhost:27021 --port 27016

# Connect to mongos and add shards
mongosh --port 27016
sh.addShard("localhost:27017")
sh.addShard("localhost:27018")

# Enable sharding for database
sh.enableSharding("myapp")

# Enable sharding for collection with shard key
sh.shardCollection("myapp.users", { "userId": 1 })

# Check sharding status
sh.status()

# Check shard distribution
db.users.getShardDistribution()

# Shard key considerations
// Good shard keys
{ "userId": 1 }           // High cardinality
{ "country": 1, "userId": 1 }  // Compound key

// Bad shard keys
{ "age": 1 }              // Low cardinality
{ "timestamp": 1 }        // Monotonic (all writes to one shard)

# Balancer operations
sh.startBalancer()
sh.stopBalancer()
sh.isBalancerRunning()

# Move chunks manually
sh.moveChunk("myapp.users", { "userId": 1000 }, "shard1")

Performance Tuning & Query Optimization

Performance
// Query analysis with explain()
db.users.find({ age: { $gt: 25 } }).explain("executionStats")

// Analyze query performance
db.users.find({ email: "john@example.com" }).explain("allPlansExecution")

// Check if index is used
db.users.find({ age: { $gt: 25 } }).explain("queryPlanner")

// Database profiler
// Enable profiler
db.setProfilingLevel(2)  // Log all operations

// Set profiler level
db.setProfilingLevel(1, { slowms: 100 })  // Log slow operations (>100ms)

// Check profiler status
db.getProfilingStatus()

// View profiler results
db.system.profile.find().sort({ ts: -1 }).limit(10)

// Disable profiler
db.setProfilingLevel(0)

// Index usage statistics
db.users.aggregate([
  { $indexStats: {} }
])

// Collection statistics
db.users.stats()

// Database statistics
db.stats()

// Performance optimization tips
// 1. Use proper indexes
db.users.createIndex({ email: 1 })
db.users.createIndex({ age: 1, city: 1 })

// 2. Use projection to limit fields
db.users.find({}, { name: 1, email: 1, _id: 0 })

// 3. Use limit and skip efficiently
db.users.find().sort({ createdAt: -1 }).limit(10)

// 4. Use covered queries (index contains all fields)
db.users.find({ age: { $gt: 25 } }, { age: 1, _id: 0 })

// 5. Use aggregation for complex queries
db.users.aggregate([
  { $match: { age: { $gt: 25 } } },
  { $group: { _id: "$city", count: { $sum: 1 } } }
])

// 6. Monitor slow queries
db.system.profile.find({ millis: { $gt: 100 } }).sort({ ts: -1 })

// 7. Use connection pooling
const client = new MongoClient(uri, {
  maxPoolSize: 10,
  minPoolSize: 5,
  maxIdleTimeMS: 30000
});

Using MongoDB with Mongoose (Node.js ODM)

Mongoose
// Install Mongoose
npm install mongoose

// Connect to MongoDB
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/myapp', {
  useNewUrlParser: true,
  useUnifiedTopology: true
});

// Define Schema
const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
    trim: true
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  age: {
    type: Number,
    min: 0,
    max: 120
  },
  isActive: {
    type: Boolean,
    default: true
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
});

// Create Model
const User = mongoose.model('User', userSchema);

// CRUD Operations
// Create
const user = new User({
  name: 'John Doe',
  email: 'john@example.com',
  age: 30
});
await user.save();

// Or use create
const user = await User.create({
  name: 'Jane Smith',
  email: 'jane@example.com',
  age: 25
});

// Read
const users = await User.find({ age: { $gt: 25 } });
const user = await User.findOne({ email: 'john@example.com' });
const user = await User.findById(userId);

// Update
await User.updateOne(
  { email: 'john@example.com' },
  { $set: { age: 31 } }
);

await User.findByIdAndUpdate(userId, { age: 31 });

// Delete
await User.deleteOne({ email: 'john@example.com' });
await User.findByIdAndDelete(userId);

// Middleware (Hooks)
userSchema.pre('save', function(next) {
  this.updatedAt = new Date();
  next();
});

userSchema.post('save', function(doc) {
  console.log('User saved:', doc.name);
});

// Virtual fields
userSchema.virtual('fullName').get(function() {
  return `${this.firstName} ${this.lastName}`;
});

// Instance methods
userSchema.methods.getAge = function() {
  return new Date().getFullYear() - this.birthYear;
};

// Static methods
userSchema.statics.findByEmail = function(email) {
  return this.findOne({ email: email });
};

// Query helpers
userSchema.query.byAge = function(age) {
  return this.where({ age: age });
};

// Usage
const users = await User.find().byAge(30);

Common MongoDB CLI Commands

CLI
# Start MongoDB server
mongod --dbpath /data/db
mongod --config /etc/mongod.conf

# Connect to MongoDB
mongosh
mongosh "mongodb://localhost:27017"
mongosh "mongodb://username:password@host:port/database"

# Basic shell commands
show dbs                    # List databases
use myapp                  # Switch database
show collections           # List collections
show users                 # List users
show roles                 # List roles
show profile               # Show profiler status

# Database operations
db.dropDatabase()          # Delete current database
db.stats()                 # Database statistics
db.getName()               # Current database name

# Collection operations
db.createCollection("users") # Create collection
db.users.drop()            # Delete collection
db.users.stats()           # Collection statistics
db.users.renameCollection("newusers") # Rename collection

# User management
db.createUser({...})       # Create user
db.dropUser("username")    # Delete user
db.getUsers()              # List users
db.grantRolesToUser("user", [...]) # Grant roles

# Index operations
db.users.createIndex({...}) # Create index
db.users.dropIndex("index_name") # Drop index
db.users.getIndexes()      # List indexes
db.users.reIndex()         # Rebuild indexes

# Backup and restore
mongodump --db myapp --out /backup
mongorestore --db myapp /backup/myapp
mongoexport --db myapp --collection users --out users.json
mongoimport --db myapp --collection users --file users.json

# Monitoring and profiling
db.setProfilingLevel(1, {slowms: 100}) # Enable profiler
db.system.profile.find()   # View profiler results
db.currentOp()             # Current operations
db.killOp(opId)            # Kill operation

# Replica set commands
rs.status()                # Replica set status
rs.conf()                  # Replica set configuration
rs.add("host:port")        # Add member
rs.remove("host:port")     # Remove member
rs.stepDown()              # Step down primary

# Sharding commands
sh.status()                # Sharding status
sh.addShard("host:port")   # Add shard
sh.enableSharding("db")    # Enable sharding
sh.shardCollection("db.collection", {key: 1}) # Shard collection

# Utility commands
db.runCommand({ping: 1})   # Ping server
db.runCommand({serverStatus: 1}) # Server status
db.runCommand({dbStats: 1}) # Database stats
db.runCommand({collStats: "users"}) # Collection stats

# Help and documentation
help                       # General help
db.help()                  # Database help
db.users.help()            # Collection help