Docs Menu
Docs Home
/
MongoDB Manual
/ /

The ESR (Equality, Sort, Range) Rule

On this page

  • Equality
  • Sort
  • Range
  • Additional Considerations
  • Example
  • Further Discussion

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" 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" 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" 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().

  • Inequality operators such as $ne or $nin are range operators, not equality operators.

  • $regex is a range operator.

  • $in:

    • 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.

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 match

  • cost: { $gt: 15000 } is a range based match, and

  • model is used for sorting

Following the ESR rule, the optimal index for the example query is:

{ manufacturer: 1, model: 1, cost: 1 }

A number of MongoDB conference presentations discuss the ESR rule in depth.

Back

Strategies