MongoDB $indexStats Stage

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

What Is $indexStats?

$indexStats is a MongoDB aggregation stage that reports how often each index is used on a collection—so you can spot unused indexes and confirm your query indexes are actually hit.

Remember
$indexStats = "which indexes are getting used?"

db.products.aggregate([
  { $indexStats: {} }
])

must be the FIRST stage
one output document per index
access.ops → usage counter
access.since → when counting started
name / key → which index

$collStats → size on disk
$indexStats → usage over time
UsePrefer
Per-index usage counters$indexStats
Collection / index disk size$collStats
Why a query is slowexplain() + $indexStats

How to Use $indexStats in MongoDB

Put it first, then sort or filter the stats documents.

  1. Run on the collection you want to audit (e.g. products).
  2. Start with { $indexStats: {} } (no options).
  3. Optional: $sort by access.ops, or $match zero-ops indexes.
  4. Optional: $project a clean report shape.
FieldMeaning
nameIndex name (_id_, sku_1, …)
keyKey pattern (e.g. { category: 1 })
access.opsHow many times the index was used
access.sinceWhen the counter started
specFull index specification

Pseudocode

Pseudocode
assert: $indexStats is stage 1

for each index on this collection:
  emit {
    name,
    key,
    host,
    access: { ops, since },
    spec
  }

// later stages may filter / sort those stats docs
// (they do not re-scan product documents)

Cheat Sheet

Keep these facts handy during index audits.

TopicDetail
TypeAggregation stage (diagnostics)
PositionMust be first
OptionsEmpty object only: {}
OutputOne document per index
Hot metricaccess.ops
Unused hintaccess.ops: 0 (review, don’t auto-drop)
Not in $facetRun as a top-level first stage

Live Preview

Rank or filter a sample index-usage snapshot (teaching model—not live server counters).

Sample counters after a few sku/category/price queries.

Result
_id_                   ops: 12
sku_1                  ops: 3
category_1             ops: 2
price_-1               ops: 2
inStock_1_category_1   ops: 0

Worked Walkthrough — Rank by Ops

After creating indexes and running a few finds, ask: which indexes were used most?

Calculation
1. $indexStats {}          ← must be first
2. Read access.ops per index
3. $sort { "access.ops": -1 }
4. $project name + ops

Example ranking idea:
  _id_     → often high (internal / _id lookups)
  sku_1    → used by find({ sku })
  category → used by category queries
  inStock… → 0 if never queried → review candidate

MongoDB Examples

We use a small products collection with several indexes. Run a few queries first so access.ops is non-zero, then inspect and rank usage in mongosh.

Example 1 — Sample Data

Five products, four user indexes (plus default _id_), then a few queries to generate usage.

mongosh
db.products.drop()
db.products.insertMany([
  { sku: "A100", name: "Wireless Mouse", category: "Electronics", price: 29,  inStock: true },
  { sku: "B200", name: "USB Keyboard",   category: "Electronics", price: 49,  inStock: true },
  { sku: "C300", name: "Office Chair",   category: "Furniture",   price: 199, inStock: false },
  { sku: "D400", name: "Desk Lamp",      category: "Furniture",   price: 35,  inStock: true },
  { sku: "E500", name: "Monitor Stand",  category: "Electronics", price: 59,  inStock: true }
])

db.products.createIndex({ sku: 1 }, { unique: true })
db.products.createIndex({ category: 1 })
db.products.createIndex({ price: -1 })
db.products.createIndex({ inStock: 1, category: 1 })  // rarely queried

// Generate some index usage
db.products.find({ sku: "A100" }).toArray()
db.products.find({ category: "Electronics" }).sort({ price: -1 }).toArray()
db.products.find({ price: { $gte: 50 } }).toArray()

How the Data Is Stored in MongoDB

After setup, list indexes with getIndexes():

mongosh
db.products.getIndexes()

Read the important pieces like this:

