持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
有时,只索引集合汇中一部分文档是有意义的。当我们对文档的子集加索引时,我们可以降低存储需求并降低创建和维护索引的性能成本。为此,MongoDB 支持创建部分索引。
创建索引
要创建部分索引,还是使用db.collection.createIndex()需要带有partialFilterExpression参数。
partialFilterExpression可以支持一下过滤条件:
- 等式表达式(即
field: value或使用$eq) $exists: true$gt、$gte、$lt、$lte$type$and和$or$in
使用
我们先看一个例子,以餐厅集合为例。
db.restaurants.insert({
"name" : "Han Dynasty",
"cuisine" : "Sichuan",
"stars" : 4.4,
"address" : {
"street" : "90 3rd Ave",
"city" : "New York",
"state" : "NY",
"zipcode" : "10003"
}
});
我们需要查询某个城市特定的美食。我们发现服务器收到的查询中,90%以上的查询都是3.5 评分以上的餐厅。因此我们可以创建以下索引:
db.restaurants.createIndex({"address.city": 1, "cuisne": 1})
db.restaurants.createIndex({"address.city": 1, "cuisne": 1, "stars": 1})
也可以创建一个部分索引
db.restaurants.createIndex(
{ "address.city": 1, cuisine: 1 },
{ partialFilterExpression: { 'stars': { $gte: 3.5 } } }
)
当餐厅有3.5以上评分,我们才对城市和美食进行索引,而不是创建前面的复合索引。
我们有效地减少了存储索引键的数量,从而减少我们对索引空间的需求,如果我们的索引变得过大,可能会无法放入到内存中。
部分索引也可用于多键索引。使用多键索引时,会为每个数组元素创建键。如果有特别大的数组,那么将创建大量的索引键。我们可以通过稀疏索引来缓解这类问题。
稀疏索引是部分索引的特例。使用稀疏索引,我们只索引我们正在索引字段的文档,而不是创建有空值的索引键。我们可以通过创建部分索引来实现相同的结果。但是这块部分索引比稀疏索引更有表现力。使用部分索引,我们可以第一个$exists 来扫描是否存在的字段。因此建议使用部分索引而不是稀疏索引。
我们可以试着查询一下
var exp = db.restaurants.explain()
exp.find({'address.city': 'New York', cuisine: 'Sichuan', stars: { $gt: 4.0 }})
{
queryPlanner: {
namespace: 'test.restaurants',
indexFilterSet: false,
parsedQuery: {
'$and': [
{ 'address.city': { '$eq': 'New York' } },
{ cuisine: { '$eq': 'Sichuan' } },
{ stars: { '$gt': 4 } }
]
},
queryHash: 'DAC1A4CF',
planCacheKey: '113446E1',
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
winningPlan: {
stage: 'FETCH',
filter: { stars: { '$gt': 4 } },
inputStage: {
stage: 'IXSCAN',
keyPattern: { 'address.city': 1, cuisine: 1 },
indexName: 'address.city_1_cuisine_1',
isMultiKey: false,
multiKeyPaths: { 'address.city': [], cuisine: [] },
isUnique: false,
isSparse: false,
isPartial: true,
indexVersion: 2,
direction: 'forward',
indexBounds: {
'address.city': [ '["New York", "New York"]' ],
cuisine: [ '["Sichuan", "Sichuan"]' ]
}
}
},
rejectedPlans: []
}
}
按照预期是索引扫描,为了出发索引扫描,我们需要在查询中包含我们索引的字段。