MongoDB
04 / 04

Aggregation & Indexes

Aggregation & Indexes

Aggregation Pipeline

Stages are processed in order, each stage's output is the next stage's input.

const pipeline = [
  // $match — filter (like WHERE, use early to limit docs)
  { $match: { active: true, age: { $gte: 18 } } },

  // $group — group and aggregate (like GROUP BY)
  {
    $group: {
      _id: '$country',
      count: { $sum: 1 },
      avgAge: { $avg: '$age' },
      emails: { $push: '$email' },
      minAge: { $min: '$age' },
      maxAge: { $max: '$age' },
    }
  },

  // $sort — sort results
  { $sort: { count: -1 } },

  // $limit and $skip — pagination
  { $skip: 0 },
  { $limit: 10 },

  // $project — reshape documents
  {
    $project: {
      country: '$_id',
      count: 1,
      avgAge: { $round: ['$avgAge', 1] },
      _id: 0,
    }
  },
];

const results = await users.aggregate(pipeline).toArray();

// $lookup — JOIN another collection
const ordersWithUsers = await orders.aggregate([
  {
    $lookup: {
      from: 'users',
      localField: 'userId',
      foreignField: '_id',
      as: 'user',
    }
  },
  { $unwind: '$user' },            // flatten array to single doc
  { $project: { total: 1, 'user.name': 1, 'user.email': 1 } },
]).toArray();

// $unwind — flatten array field
await posts.aggregate([
  { $unwind: '$tags' },            // one doc per tag
  { $group: { _id: '$tags', count: { $sum: 1 } } },
  { $sort: { count: -1 } },
]).toArray();

// $addFields / $set
await users.aggregate([
  { $addFields: { fullName: { $concat: ['$firstName', ' ', '$lastName'] } } },
]).toArray();

// $facet — multiple pipelines in one pass
await products.aggregate([
  {
    $facet: {
      totalCount: [{ $count: 'count' }],
      byCategory: [{ $group: { _id: '$category', count: { $sum: 1 } } }],
      priceStats: [{ $group: { _id: null, avg: { $avg: '$price' }, min: { $min: '$price' } } }],
    }
  }
]).toArray();

Indexes

// Single field
await users.createIndex({ email: 1 });           // ascending
await users.createIndex({ createdAt: -1 });      // descending (for latest first)

// Unique index
await users.createIndex({ email: 1 }, { unique: true });

// Compound index (order matters — use ESR rule: Equality, Sort, Range)
await users.createIndex({ country: 1, createdAt: -1 });

// Partial index — index only matching documents
await users.createIndex(
  { email: 1 },
  { partialFilterExpression: { active: true } }
);

// Sparse index — skip documents where field doesn't exist
await users.createIndex({ phone: 1 }, { sparse: true });

// TTL index — auto-delete documents after N seconds
await sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 86400 }); // 24h

// Text index for full-text search
await articles.createIndex({ title: 'text', body: 'text' });
await articles.find({ $text: { $search: 'mongodb performance' } }).toArray();

// Wildcard index
await products.createIndex({ 'attributes.$**': 1 });

// List indexes
await users.listIndexes().toArray();

// Drop index
await users.dropIndex({ email: 1 });

// Explain
await users.find({ email: 'a@b.com' }).explain('executionStats');

Mongoose (ODM)

import mongoose, { Schema, model, Document } from 'mongoose';

// Schema definition
const userSchema = new Schema({
  email: { type: String, required: true, unique: true, lowercase: true, trim: true },
  name: { type: String, required: true, minlength: 2 },
  age: { type: Number, min: 0, max: 150 },
  roles: [{ type: String, enum: ['user', 'admin', 'mod'] }],
  address: {
    city: String,
    country: { type: String, default: 'US' },
  },
}, {
  timestamps: true,            // adds createdAt, updatedAt
  toJSON: { virtuals: true },
});

// Virtual field
userSchema.virtual('isAdult').get(function() {
  return this.age >= 18;
});

// Methods
userSchema.methods.greet = function() {
  return `Hello, ${this.name}`;
};

// Statics
userSchema.statics.findByEmail = function(email: string) {
  return this.findOne({ email: email.toLowerCase() });
};

// Pre-save hook
userSchema.pre('save', async function(next) {
  if (this.isModified('password')) {
    this.password = await bcrypt.hash(this.password, 10);
  }
  next();
});

const User = model('User', userSchema);

// CRUD with Mongoose
const user = await User.create({ email: 'a@b.com', name: 'Alice' });
const users = await User.find({ active: true }).select('name email').lean();
await User.findByIdAndUpdate(id, { $inc: { loginCount: 1 } }, { new: true });
await User.findByIdAndDelete(id);

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free