Covered queries in MongoDB
By Marios Pavlidis · Updated October 2026 · Applies to MongoDB 5.0–8.0
A covered query is one that MongoDB can satisfy entirely from the index — it never loads a full document from the collection. The signal is totalDocsExamined: 0 in the execution stats and no FETCH stage in the plan tree. This is the most efficient an IXSCAN can be.
Coverage requirements
Three conditions must all hold for a query to be covered:
- 1.Every field in the query predicate (WHERE / $match) must be in the index.
- 2.Every field in the projection must be in the index.
- 3._id must either be explicitly excluded from the projection ({ _id: 0 }) or included in the index.
MongoDB includes _id in every projection by default. Forgetting to exclude it is the most common reason a query is not covered.
Worked example (illustrative)
Numbers below are synthetic. The collection is users; we want the email addresses of active users.
// Index definition
db.users.createIndex({ status: 1, email: 1 })
// Query — both predicate and projection fields are in the index.
// _id is explicitly excluded.
db.users.find(
{ status: "active" },
{ email: 1, _id: 0 }
).explain("executionStats"){
"queryPlanner": {
"winningPlan": {
"stage": "PROJECTION_COVERED", // no FETCH
"inputStage": {
"stage": "IXSCAN",
"keyPattern": { "status": 1, "email": 1 },
"indexName": "status_1_email_1",
"indexBounds": {
"status": ["[\"active\", \"active\"]"],
"email": ["[MinKey, MaxKey]"]
}
}
}
},
"executionStats": {
"nReturned": 3840,
"executionTimeMillis": 6,
"totalKeysExamined": 3840,
"totalDocsExamined": 0 // no documents loaded
}
}Interpretation: PROJECTION_COVERED replaces the FETCH stage. MongoDB read 3,840 index entries and returned 3,840 results without loading a single document. The ratio of examined keys to returned results is still 1:1.
What breaks coverage
Forgetting to exclude _id
// NOT covered — _id is returned by default and is not in the index
db.users.find({ status: "active" }, { email: 1 })MongoDB must fetch the document to retrieve _id. Add _id: 0 to the projection or include _id in the index.
Projecting a field not in the index
// NOT covered — 'name' is not in the index
db.users.find({ status: "active" }, { email: 1, name: 1, _id: 0 })MongoDB must fetch the document to retrieve name. Either add name to the index or remove it from the projection.
Array fields
Queries on fields that contain arrays are not covered — MongoDB must access the document to handle multi-key index entries correctly. The planner will add a FETCH stage even if all other conditions are met.
Embedded documents in the query
Querying on a nested path (e.g. address.city) can be covered if the full dotted path is in the index, but matching on the entire embedded document object (e.g. { address: { city: "NY" } }) is not covered.
When coverage is worth pursuing
Covered queries eliminate random I/O to the collection entirely. They are most valuable when:
- The query runs at high frequency (hot path, reporting loop)
- The collection is large and WiredTiger cache pressure is high
- The projected fields are few and naturally belong in the index (e.g. a lookup table returning IDs and status)
Adding fields to an index for coverage increases index size and write amplification. For low-frequency queries or small collections, a non-covered IXSCAN with a low examination ratio is usually sufficient.