Docs 菜单
Docs 主页
/
MongoDB Manual
/ /

部分索引

在此页面上

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

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

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

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

  • $exists: true 表达式,

  • $gt, $gte, $lt, $lte expressions,

  • $type 表达式,

  • $and 操作符,

  • $or 操作符,

  • $in 运算符

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

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

您可以为所有 MongoDB 索引类型指定 partialFilterExpression 选项。为时间序列集合上的 TTL 索引指定 partialFilterExpression 时,您只能在 metaField 集合上筛选:

提示

另请参阅:

如要了解如何在 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 索引不能是部分索引。

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

  • 如果使用客户端字段级加密Queryable Encryption 加密,则 partialFilterExpression 不能引用加密字段。

从MongoDB 7.3开始,您无法创建等效索引,即具有相同索引键和使用排序规则的相同部分表达式的部分索引。

对于 MongoDB 7.3 中已有等价索引的数据库,索引会保留,但查询时只使用第一个等价索引。这与 7.3 之前的 MongoDB 版本的行为相同。

有关示例,请参阅等效索引示例。

示例集合 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 }
] )

从MongoDB 7.3开始,您无法创建等效索引,即具有相同索引键和使用排序规则的相同部分表达式的部分索引。

对于 MongoDB 7.3 中已有等价索引的数据库,索引会保留,但查询时只使用第一个等价索引。这与 7.3 之前的 MongoDB 版本的行为相同。

在以前的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 }
}
)

这些索引是等效的,因为这两个索引指定了相同的披萨大小,只是部分过滤表达式中文本大小写不同。 查询仅使用一个索引:即最先创建的索引,在上一示例中为index0

从MongoDB 7.3开始,您无法创建第二个索引( index1 ),并会返回以下错误:

MongoServerError: Index already exists with a different name: index0

在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 }
)

后退

Hidden