Partial Indexes
New in version 3.2.
Partial indexes only index the documents in a collection that meet aspecified filter expression. By indexing a subset of the documents in acollection, partial indexes have lower storage requirements and reducedperformance costs for index creation and maintenance.
Create a Partial Index
To create a partial
index, use thedb.collection.createIndex()
method with thepartialFilterExpression
option. The partialFilterExpression
option accepts a document that specifies the filter condition using:
- equality expressions (i.e.
field: value
or using the$eq
operator), $exists: true
expression,$gt
,$gte
,$lt
,$lte
expressions,$type
expressions,$and
operator at the top-level only
For example, the following operation creates a compound index thatindexes only the documents with a rating
field greater than 5.
- db.restaurants.createIndex(
- { cuisine: 1, name: 1 },
- { partialFilterExpression: { rating: { $gt: 5 } } }
- )
You can specify a partialFilterExpression
option for all MongoDBindex types.
Behavior
Query Coverage
MongoDB will not use the partial index for a query or sort operation ifusing the index results in an incomplete result set.
To use the partial index, a query must contain the filter expression(or a modified filter expression that specifies a subset of the filterexpression) as part of its query condition.
For example, given the following index:
- db.restaurants.createIndex(
- { cuisine: 1 },
- { partialFilterExpression: { rating: { $gt: 5 } } }
- )
The following query can use the index since the query predicateincludes the condition rating: { $gte: 8 }
that matches a subset ofdocuments matched by the index filter expression rating: { $gt: 5}
:
- db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } )
However, the following query cannot use the partial index on thecuisine
field because using the index results in an incompleteresult set. Specifically, the query predicate includes the conditionrating: { $lt: 8 }
while the index has the filter rating: { $gt:5 }
. That is, the query { cuisine: "Italian", rating: { $lt: 8 }}
matches more documents (e.g. an Italian restaurant with a ratingequal to 1) than are indexed.
- db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } )
Similarly, the following query cannot use the partial index because thequery predicate does not include the filter expression and using theindex would return an incomplete result set.
- db.restaurants.find( { cuisine: "Italian" } )
Comparison with the sparse Index
Tip
Partial indexes represent a superset of the functionality offered bysparse indexes and should be preferred over sparse indexes.
Partial indexes offer a more expressive mechanism thanSparse Indexes indexes to specify which documents areindexed.
Sparse indexes select documents to index solely based on theexistence of the indexed field, or for compound indexes, the existenceof the indexed fields.
Partial indexes determine the index entries based on the specifiedfilter. The filter can include fields other than the index keys andcan specify conditions other than just an existence check. For example,a partial index can implement the same behavior as a sparse index:
- db.contacts.createIndex(
- { name: 1 },
- { partialFilterExpression: { name: { $exists: true } } }
- )
This partial index supports the same queries as a sparse index on thename
field.
However, a partial index can also specify filter expressions on fieldsother than the index key. For example, the following operation createsa partial index, where the index is on the name
field but thefilter expression is on the email
field:
- db.contacts.createIndex(
- { name: 1 },
- { partialFilterExpression: { email: { $exists: true } } }
- )
For the query optimizer to choose this partial index, the querypredicate must include a condition on the name
field as wellas a non-null match on the email
field.
For example, the following query can use the index because it includesboth a condition on the name
field and a non-null match on theemail
field:
- db.contacts.find( { name: "xyz", email: { $regex: /\.org$/ } } )
However, the following query cannot use the index because itincludes a null match on the email
field, which is not permittedby the filter expression{ email: { $exists: true } }
:
- db.contacts.find( { name: "xyz", email: { $exists: false } } )
Restrictions
In MongoDB, you cannot create multiple versions of an index thatdiffer only in the options. As such, you cannot create multiplepartial indexes that differ only by the filter expression.
You cannot specify both the partialFilterExpression
option andthe sparse
option.
MongoDB 3.0 or earlier do not support partial indexes. To use partialindexes, you must use MongoDB version 3.2 or higher. For shardedclusters or replica sets, all nodes must be version 3.2 or higher.
_id
indexes cannot be partial indexes.
Shard key indexes cannot be partial indexes.
Examples
Create a Partial Index On A Collection
Consider a collection restaurants
containing documents that resemblethe following
- {
- "_id" : ObjectId("5641f6a7522545bc535b5dc9"),
- "address" : {
- "building" : "1007",
- "coord" : [
- -73.856077,
- 40.848447
- ],
- "street" : "Morris Park Ave",
- "zipcode" : "10462"
- },
- "borough" : "Bronx",
- "cuisine" : "Bakery",
- "rating" : { "date" : ISODate("2014-03-03T00:00:00Z"),
- "grade" : "A",
- "score" : 2
- },
- "name" : "Morris Park Bake Shop",
- "restaurant_id" : "30075445"
- }
You could add a partial index on the borough
and cuisine
fieldschoosing only to index documents where the rating.grade
field is A
:
- db.restaurants.createIndex(
- { borough: 1, cuisine: 1 },
- { partialFilterExpression: { 'rating.grade': { $eq: "A" } } }
- )
Then, the following query on the restaurants
collection uses the partial indexto return the restaurants in the Bronx with rating.grade
equal to A
:
- db.restaurants.find( { borough: "Bronx", 'rating.grade': "A" } )
However, the following query cannot use the partial index because thequery expression does not include the rating.grade
field:
- db.restaurants.find( { borough: "Bronx", cuisine: "Bakery" } )
Partial Index with Unique Constraint
Partial indexes only index the documents in a collection that meet aspecified filter expression. If you specify both thepartialFilterExpression
and a unique constraint, the unique constraint only applies to thedocuments that meet the filter expression. A partial index with aunique constraint does not prevent the insertion of documents that donot meet the unique constraint if the documents do not meet the filtercriteria.
For example, a collection users
contains the following documents:
- { "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }
- { "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }
- { "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }
The following operation creates an index that specifies a uniqueconstraint on the username
field and a partialfilter expression age: { $gte: 21 }
.
- db.users.createIndex(
- { username: 1 },
- { unique: true, partialFilterExpression: { age: { $gte: 21 } } }
- )
The index prevents the insertion of the following documents sincedocuments already exist with the specified usernames and the age
fields are greater than 21
:
- db.users.insert( { username: "david", age: 27 } )
- db.users.insert( { username: "amanda", age: 25 } )
- db.users.insert( { username: "rajiv", age: 32 } )
However, the following documents with duplicate usernames are allowedsince the unique constraint only applies to documents with age
greater than or equal to 21.
- db.users.insert( { username: "david", age: 20 } )
- db.users.insert( { username: "amanda" } )
- db.users.insert( { username: "rajiv", age: null } )