Docs 菜单

Docs 主页开发应用程序MongoDB Manual

部分索引

在此页面上

  • 创建部分索引
  • 行为
  • 限制
  • 举例

部分索引仅对集合中满足指定过滤表达式的文档进行索引。通过索引集合中的一部分文档,部分索引的存储要求更低,索引创建和维护的性能成本也更低。

要创建 partial 索引,请使用 db.collection.createIndex() 方法并指定 partialFilterExpression 选项。partialFilterExpression 选项接受使用以下方式指定筛选条件的文档:

  • 等式表达式(即 field: value 或使用 $eq操作符)、

  • $exists: true 表达式,

  • $gt$gte$lt$lte 表达式、

  • $type 表达式,

  • $and 操作符,

  • $or 操作符,

  • $in 运算符

例如,以下操作创建一个复合索引,该索引仅对 rating 字段大于 5 的文档进行索引。

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

您可以为所有 MongoDB索引类型指定 partialFilterExpression 选项。

提示

另请参阅:

如要了解如何在 MongoDB Compass 中管理索引,请参阅管理索引

如果使用部分索引导致结果副本集不完整,MongoDB 不会使用该部分索引进行查询或排序操作。

要使用部分索引,查询必须包含过滤表达式(或指定过滤表达式子集的修改后的过滤表达式)作为其查询条件的一部分。

例如,给定以下索引:

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

以下查询可以使用该索引,因为查询谓词包含的条件 rating: { $gte: 8 } 与以下索引筛选器表达式 rating: { $gt: 5 } 所匹配的一个文档子集匹配:

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

但是,以下查询不能在 cuisine 字段使用部分索引,因为使用该索引会导致结果集不完整。具体来说,查询谓词包含条件 rating: { $lt: 8 },而该索引具有筛选器 rating: { $gt: 5 }。换言之,查询 { cuisine: "Italian", rating: { $lt: 8 } } 匹配的文档(例如评级等于 1 的意大利餐厅)比索引的文档多。

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

同样,以下查询不能使用部分索引,因为查询谓词不包括过滤表达式,并且使用索引将返回不完整的结果集。

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

部分索引应优先于稀疏索引。部分索引具有以下优点:

  • 更好地控制哪些文档被索引。

  • 稀疏索引提供的部分功能。

稀疏索引根据索引字段的存在性来选择要索引的文档,对于复合索引,则根据索引字段的存在性来选择要索引的文档。

部分索引根据指定的过滤器确定索引条目。过滤器可以包含索引键以外的字段,并且可以指定除存在性检查之外的条件。例如,部分索引可以实现与稀疏索引相同的行为:

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

此部分索引支持与 name 字段上的稀疏索引相同的查询。

但是,部分索引还可以在索引键以外的字段上指定筛选器表达式。例如,以下操作创建部分索引,其中索引位于 name 字段,但筛选器表达式位于 email 字段:

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

要让查询优化器选择此部分索引,查询谓词必须在 name 字段上包含条件并在 email 字段上包含 non-null 匹配项。

例如,以下查询可以使用该索引,因为它既在 name 字段上包含条件,又在 email 字段上包含 non-null 匹配:

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

但是,以下查询无法使用索引,因为该查询在 email 字段上包含 null 匹配,这是筛选器表达式 { email: { $exists: true } } 所不允许的。

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

部分索引也可以是 TTL 索引。部分 TTL 索引匹配指定的筛选器表达式,并且仅使那些文档过期。有关详情,请参阅使用筛选条件令文档过期

  • 您不能同时指定 partialFilterExpression 选项和 sparse 选项。

  • _id 索引不能是部分索引。

  • 分片键索引不能为部分索引。

  • 如果您使用的是客户端字段级加密或 可查询加密,则partialFilterExpression无法引用加密字段。

示例集合 restaurants 包含类似于以下内容的文档

{
"_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"
}

您可以对 boroughcuisine 字段添加部分索引,选择仅对 rating.grade 字段为 A 的文档建立索引:

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

然后,对 restaurants 集合的以下查询使用该部分索引返回布朗克斯区中 rating.grade 等于 A 的餐厅:

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

但是,以下查询不能使用该部分索引,因为查询表达式不包括 rating.grade 字段:

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

部分索引仅对集合中满足指定筛选表达式的文档进行索引。如果同时指定 partialFilterExpression唯一性约束,则唯一性约束只适用于符合筛选表达式的文档。如果文档不满足筛选条件,具有唯一性约束的部分索引不会阻止插入不满足唯一性约束的文档。

例如,集合 users 包含以下文档:

{ "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }
{ "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }
{ "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }

以下操作创建一个索引,该索引指定 username 字段上的唯一性约束以及部分筛选器表达式 age: { $gte: 21 }

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

该索引会阻止插入以下文档,因为具有指定用户名的文档已存在,并且 age 字段大于 21

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

但允许插入以下具有重复用户名的文档,因为唯一性约束仅适用于 age 大于或等于 21 的文档。

db.users.insertMany( [
{ username: "david", age: 20 },
{ username: "amanda" },
{ username: "rajiv", age: null }
] )
← 将现有索引转换为唯一索引