Database Manual / Indexes / Properties

Partial Indexes部分索引

Partial indexes only index the documents in a collection that meet a specified filter expression. By indexing a subset of the documents in a collection, partial indexes have lower storage requirements and reduced performance costs for index creation and maintenance.部分索引仅对集合中满足指定筛选表达式的文档进行索引。通过为集合中的文档子集建立索引,部分索引具有较低的存储要求,并降低了索引创建和维护的性能成本。

Create a Partial Index创建部分索引

To create a partial index, use the db.collection.createIndex() method with the partialFilterExpression option. The partialFilterExpression option accepts a document that specifies the filter condition using:要创建partial索引,请使用db.collection.createIndex()方法和partialFilterExpression选项。partialFilterExpression选项接受使用以下命令指定筛选条件的文档:

  • equality expressions (i.e. field: value or using the $eq operator),等式表达式(即field: value或使用$eq运算符),
  • $exists: true expression,表达式,
  • $gt, $gte, $lt, $lte expressions,表达式,
  • $type expressions,表达式,
  • $and operator,运算符,
  • $or operator,运算符,
  • $in operator运算符

For example, the following operation creates a compound index that indexes only the documents with a rating field greater than 5.例如,以下操作创建了一个复合索引,该索引仅对rating字段大于5的文档进行索引。

db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)

You can specify a partialFilterExpression option for all MongoDB index types. When specifying a partialFilterExpression for a TTL index on a time series collection, you can only filter on the collection metaField.您可以为所有MongoDB索引类型指定partialFilterExpression选项。为时间序列集合上的TTL索引指定partialFilterExpression时,只能在集合metaField上进行筛选。

Tip

To learn how to manage indexes in MongoDB Compass, see Manage Indexes.要了解如何在MongoDB Compass中管理索引,请参阅管理索引

Behavior行为

Query Coverage查询覆盖率

MongoDB will not use the partial index for a query or sort operation if using the index results in an incomplete result set.如果使用索引会导致结果集不完整,MongoDB将不会在查询或排序操作中使用部分索引。

To use the partial index, a query must contain the filter expression (or a modified filter expression that specifies a subset of the filter expression) 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 predicate includes the condition rating: { $gte: 8 } that matches a subset of documents matched by the index filter expression rating: { $gt: 5 }:以下查询可以使用索引,因为查询谓词包含条件rating: { $gte: 8 },该评级与索引筛选器表达式rating: { $gt: 5 }匹配的文档子集相匹配:

db.restaurants.find( { cuisine: "Italian", rating: { $gte: 8 } } )

However, the following query cannot use the partial index on the cuisine field because using the index results in an incomplete result set. 但是,以下查询无法在cuisine字段上使用部分索引,因为使用索引会导致结果集不完整。Specifically, the query predicate includes the condition rating: { $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 rating equal to 1) than are indexed.具体来说,查询谓词包括条件评级:rating: { $lt: 8 },而索引具有筛选rating: { $gt: 5 }。也就是说,查询{ cuisine: "Italian", rating: { $lt: 8 } }匹配的文档(例如,评级等于1的意大利餐厅)比索引中的多。

db.restaurants.find( { cuisine: "Italian", rating: { $lt: 8 } } )

Similarly, the following query cannot use the partial index because the query predicate does not include the filter expression and using the index would return an incomplete result set.同样,以下查询不能使用部分索引,因为查询谓词不包括筛选表达式,使用索引将返回不完整的结果集。

db.restaurants.find( { cuisine: "Italian" } )

Comparison with Sparse Indexes与稀疏索引的比较

Use partial indexes over sparse indexes if you want more precise control over which documents to index:如果您希望更精确地控制要索引的文档,请使用部分索引而不是稀疏索引

  • Sparse indexes include or exclude documents solely based on the presence of the indexed field (or multiple fields, for sparse compound indexes).稀疏索引仅根据索引字段(或稀疏复合索引的多个字段)的存在来包含或排除文档。
  • Partial indexes include or exclude documents based on the filter expression. The expression can include fields other than index keys, and can specify conditions other than a field existing.部分索引根据筛选表达式包含或排除文档。表达式可以包括索引键以外的字段,也可以指定现有字段以外的条件。

For example, a partial index can implement the same behavior as a sparse index. This partial index supports the same queries as a sparse index on the name field:例如,部分索引可以实现与稀疏索引相同的行为。此部分索引支持与name字段上的稀疏索引相同的查询:

db.contacts.createIndex(
{ name: 1 },
{ partialFilterExpression: { name: { $exists: true } } }
)

