Query optimization reduces the amount of data a query must process. Use indexes, projections, and query limits to improve performance and reduce resource consumption. Review query performance periodically as collections grow to determine when to scale.
Create Indexes to Support Queries
Indexes store field values in a separate data structure. In read operations, MongoDB searches the index instead of scanning the entire collection. In write operations, MongoDB updates both the collection and the index.
Create indexes for your most common queries. If a query searches multiple fields, create a compound index.
For example, consider the following query on the rated field in the
movies collection:
let ratingValue = <someUserInput>; db.movies.find( { rated: ratingValue } );
To improve performance for this query, add an index to the movies
collection on the rated field. [1] In
mongosh, create indexes using the
db.collection.createIndex() method:
db.movies.createIndex( { rated: 1 } )
To analyze query performance, see Interpret Explain Plan Results.
| [1] | For single-field indexes, the order of the index does not matter. For compound indexes, the field order impacts what queries the index supports. For details, see Compound Index Sort Order. |
Create Selective Queries
Query selectivity measures how well a query predicate filters documents and determines whether queries can use indexes effectively.
Highly selective queries match fewer documents and use indexes more effectively. For instance, an equality match on
_idis highly selective as it can match at most one document.Less selective queries match more documents and use indexes less efficiently.
For instance, the inequality operators $nin and
$ne are not very selective since they often match a large
portion of the index. As a result, in many cases, a $nin or
$ne query with an index may perform no better than a
$nin or $ne query that must scan all documents in
a collection.
The selectivity of a regular expression depends on the
expression itself. For details, see regular expression and index
use.
Project Only Necessary Data
When you need a subset of fields from documents, you can improve performance by returning only the fields you need. Projections reduce network traffic and processing time.
For example, if your query to the movies collection needs only the
year, title, directors, and plot fields, specify
those fields in the projection:
db.movies.find( {}, { year: 1, title: 1, directors: 1 } ).sort( { year: -1 } ).limit(3)
When you use a $project aggregation stage it should typically be the last stage in
your pipeline, used to specify which fields to return to the client.
Using a $project stage at the beginning or middle of a pipeline to
reduce the number of fields passed to subsequent pipeline stages is
unlikely to improve performance, because the database performs this
optimization automatically.
For more information, see Project Fields to Return from Query.
Example
To achieve a covered query, index the projected fields. The ESR (Equality, Sort, Range) rule applies to the order of fields in the index.
For example, consider the following index on a movies collection:
db.movies.createIndex( { rated: 1, _id: 1, "imdb.rating": 1, title: 1, released: 1 } )
The preceding index, while technically correct, isn't structured to optimize query performance.
The following query uses the ESR (Equality, Sort, Range) rule to structure a more efficient aggregation pipeline and improve query response times.
db.movies.aggregate( [ { $match: { rated: "PG", released: { $gt: ISODate("2000-01-01T00:00:00Z") } } }, { $sort: { title: 1 } }, { $limit: 5 }, { $project: { _id: 1, "imdb.rating": 1 } } ] )
The index and query follow the ESR rule:
ratedis used for an equality match (E), so it is the first field in the index.titleis used for sorting (S), so it is afterratedin the index.releasedis used for a range query (R), so it is the last field in the index.
Limit Query Results
MongoDB cursors return results in batches. If you know
how many results you need, pass that value to the limit()
method to reduce network resource usage.
Sort results before applying a limit to ensure that the query returns
the expected documents. For example, if you need only 10 results from
your query to the movies collection, run the following query:
db.movies.find( {}, { title: 1, year: 1 } ).sort( { year: -1 } ).limit(10)
For more information, see limit().
Use Index Hints
The query optimizer
typically selects the optimal index for a specific operation. However,
you can force MongoDB to use a specific index using the
hint() method. Use hint() to support
performance testing or when you are querying a field that appears in
several indexes to ensure that MongoDB uses the correct index.
Use Server-Side Operations
Use the $inc operator to increment or decrement
values in documents. The operator increments the value of the field on
the server side, as an alternative to selecting a document, making
changes in the client code, and then writing the entire
document to the server. The $inc operator can also help
avoid race conditions that occur when two application instances query
for a document, manually increment a field, and save the entire
document back at the same time.
Run Covered Queries
A covered query is a query than can be satisfied entirely by an index without having to examine any documents. An index covers a query when all of the following are true:
All the fields in the query (including fields specified by the application and any fields needed internally, such as for sharding) are part of the index.
All the fields returned in the results are in the same index.
No fields in the query are equal to
null. For example, the following query predicates cannot result in covered queries:{ "field": null }{ "field": { $eq: null } }
Example
A movies collection has the following index on the rated and
title fields:
db.movies.createIndex( { rated: 1, title: 1 } )
The index covers the following operation which queries on the rated
and title fields and returns only the title field:
db.movies.find( { rated: "PG", title: /^T/ }, { title: 1, _id: 0 } ).limit(3)
For the specified index to cover the query, the projection document
must explicitly specify _id: 0 to exclude the _id field from
the result since the index doesn't include the _id field.
Embedded Documents
An index can cover a query on fields within embedded documents.
For example, consider the theaters collection from the
sample_mflix dataset, which
has documents with the following structure:
{ theaterId: <num>, location: { address: { street1: "<address>", city: "<city>", state: "<state>", zipcode: "<zip>" }, geo: { ... } } }
The collection has the following index:
db.theaters.createIndex( { "location.address.city": 1 } )
The { "location.address.city": 1 } index covers the following
query:
db.theaters.find( { "location.address.city": "Portland" }, { "location.address.city": 1, _id: 0 } ).limit(1)
Note
To index fields in embedded documents, use dot notation. See Create an Index on an Embedded Field.
Multikey Covering
Multikey indexes can cover queries over non-array fields if the index tracks which field or fields make it multikey.
Multikey indexes cannot cover queries over array fields.
For an example, see Covered Queries on the multikey indexes page.
Performance
Covered queries match query conditions and return results using only the index. This is faster than fetching documents because index keys are typically smaller than documents and indexes are usually in RAM or stored sequentially on disk.
Limitations
Index Types
Not all index types support covered queries. See the documentation for the specific index type.
Sharded Collections
When run on mongos, indexes can only cover queries on
sharded collections if the index contains
the shard key.
Explain Results
To check whether a query is covered, use db.collection.explain()
or explain(). See Covered Queries.