mongoui

Solution

Why your MongoDB query is slow, and how to fix it

A slow MongoDB query is almost always one collection scan that one index fixes. Read the plan before you reach for a bigger cluster. Here is how to find the COLLSCAN and the index that turns it into a lookup.

Last reviewed July 4, 2026

A slow MongoDB query is almost always one collection scan that one index fixes. Before you reach for a bigger instance or a shard key, read the plan. Most "MongoDB is slow" tickets are a single query doing a COLLSCAN, examining hundreds of thousands of documents to return a handful. Add the index, and the scaling meeting cancels itself.

Here is how to find that query, confirm the diagnosis, and fix it.

Read the plan with explain()

The plan is not hidden. It is dense. Append explain("executionStats") to the query and MongoDB tells you exactly what it did:

db.orders
  .find({ userId: 123, status: "shipped" })
  .sort({ createdAt: -1 })
  .explain("executionStats")

Two things carry the diagnosis:

executionStats: {
  nReturned: 24,
  totalDocsExamined: 486213,   // read almost half a million
  executionTimeMillis: 812
}
winningPlan: { stage: "COLLSCAN" }   // no index used

A COLLSCAN is your database reading every document to answer a question it could have looked up in an index. It is the group chat of query plans: everyone gets the message, nobody needed to. The tell is the ratio of totalDocsExamined to nReturned. Examining 486,213 documents to return 24 means the query walked the whole collection. You want that ratio close to 1.

The MongoDB explain results reference documents every field, but those three are where the story lives.

Add the index that matches the query

The fix is a compound index that covers the filter and the sort in the right order. The rule is Equality, Sort, Range: fields you match exactly first, the sort field next, ranges last.

db.orders.createIndex({ userId: 1, status: 1, createdAt: -1 })

Run the same explain() again and the plan flips:

winningPlan: { stage: "IXSCAN", indexName: "userId_1_status_1_createdAt_-1" }
executionStats: { nReturned: 24, totalDocsExamined: 24 }

Twenty-four examined to return twenty-four. The page loads before you finish reading the alert. See how to create an index in MongoDB for the full syntax, and MongoDB index optimization for keeping the set lean once you have added a few.

When it is not an index

Not every slow query is a missing index, and guessing is how you end up with six indexes and the same latency. Read the plan first. The plan points at a different problem when:

  • The stage is already IXSCAN but executionTimeMillis is still high. Your working set may be larger than RAM, so the index pages are coming off disk.
  • The query fans out through $lookup into a collection that itself has no index on the join field.
  • The result set is unbounded. A query returning a million documents is slow because it returns a million documents, and no index changes that. Add a limit, or paginate.

The mechanism is always the same: the plan shows the stage, the stage names the problem. You do not have to memorize it, but you do have to look.

Or read the plan in plain English

mongoui's Query Optimizer reads the same explain() output and reports it for you: the winning plan, whether the stage is a scan or a lookup, documents examined versus returned, and the exact index that fixes a slow query. The reason developers avoid query plans is not that the data is missing, it is that the output is dense. mongoui translates it, then points at the index.

Smart Scan and Local narration run on your machine, so full documents and MongoDB credentials stay local. If you explicitly switch Optimize to AI narration, the authored query plus bounded field, index, and plan context go directly to your selected provider. Read the plan first, add the index, and let the 2am COLLSCAN stay a story you tell, not one you live.

Frequently asked questions

Why is my MongoDB query slow?

Most often, a missing index. The query does a COLLSCAN, reading every document in the collection to answer a filter that an index could have looked up directly. Run explain("executionStats") and compare totalDocsExamined to nReturned: a large gap is the tell.

How do I use explain() in MongoDB?

Append .explain("executionStats") to the query, for example db.orders.find({ userId: 123 }).explain("executionStats"). Read winningPlan.stage for COLLSCAN versus IXSCAN, and executionStats for documents examined versus returned. mongoui reads the same output and reports it in plain English.

What is a COLLSCAN in MongoDB?

A collection scan: MongoDB reads every document in the collection because no index covers the query. It is the query plan equivalent of reading the whole book to find one sentence. A supporting index turns it into an IXSCAN.

Is every slow MongoDB query fixed by an index?

No. Read the plan before you guess. Fan-out $lookup, unbounded result sets, and a working set larger than RAM are real and different problems. An index fixes the common case; the plan tells you which case you have.