However, a partial index can also filter on fields other than the index key. For example, a partial index on the name field can check for the existence of the email field:但是,部分索引也可以筛选索引键以外的字段。例如,name字段上的部分索引可以检查email字段的存在:

db.contacts.createIndex(
{ name: 1 },
{ partialFilterExpression: { email: { $exists: true } } }
)

For the query optimizer to choose this partial index, the query predicate must include a condition on the name field as well as a non-null match on the email field.为了使查询优化器选择此部分索引,查询谓词必须在name字段上包含条件,在电子邮件字段上包含非空匹配。

For example, the following query can use the index because it includes both a condition on the name field and a non-null match on the email field:例如,以下查询可以使用索引,因为它在name字段上包含条件,在email字段上包含非空匹配:

db.contacts.find( { name: "xyz", email: { $regex: /\.org$/ } } )

However, the following query cannot use the index because it includes a null match on the email field, which is not permitted by the filter expression { email: { $exists: true } }:但是,以下查询无法使用索引,因为它在email字段上包含空匹配,这是筛选表达式{ email: { $exists: true } }不允许的:

db.contacts.find( { name: "xyz", email: { $exists: false } } )

Partial TTL Indexes部分TTL索引

Partial indexes can also be TTL indexes. Partial TTL indexes match the specified filter expression and expire only those documents. 部分索引也可以是TTL索引。部分TTL索引与指定的筛选器表达式匹配,并且仅使这些文档过期。For details, see Expire Documents with Filter Conditions.有关详细信息,请参阅使用筛选条件过期文档

Restrictions限制

  • You cannot specify both the partialFilterExpression option and the sparse option.不能同时指定partialFilterExpression选项和sparse选项。
  • _id indexes cannot be partial indexes.索引不能是部分索引。
  • Shard key indexes cannot be partial indexes.分片键索引不能是部分索引。
  • If you are using Client-Side Field Level Encryption or Queryable Encryption, a partialFilterExpression cannot reference an encrypted field.如果您使用的是客户端字段级加密可查询加密,则partialFilterExpression不能引用加密字段。

Equivalent Indexes等效索引

Starting in MongoDB 7.3, you cannot create equivalent indexes, which are partial indexes with the same index keys and the same partial expressions that use a collation.从MongoDB 7.3开始,您无法创建等效索引,即具有相同索引键和使用排序规则的相同部分表达式的部分索引。

For databases in MongoDB 7.3 with existing equivalent indexes, the indexes are retained but only the first equivalent index is used in queries. This is the same behavior as MongoDB versions earlier than 7.3.对于MongoDB 7.3中具有现有等效索引的数据库,保留索引,但在查询中只使用第一个等效索引。这与7.3之前的MongoDB版本的行为相同。

For an example, see Equivalent Indexes Example.有关示例,请参阅等效索引示例

Examples示例

Create a Partial Index On A Collection在集合上创建部分索引

Consider a collection restaurants that contains documents that resemble the following example:考虑一个包含类似以下示例的文档的集合restaurants

