Partial Indexes
在本页面
3.2 版中的新功能。
部分索引仅索引集合中符合指定过滤器表达式的文档。通过索引集合中文档的子集,部分索引具有较低的存储需求,并降低了索引创建和维护的性能成本。
创建部分索引
若要创建partial
索引,请将db.collection.createIndex()方法与partialFilterExpression
选项一起使用。 partialFilterExpression
选项接受使用以下命令指定过滤条件的文档:
-
等式表达式(即
field: value
或使用$eq运算符), -
$exists: true expression,
-
$type expressions,
-
$and仅限顶级运算符
例如,以下操作创建一个复合索引,该索引仅索引rating
字段大于 5 的文档。
db.restaurants.createIndex(
{ cuisine: 1, name: 1 },
{ partialFilterExpression: { rating: { $gt: 5 } } }
)
您可以为所有 MongoDB index types指定partialFilterExpression
选项。
Behavior
Query Coverage
如果使用索引导致结果集不完整,则 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" } )
与稀疏索引的比较
Tip
部分索引代表稀疏索引提供的功能的超集,应优先于稀疏索引。
与Sparse Indexes索引相比,部分索引提供了一种更具表达力的机制来指定要对哪些文档构建索引。
稀疏索引根据被索引字段的存在来选择文档以“仅”作为索引,对于复合索引,则仅基于被索引字段的存在来选择要进行索引的文档。
部分索引根据指定的过滤器确定索引条目。筛选器可以包含除索引键以外的其他字段,并且可以指定除存在性检查以外的条件。例如,部分索引可以实现与稀疏索引相同的行为:
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
字段上的非空匹配项:
db.contacts.find( { name: "xyz", email: { $regex: /\.org$/ } } )
但是,以下查询无法使用索引,因为它在email
字段上包含空匹配,这是过滤器表达式{ email: { $exists: true } }
不允许的:
db.contacts.find( { name: "xyz", email: { $exists: false } } )
Restrictions
在 MongoDB 中,您不能创建仅在选项上有所不同的多个索引版本。因此,您不能创建仅因过滤器表达式而不同的多个部分索引。
您不能同时指定partialFilterExpression
选项和sparse
选项。
MongoDB 3.0 或更早版本不支持部分索引。要使用部分索引,必须使用 MongoDB 3.2 或更高版本。对于分片群集或副本集,所有节点必须为 3.2 或更高版本。
_id
索引不能是部分索引。
分片键索引不能是部分索引。
Examples
在集合上创建部分索引
考虑一个集合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"
}
您可以在borough
和cuisine
字段上添加部分索引,仅选择索引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
和unique constraint,则唯一约束仅适用于满足过滤器表达式的文档。如果文档不符合过滤条件,则具有唯一性约束的部分索引不会阻止插入不符合唯一性约束的文档。
例如,集合users
包含以下文档:
{ "_id" : ObjectId("56424f1efa0358a27fa1f99a"), "username" : "david", "age" : 29 }
{ "_id" : ObjectId("56424f37fa0358a27fa1f99b"), "username" : "amanda", "age" : 35 }
{ "_id" : ObjectId("56424fe2fa0358a27fa1f99c"), "username" : "rajiv", "age" : 57 }
以下操作将创建一个索引,该索引在username
字段上指定一个unique constraint和一个部分过滤器表达式age: { $gte: 21 }
。
db.users.createIndex(
{ username: 1 },
{ unique: true, partialFilterExpression: { age: { $gte: 21 } } }
)
该索引防止插入以下文档,因为文档已经存在且具有指定的用户名,并且age
字段大于21
:
db.users.insert( { username: "david", age: 27 } )
db.users.insert( { username: "amanda", age: 25 } )
db.users.insert( { username: "rajiv", age: 32 } )
但是,允许以下用户名重复的文档,因为唯一约束仅适用于age
大于或等于 21 的文档。
db.users.insert( { username: "david", age: 20 } )
db.users.insert( { username: "amanda" } )
db.users.insert( { username: "rajiv", age: null } )