Home/Blog/software engineering
Back to all articles
software engineering#MongoDB#Database Optimization#Node.js

MongoDB Performance Optimization: Indexes, ESR Rule & Aggregation Pipelines in Node.js

RB
Rajput BhavinArchitect
Founder, Rajput Bhavin Engineering
24 August 2026•11 min read

Quick Answer: Optimizing MongoDB Performance#

⚡ Quick Answer / Executive Summary
Optimizing MongoDB in Node.js applications requires adhering to the ESR (Equality, Sort, Range) rule for compound indexes, filtering documents early in aggregation pipelines using $match and $project, preventing unbounded document growth through the Subset and Bucket design patterns, and configuring Mongoose connection pool sizing (maxPoolSize: 50) to eliminate socket connection overhead.

4 Common MongoDB Bottlenecks in Production#

Without rigorous database engineering, MongoDB applications experience severe latency degradation as collections scale past 100,000 documents:

  1. ✓Full Collection Scans (COLLSCAN): Missing indexes force the database engine to scan every document on disk, maxing out CPU and I/O.
  2. ✓In-Memory Sort Spills: Queries sorting unindexed fields exceed MongoDB’s default 100MB RAM sort limit, causing queries to fail or spill to slow disk storage.
  3. ✓Unbounded Array Growth: Embedding thousands of comments, transactions, or logs inside a single document, eventually hitting MongoDB's strict 16MB BSON document limit.
  4. ✓N+1 $lookup Pipeline Joins: Executing unindexed multi-collection joins in aggregations, turning a 5ms query into a 3-second database lock.

1. Mastering the ESR Rule for Compound Indexes#

When creating compound indexes to support multi-field queries, always order index keys following the ESR Rule:

Raw Output
Production Code
1. Equality Fields   ──> Exact match filters (e.g. status: "active", orgId: "org_123")
2. Sort Fields       ──> Order by fields (e.g. createdAt: -1)
3. Range Fields      ──> Inequality filters (e.g. price: { $gt: 100 }, date: { $lte: ... })

#Production Example:

JavaScript
Production Code
// Query:
db.orders.find({
  orgId: "org_9823",      // Equality
  createdAt: { $gte: ISODate("2026-01-01") } // Range
}).sort({ totalAmount: -1 }); // Sort

// Optimal ESR Index:
db.orders.createIndex({
  orgId: 1,         // 1. Equality First
  totalAmount: -1,  // 2. Sort Second
  createdAt: 1      // 3. Range Last
});

Using explain('executionStats'), this index guarantees totalDocsExamined matches nReturned exactly (index-covered scan with zero in-memory sort).


2. High-Performance Aggregation Pipelines#

Aggregation pipelines run heavy analytical queries on the database. Structure your pipeline stages for maximum throughput:

JavaScript
Production Code
db.transactions.aggregate([
  // Stage 1: Filter immediately using indexed fields ($match)
  {
    $match: {
      tenantId: "tenant_445",
      status: "COMPLETED",
      createdAt: { $gte: ISODate("2026-01-01") }
    }
  },
  
  // Stage 2: Project ONLY fields required for downstream stages (Reduces RAM footprint)
  {
    $project: {
      customerId: 1,
      amount: 1,
      category: 1
    }
  },
  
  // Stage 3: Group & Aggregate
  {
    $group: {
      _id: "$category",
      totalRevenue: { $sum: "$amount" },
      transactionCount: { $sum: 1 }
    }
  },
  
  // Stage 4: Sort and Limit final output
  { $sort: { totalRevenue: -1 } },
  { $limit: 10 }
]);

3. Schema Design Patterns: Subset & Bucket Pattern#

Instead of choosing between pure embedding (which risks hitting 16MB limits) or pure referencing (which requires slow joins), use specialized design patterns:

#The Subset Pattern (For High-Traffic Product / Feed Apps)

Keep the 10 most recent reviews embedded in the main `Product` document for sub-millisecond initial page renders. Move the remaining historical reviews to a separate `reviews` collection queried only when the user clicks "View All".
TypeScript
Production Code
// Product Document (Embedded Subset)
{
  _id: ObjectId("..."),
  name: "Enterprise Cloud Gateway",
  price: 499,
  rating: 4.8,
  recentReviews: [ // Top 5 reviews only for fast product page render
    { reviewer: "Alex", rating: 5, comment: "Exceptional performance." }
  ],
  totalReviewsCount: 1420
}

4. Production Mongoose Connection Pooling in Node.js#

TypeScript
Production Code
import mongoose from 'mongoose';

export async function connectDatabase() {
  await mongoose.connect(process.env.MONGODB_URI!, {
    maxPoolSize: 50, // Maintain up to 50 socket connections
    minPoolSize: 10,  // Keep 10 warm sockets ready
    serverSelectionTimeoutMS: 5000,
    socketTimeoutMS: 45000,
    family: 4, // Use IPv4 to avoid DNS lookup delays
  });

  mongoose.connection.on('error', (err) => {
    console.error('MongoDB Connection Error:', err);
  });
}

High-Performance Database Engineering with Rajput Bhavin#

Need help diagnosing database bottlenecks, refactoring unindexed queries, or scaling your data infrastructure?

Software Engineering Consultation

Need help architecting your modern software stack?

Rajput Bhavin Engineering provides hands-on systems architecture, codebase reviews, and custom cloud software development for technology teams in India and worldwide.

RB

Rajput Bhavin

Systems Architect

Founder and Systems Architect at Rajput Bhavin Engineering in Gota, Ahmedabad. Specializes in high-concurrency cloud software, Next.js web applications, multi-tenant SaaS architecture, and autonomous AI systems.

Connect Now