部分索引仅对集合中满足指定过滤表达式的文档进行索引。通过索引集合中的一部分文档,部分索引的存储要求更低,索引创建和维护的性能成本也更低。
创建部分索引
要创建 partial 索引,请使用 db.collection.createIndex() 方法并指定 partialFilterExpression 选项。partialFilterExpression 选项接受使用以下方式指定过滤条件的文档:
等式表达式(即
field: value或使用$eq操作符)$exists: true表达式(expression)$type表达式$and运算符$or运算符$in运算符$geoWithin运算符$geoIntersects运算符
示例,以下操作创建一个复合索引,仅对 genre字段为 Drama 的文档编制索引:
db.movies.createIndex( { title: 1 }, { partialFilterExpression: { genres: "Drama" } } )
您可以为所有 MongoDB 索引类型指定 partialFilterExpression 选项。为时间序列集合上的 TTL 索引指定 partialFilterExpression 时,您只能在 metaField 集合上筛选:
提示
如要了解如何在 MongoDB Compass 中管理索引,请参阅管理索引。
行为
查询覆盖
如果使用部分索引导致结果副本集不完整,MongoDB 不会使用该部分索引进行查询或排序操作。
要使用部分索引,查询必须包含过滤表达式(或指定过滤表达式子集的修改后的过滤表达式)作为其查询条件的一部分。
例如,给定以下索引:
db.users.createIndex( { name: 1 }, { partialFilterExpression: { password: { $exists: true } } } )
以下查询可以使用该索引,因为查询谓词包含的条件 password: { $exists: true } 与索引过滤表达式password: { $exists: true
} 所匹配的文档相匹配:
db.users.find( { name: "Ned Stark", password: { $exists: true } } )
但是,以下查询无法对 name字段使用部分索引,因为使用该索引会导致结果设立不完整。具体来说,查询谓词包括条件 password: { $exists: false },而索引具有过滤password: { $exists:
true }。也就是说,查询{ name: "Ned Stark", password: { $exists: false }
} 匹配的文档(没有密码的用户)比索引涵盖的文档还要多。
db.users.find( { name: "Ned Stark", password: { $exists: false } } )
同样,以下查询不能使用部分索引,因为查询谓词不包括过滤表达式,并且使用索引将返回不完整的结果集。
db.users.find( { name: "Ned Stark" } )
与稀疏索引比较
如果要更精确地控制要索引的文档,请使用部分索引而不是稀疏索引:
稀疏索引仅根据索引字段(或多个字段,对于稀疏复合索引)是否存在包含或排除文档。
部分索引根据过滤表达式包含或排除文档。表达式可以包含索引键以外的字段,并且可以指定除现有字段之外的条件。
示例,部分索引可以实现与稀疏索引相同的行为。 此部分索引支持与 name字段上的稀疏索引相同的查询:
db.users.createIndex( { name: 1 }, { partialFilterExpression: { name: { $exists: true } } } )
但是,部分索引还可以过滤索引键以外的字段。示例,name字段上的部分索引可以检查 email字段是否存在:
db.users.createIndex( { name: 1 }, { partialFilterExpression: { email: { $exists: true } } } )
要让查询优化器选择此部分索引,查询谓词必须在 name 字段上包含条件并在 email 字段上包含 non-null 匹配项。
例如,以下查询可以使用该索引,因为它既在 name 字段上包含条件,又在 email 字段上包含 non-null 匹配:
db.users.find( { name: "Ned Stark", email: { $regex: /gameofthron\.es$/ } } )
但是,以下查询无法使用索引,因为该查询在 email 字段上包含 null 匹配,这是筛选器表达式 { email: { $exists: true } } 所不允许的。
db.users.find( { name: "Ned Stark", email: { $exists: false } } )
部分 TTL 索引
部分索引也可以是 TTL 索引。部分 TTL 索引匹配指定的筛选器表达式,并且仅使那些文档过期。有关详情,请参阅使用筛选条件令文档过期。
限制
您不能同时指定
partialFilterExpression选项和sparse选项。_id索引不能是部分索引。分片键索引不能为部分索引。
如果使用客户端字段级加密或 Queryable Encryption 加密,则
partialFilterExpression不能引用加密字段。
等效索引
从MongoDB 7.3开始,您无法创建等效索引,即具有相同索引键和使用排序规则的相同部分表达式的部分索引。
对于 MongoDB 7.3 中已有等价索引的数据库,索引会保留,但查询时只使用第一个等价索引。这与 7.3 之前的 MongoDB 版本的行为相同。
示例,您不能在部分过滤表达式中创建两个仅文本大小写不同的索引。
示例
本页上的示例使用 sample_mflix示例数据集的数据。有关如何将此数据集加载到自管理MongoDB 部署中的详细信息,请参阅加载示例数据集。如果对示例数据库进行了任何修改,则可能需要删除并重新创建数据库才能运行本页上的示例。
在集合上创建部分索引
以下示例在 title 和 genres 字段上添加部分索引。该操作仅对 rating字段为 PG 的文档编制索引:
db.movies.createIndex( { title: 1, genres: 1 }, { partialFilterExpression: { rated: "PG" } } )
以下对 movies集合的查询使用部分索引返回所有标题为“三剑客”的电影的编剧:
db.movies.find( { title: "The Three Musketeers", genres: ["Action", "Adventure", "Comedy"], rated: "PG" }, { writers: 1 } )
但是,以下查询无法使用部分索引,因为查询谓词不包含 rating过滤:
db.movies.find( { genres: "Drama" }, { title: 1 } ).limit(5)
带唯一性约束的部分索引
部分索引仅对集合中满足指定筛选表达式的文档进行索引。如果同时指定 partialFilterExpression 和唯一性约束,则唯一性约束只适用于符合筛选表达式的文档。如果文档不满足筛选条件,具有唯一性约束的部分索引不会阻止插入不满足唯一性约束的文档。
以下对users emailpassword: { $exists: true }集合的操作创建一个索引,该索引指定对 字段和部分过滤表达式 的唯一约束。
db.users.createIndex( { name: 1 }, { name: "name_partial_unique_idx", unique: true, partialFilterExpression: { password: { $exists: true } } } )
该索引可防止插入集合中已存在的电子邮件地址和现有 password字段的文档。
但是,允许存在以下包含重复电子邮件地址的文档,因为唯一性约束仅适用于包含 password字段的文档。
db.users.insertMany( [ // This document does NOT have a password field, so it's NOT indexed // The unique constraint does not apply to it { name: "Jon Snow", email: "jon1@example.com" }, // This document has a password field, so it IS indexed // The unique constraint applies to it { name: "Sansa Stark", email: "sansa@example.com", password: "password123" }, // This document does NOT have a password field, so it's NOT indexed // We can insert it even though it has the same name as the first document // This demonstrates that the unique constraint only applies to indexed documents { name: "Jon Snow", email: "jon2@example.com" } ] )