PieceExamplePlain English
sku indexuniqueHit by exact sku lookups
category / pricesingle-fieldUsed by sample finds
inStock + categorycompoundMay stay at 0 ops if unused

How It Works

  1. Create indexes. Four user indexes plus default _id_.
  2. Run queries. Those finds bump access.ops on indexes they use.
  3. Leave one idle. The inStock compound is never queried on purpose.

Example 2 — Basic Stats

Goal: list every index with its current usage counters.

mongosh
db.products.aggregate([
  { $indexStats: {} }
])

How It Works

  1. First stage only. $indexStats must lead the pipeline.
  2. Metadata, not rows. It reads index stats—not product documents.
  3. One doc per index. Including _id_.
  4. Read access.ops. Higher means more usage since tracking began.

Example 3 — Rank & Unused

Goal: sort indexes by usage, then list candidates with access.ops: 0 (excluding _id_).

mongosh
// Most used first
db.products.aggregate([
  { $indexStats: {} },
  { $sort: { "access.ops": -1 } },
  {
    $project: {
      indexName: "$name",
      keyPattern: "$key",
      ops: "$access.ops",
      trackingSince: "$access.since",
      _id: 0
    }
  }
])

// Potentially unused (review — do not auto-drop)
db.products.aggregate([
  { $indexStats: {} },
  {
    $match: {
      "access.ops": 0,
      name: { $ne: "_id_" }
    }
  },
  {
    $project: {
      indexName: "$name",
      keyPattern: "$key",
      ops: "$access.ops",
      since: "$access.since",
      _id: 0
    }
  }
])

How It Works

  1. Sort after stats. $sort ranks the stats documents, not products.
  2. Zero-ops filter. $match finds indexes that never counted a use.
  3. Keep _id_. Exclude it from unused lists—you need it.
  4. Review, then drop. Zero ops is a hint, not a delete command.

Edge Cases & Pitfalls

Check these before dropping indexes based on $indexStats.

order

Must be the first stage

Put $match / $sort after $indexStats, never before it.

reset

Counters reset on restart / new index

Low ops after a mongod restart or a brand-new index can look like “unused.” Check access.since.

replica

Stats are per node

An index may be hot on a secondary that serves reads. Audit the members that take traffic.

facet

Not allowed inside $facet

Run $indexStats as its own first-stage pipeline on the collection.

Performance Notes

ApproachCostBest for
$indexStatsLight metadata readIndex audits
$indexStats + $match / $sortTiny result setUnused / hot lists
$collStats + $indexStatsTwo diagnostic pipelinesSize + usage decisions

$indexStats is cheap compared to scanning documents. Pair it with $collStats when a low-usage index is also large on disk.

Key Takeaways

  • Role: per-index usage counters for a collection.
  • Metric: access.ops (+ access.since).
  • Rule: must be the first stage.
  • Unused: review zero ops—don’t drop blindly.

One line: $indexStats answers “which indexes are actually being used?”

Frequently Asked Questions

$indexStats returns usage statistics for every index on the collection—one document per index. Each result includes the index name, key pattern, host, and access.ops (how many times the index was used since tracking started).
It must be first: db.products.aggregate([{ $indexStats: {} }]). Stages before it cause an error. You can add $match, $sort, or $project after it.
access.ops counts operations that used the index since the mongod process started or since the index was created (whichever is more recent). access.since shows when counting began.
Often a candidate for review—but not always. A new index, a recent restart, or usage on another replica set member can show low or zero ops. Confirm with query patterns and explain() before dropping.
$collStats reports storage size and index sizes. $indexStats reports per-index usage (access.ops). Use both: disk footprint vs which indexes queries actually hit.
No. Like $collStats and $geoNear, $indexStats cannot appear inside $facet sub-pipelines. Run it as the first stage on the target collection.

Next: $limit

Cap how many documents continue through the pipeline—great after $sort.

$limit tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful