文档菜单
文档首页
/
MongoDB 手册
/ /

部分索引

本页内容

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

部分索引只对满足指定过滤表达式的集合中的文档进行索引。通过索引集合中的子集文档,部分索引具有更低的存储需求,并且在创建和维护索引方面的性能成本更低。

要创建一个部分索引,使用带有partialFilterExpression选项的db.collection.createIndex()方法。该partialFilterExpression选项接受一个文档,该文档使用等式表达式(即field: value或使用$eq运算符)指定过滤器条件

  • (例如field: value或使用$eq运算符)

  • $exists: true表达式、

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

  • $type表达式、

  • $and运算符、

  • $or运算符、

  • $in运算符

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

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字段的非空匹配。

例如,以下查询可以使用索引,因为它包含对name字段的条件以及对email字段的非空匹配

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

但是,以下查询无法使用索引,因为它包含对email字段的空匹配,这不符合过滤器表达式{ email: { $exists: true } }

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

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

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

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

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

  • 如果您正在使用客户端字段级加密可查询加密,则partialFilterExpression不能引用加密字段。

从MongoDB 7.3开始,您不能创建等效索引,这些索引是具有相同索引键和相同部分表达式(使用排序规则)的部分索引。

对于MongoDB 7.3中具有现有等效索引的数据库,索引将被保留,但在查询中仅使用第一个等效索引。这与MongoDB 7.3之前的版本的行为相同。

示例请参阅等效索引示例.

考虑一个包含类似以下文档的集合 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中具有现有等效索引的数据库,索引将被保留,但在查询中仅使用第一个等效索引。这与MongoDB 7.3之前的版本的行为相同。

在之前的 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

在 MongoDB 7.3 版本之前的版本中,您可以创建索引,但只有第一个索引(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 }
)

返回

隐藏