The ESR (Equality, Sort, Range) Rule
An index that references multiple fields is a compound index. Compound indexes can dramatically improve query response times.
Index keys correspond to document fields. In most cases, applying the ESR (Equality, Sort, Range) Rule to arrange the index keys helps to create a more efficient compound index.
This page introduces the ESR Rule. For more information on optimizing
queries, see explain
and
Query Plans.
Tip
To force MongoDB to use a particular index, use cursor.hint() when testing indexes.
Equality
"Equality" refers to an exact match on a single value. The following
exact match queries scan the cars
collection for documents whose
model
field exactly matches Cordoba
.
db.cars.find( { model: "Cordoba" } ) db.cars.find( { model: { $eq: "Cordoba" } } )
Index searches make efficient use of exact matches to limit the number of documents that need to be examined to complete a query. Put the fields that require exact matches first in your index.
An index may have multiple keys for queries with exact matches. The index keys for equality matches can appear in any order. However, to satisfy an equality match with the index, all of the index keys for exact matches must come before any other index fields. MongoDB's search algorithm eliminates any need to arrange the exact match fields in a particular order.
Exact matches should be selective. To reduce the number of index keys scanned, ensure equality tests eliminate at least 90% of possible document matches.
Sort
"Sort" determines the order for results. Sort follows equality matches because the equality matches reduce the number of documents that need to be sorted. Sorting after the equality matches also allows MongoDB to do a non-blocking sort.
An index can support sort operations when the query fields are a subset of the index keys. Sort operations on a subset of the index keys are only supported if the query includes equality conditions for all of the prefix keys that precede the sort keys. For more information, see Sort and Non-prefix Subset of an Index.
The following example queries the cars
collection. The output is
sorted by model
:
db.cars.find( { manufacturer: "GM" } ).sort( { model: 1 } )
To improve query performance, create an index on the manufacturer
and model
fields:
db.cars.createIndex( { manufacturer: 1, model: 1 } )
manufacturer
is the first key because it is an equality match.model
is indexed in the same order (1
) as the query.
Range
"Range" filters scan fields. The scan doesn't require an exact match, which means range filters are loosely bound to index keys. To improve query efficiency, limit the range bounds and use equality matches to reduce the number of documents to scan.
Range filters resemble the following:
db.cars.find( { price: { $gte: 15000} } ) db.cars.find( { age: { $lt: 10 } } ) db.cars.find( { priorAccidents: { $ne: null } } )
MongoDB cannot perform an index sort on the results of a range filter.
Place the range filter after the sort predicate so MongoDB can use a
non-blocking index sort. For more information on blocking sorts, see
cursor.allowDiskUse()
.
Additional Considerations
Inequality operators such as
$ne
or$nin
are range operators, not equality operators.$regex
is a range operator.When
$in
is used alone, it is an equality operator that performs a series of equality matches.When
$in
is used with.sort()
:If
$in
has less than 200 array elements, the elements are expanded and then merged in the sort order specified for the index. This improves performance for small arrays.$in
is similar to an equality predicate with ESR.If
$in
has 200 elements or more, the elements are ordered like a range operator. In this scenario, the performance improvement for small arrays isn't realized. It isn't possible for the subsequent fields in the index to provide a sort, and$in
is similar to a range predicate with ESR.If you typically use
$ins
with small arrays, include$ins
earlier in the index specification. If you typically use large arrays, include$ins
where you would include a range predicate.
Note
The 200 limit is subject to change and is not guaranteed to stay the same for all MongoDB versions.
Example
The following query searches the cars
collection for vehicles
manufactured by Ford that cost more than $15,000 dollars. The results
are sorted by model:
db.cars.find( { manufacturer: 'Ford', cost: { $gt: 15000 } } ).sort( { model: 1 } )
The query contains all the elements of the ESR Rule:
manufacturer: 'Ford'
is an equality based matchcost: { $gt: 15000 }
is a range based match, andmodel
is used for sorting
Following the ESR rule, the optimal index for the example query is:
{ manufacturer: 1, model: 1, cost: 1 }
Further Discussion
A number of MongoDB conference presentations discuss the ESR rule in depth.