db.restaurants.insertOne(
{
_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 fields choosing only to index documents where the rating.grade field is A:您可以在boroughcuisine字段上添加部分索引,只选择对rating.grade字段为A的文档进行索引:

db.restaurants.createIndex(
{ borough: 1, cuisine: 1 },
{ partialFilterExpression: { 'rating.grade': { $eq: "A" } } }
)

Then, the following query on the restaurants collection uses the partial index to return the restaurants in the Bronx with rating.grade equal to A:然后,以下关于restaurants集合的查询使用部分索引返回布朗克斯区rating.grade等于A的餐厅:

db.restaurants.find( { borough: "Bronx", 'rating.grade': "A" } )

However, the following query cannot use the partial index because the query predicate does not include the rating.grade field:但是,以下查询不能使用部分索引,因为查询谓词不包括rating.grade字段:

db.restaurants.find( { borough: "Bronx", cuisine: "Bakery" } )

Partial Index with Unique Constraint具有唯一约束的部分索引

Partial indexes only index the documents in a collection that meet a specified filter expression. If you specify both the partialFilterExpression and a unique constraint, the unique constraint only applies to the documents that meet the filter expression. 部分索引仅对集合中满足指定筛选表达式的文档进行索引。如果同时指定partialFilterExpression唯一约束,则唯一约束仅适用于符合筛选表达式的文档。A partial index with a unique constraint does not prevent the insertion of documents that do not meet the unique constraint if the documents do not meet the filter criteria.如果文档不符合筛选条件,则具有唯一约束的部分索引不会阻止插入不符合唯一约束的文档。

For example, a collection users contains the following documents:例如,集合users包含以下文档:

db.users.insertMany( [
{ _id: ObjectId("56424f1efa0358a27fa1f99a"), username: "david", ag : 29 },
{ _id: ObjectId("56424f37fa0358a27fa1f99b"), username: "amanda", age: 35 },
{ _id: ObjectId("56424fe2fa0358a27fa1f99c"), username: "rajiv", age: 57 }
] )

The following operation creates an index that specifies a unique constraint on the username field and a partial filter expression age: { $gte: 21 }.以下操作创建了一个索引,该索引指定了用户名字段和部分筛选器表达式age: { $gte: 21 }唯一约束

db.users.createIndex(
{ username: 1 },
{ unique: true, partialFilterExpression: { age: { $gte: 21 } } }
)

The index prevents the insertion of the following documents since documents already exist with the specified usernames and the age fields are greater than 21:该索引阻止插入以下文档,因为具有指定用户名的文档已经存在,并且age字段大于21

db.users.insertMany( [
{ username: "david", age: 27 },
{ username: "amanda", age: 25 },
{ username: "rajiv", age: 32 }
] )

However, the following documents with duplicate usernames are allowed since the unique constraint only applies to documents with age greater than or equal to 21.但是,允许使用以下具有重复用户名的文档,因为唯一约束仅适用于age大于或等于21岁的文档。

db.users.insertMany( [
{ username: "david", age: 20 },
{ username: "amanda" },
{ username: "rajiv", age: null }
] )

Equivalent Indexes Example等效指数示例

Starting in MongoDB 7.3, you cannot create equivalent indexes, which are partial indexes with the same index keys and the same partial expressions that use a collation.从MongoDB 7.3开始,您无法创建等效索引,即具有相同索引键和使用排序规则的相同部分表达式的部分索引。

For databases in MongoDB 7.3 with existing equivalent indexes, the indexes are retained but only the first equivalent index is used in queries. This is the same behavior as MongoDB versions earlier than 7.3.对于MongoDB 7.3中具有现有等效索引的数据库,保留索引,但在查询中只使用第一个等效索引。这与7.3之前的MongoDB版本的行为相同。

In previous MongoDB versions, you can create two equivalent indexes. The following example creates a pizzas collection and two equivalent indexes named index0 and index1:在以前的MongoDB版本中,您可以创建两个等效的索引。以下示例创建了一个pizzas集合和两个名为index0index1的等效索引:

// Create the pizzas collection创建披萨集合
db.pizzas.insertMany( [
{ _id: 0, type: "pepperoni", size: "small", price: 4 },
{ _id: 1, type: "cheese", size: "medium", price: 7 },
{ _id: 2, type: "vegan", size: "large", price: 8 }
] )

// Create two equivalent indexes with medium pizza sizes创建两个中等披萨尺寸的等效索引
db.pizzas.createIndex(
{ type: 1 },
{ name: "index0",
partialFilterExpression: { size: "medium" },
collation: { locale: "en_US", strength: 1 }
}
)

db.pizzas.createIndex(
{ type: 1 },
{ name: "index1",
partialFilterExpression: { size: "MEDIUM" },
collation: { locale: "en_US", strength: 1 }
}
)

The indexes are equivalent because the two indexes specify the same pizza size and only differ in the text case in the partial filter expression. Only one index is used by queries: the index that was created first, which is index0 in the previous example.索引是等效的,因为这两个索引指定了相同的披萨大小,并且仅在部分筛选器表达式中的文本大小写上有所不同。查询只使用一个索引:首先创建的索引,在前面的示例中为index0。

Starting in MongoDB 7.3, you cannot create the second index (index1) and this error is returned:从MongoDB 7.3开始,您无法创建第二个索引(index1),并返回以下错误:

MongoServerError: Index already exists with a different name: index0

In MongoDB versions earlier than 7.3, you can create the indexes but only the first index (index0) is used with these queries:在早于7.3的MongoDB版本中,您可以创建索引,但只有第一个索引(index0)用于这些查询:

db.pizzas.find( { type: "cheese", size: "medium" } ).collation(
{ locale: "en_US", strength: 1 }
)

db.pizzas.find( { type: "cheese", size: "MEDIUM" } ).collation(
{ locale: "en_US", strength: 1 }
)

db.pizzas.find( { type: "cheese", size: "Medium" } ).collation(
{ locale: "en_US", strength: 1 }
)