mongoui
Indexes·June 28, 2026·6 min read

MongoDB compound indexes: the field order that makes or breaks them

A compound index covers more than one field, and the order of those fields decides whether it works. The rule is Equality, Sort, Range. Get it right and one index replaces three, get it wrong and MongoDB ignores it.

Arctic Glass illustration for MongoDB compound indexes: the field order that makes or breaks them

A compound index indexes more than one field at once, and the order you list those fields in is the whole game. Same fields, different order, completely different index. Get the order right and a single compound index serves a filter and a sort in one pass. Get it wrong and MongoDB quietly ignores the index and scans anyway, which is the exact outcome you built the index to avoid.

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

Here is how to order the fields, and why the order has the power it does.

The rule: Equality, Sort, Range

The field order that works follows one rule, Equality, Sort, Range:

  • Equality first: fields you match on an exact value (userId: 123, status: "shipped").
  • Sort next: the field you order results by (createdAt).
  • Range last: fields you filter with a range ($gt, $lt, $in, $gte).

So a query like this:

db.orders
  .find({ userId: 123, status: "shipped", total: { $gt: 5000 } })
  .sort({ createdAt: -1 })

wants this index:

db.orders.createIndex({ userId: 1, status: 1, createdAt: -1, total: 1 })
//                       equality    equality    sort         range

With the fields in that order, MongoDB walks the index straight to the matching rows, in the sort order you asked for, and never loads documents into memory to sort them. Put total (a range) before createdAt (the sort) and it cannot do that, because a range scan scatters the sort order. The MongoDB manual calls this the ESR guideline, and it is the single most useful thing to know about compound indexes.

The prefix rule: why one index does the work of three

A compound index also serves queries on its leading fields, its prefixes. An index on { userId: 1, status: 1, createdAt: -1 } supports:

  • queries on userId alone
  • queries on userId and status
  • queries on all three

but not on status alone, or createdAt alone. The leading field has to be involved. This is why you can often delete a pile of single-field indexes: one compound index on { a, b, c } covers { a } and { a, b } for free. Three indexes become one, with the same coverage, less write cost, and less storage. Finding those redundant single-field indexes is part of index optimization.

Sort direction matters here (and only here)

On a single-field index, direction barely matters, MongoDB can read it either way. On a compound index it matters, because the directions have to line up with a multi-field sort. An index on { status: 1, createdAt: -1 } serves sort({ status: 1, createdAt: -1 }) and its exact reverse, but not sort({ status: 1, createdAt: 1 }). If you sort two fields in opposite directions, the index directions have to match.

Confirm it worked

Built the index, still slow? Check the plan:

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

An IXSCAN with totalDocsExamined close to nReturned means the compound index is doing its job. A COLLSCAN, or an IXSCAN followed by an in-memory SORT stage, means the field order is off. Reading that plan is covered in why your MongoDB query is slow, and the basic index syntax is in how to create an index.

mongoui's Query Optimizer reads that explain() output and tells you, in plain English, whether your compound index matched and what order it should be in if it did not. Its Index view shows the prefix overlaps, so the three-single-indexes-become-one cleanup is obvious. Local narration makes no provider call. Explicit AI narration sends the authored query and bounded structural plan context directly to your selected provider; full documents and MongoDB credentials stay local. Order the fields Equality, Sort, Range, and let one index cover what three used to.

Frequently asked questions

What is a compound index in MongoDB?

An index on two or more fields, for example db.orders.createIndex({ userId: 1, status: 1, createdAt: -1 }). It supports queries that filter and sort across those fields together, and its field order determines which queries it can serve efficiently.

What is the ESR rule for MongoDB compound indexes?

Equality, Sort, Range. Put fields you match exactly first, the field you sort on next, and range filters ($gt, $lt, $in) last. Following that order lets one index satisfy the filter and the sort without an in-memory sort.

Does field order matter in a MongoDB compound index?

Yes, completely. { userId: 1, status: 1 } and { status: 1, userId: 1 } are different indexes with different capabilities. Because of the prefix rule, a compound index can serve queries on its leading fields but not on a trailing field alone.

Can one compound index replace several single-field indexes?

Often, yes. A compound index on { a: 1, b: 1, c: 1 } also serves queries on { a } and { a, b } through the prefix rule. Replacing three single-field indexes with one well-ordered compound index keeps the same query coverage while cutting write cost and storage.