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
IXSCANbutexecutionTimeMillisis still high. Your working set may be larger than RAM, so the index pages are coming off disk. - The query fans out through
$lookupinto 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.