我有一个具有以下索引的orders集合,其中包括:
{location: 1, completedDate: 1, estimatedProductionDate: 1, estimatedCompletionDate: 1}我正在执行以下查询:
db.orders.find({
status: {$in: [1, 2, 3]},
location: "PA",
$or: [
{completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}},
{
completedDate: null,
estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
}
]
}).explain()我希望这将为$or的每个分支执行一个有效的$or,然后组合结果:
{completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}}
"indexBounds" : {
"location" : [
"[\"TX\", \"TX\"]"
],
"completedDate" : [
"[MinKey, ISODate("2017-08-22T04:59:59.999Z")]"
],
"estimatedProductionDate" : [
"[MinKey, MaxKey]"
],
"estimatedCompletionDate" : [
"[MinKey, MaxKey]"
]
}
{
completedDate: null,
estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
}
"indexBounds" : {
"location" : [
"[\"TX\", \"TX\"]"
],
"completedDate" : [
"[null, null]"
],
"estimatedProductionDate" : [
"[MinKey, ISODate("2017-08-22T04:59:59.999Z")]"
],
"estimatedCompletionDate" : [
"[MinKey, MaxKey]"
]
}相反,它只将location限制在IXSCAN中,并在FETCH期间执行其余的筛选。有没有办法优化这个查询而不将它分割成两个单独的查询?
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"$and" : [
{
"completedDate" : {
"$eq" : null
}
},
{
"estimatedProductionDate" : {
"$lt" : "2017-08-22T04:59:59.999Z"
}
}
]
},
{
"completedDate" : {
"$lt" : "2017-08-22T04:59:59.999Z"
}
}
]
},
{
"status" : {
"$in" : [
1,
2,
3
]
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"location" : 1,
"completedDate" : 1,
"estimatedProductionDate" : 1,
"estimatedCompletionDate" : 1
},
"indexName" : "location_1_completedDate_1_estimatedProductionDate_1_estimatedCompletionDate_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"location" : [
"[\"TX\", \"TX\"]"
],
"completedDate" : [
"[MinKey, MaxKey]"
],
"estimatedProductionDate" : [
"[MinKey, MaxKey]"
],
"estimatedCompletionDate" : [
"[MinKey, MaxKey]"
]
}
}
},发布于 2017-08-21 01:55:46
有三个问题立即显现出来:
你的索引
我不确定您有哪些其他索引,但是您的查询是这样的:
{
status:1,
location:1,
$or: [
{completedDate:1},
{completedDate:1, estimatedProductionDate:1}
]
}但是,索引不包含术语status。您需要索引中的status字段来最大限度地使用索引。
您的$or查询
转述页面$or子句和索引
..。要使MongoDB使用索引计算$or表达式,索引必须支持$or表达式中的所有子句。否则,MongoDB将执行集合扫描。
简单地说,高效的$or查询在MongoDB中将要求$or术语成为顶级术语,该术语的每个部分都由一个索引支持。
例如,您可能会发现以下索引和查询的性能要好一些:
db.orders.createIndex({
status:1,
location:1,
completedDate:1,
estimatedProductionDate:1
})
db.orders.explain().find({
$or: [
{
status: {$in: [1, 2, 3]},
location: "PA",
completedDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}},
{
status: {$in: [1, 2, 3]},
location: "PA",
completedDate: null,
estimatedProductionDate: {$lt: ISODate("2017-08-22T04:59:59.999Z")}
}
]
})原因是因为MongoDB将$or查询中的每个术语视为单独的查询。因此,每个术语都可以使用自己的索引。
请注意,我前面建议的索引中字段的顺序遵循查询中字段的顺序。
但是,这仍然不是最优的,因为MongoDB必须在索引扫描completedDate: null查询之后使用filter: {completedDate: {$eq: null}}执行fetch操作。其原因是微妙的,也是解释得最好的这里。
因此,由键模式{"a.b":1}的索引回答的查询{"a.b":null}必须获取文档并重新检查谓词,以确保文档{}包含在结果集中,并且文档{a:[]}不包含在结果集中。
为了最大限度地利用索引,您最好只在completedDate字段中分配一些东西,而不是将其设置为null。
https://stackoverflow.com/questions/45763318
复制相似问题