当使用$elemMatch时,如果索引覆盖了过滤器,为什么MongoDB要执行一个获取阶段?
我有下表:当使用$elemMatch时,如果索引覆盖了过滤器,为什么MongoDB要执行一个获取阶段?,mongodb,optimization,indexing,fetch,Mongodb,Optimization,Indexing,Fetch,我有下表: > db.foo.find() { "_id" : 1, "k" : [ { "a" : 50, "b" : 10 } ] } { "_id" : 2, "k" : [ { "a" : 90, "b" : 80 } ] } 在k字段上使用复合索引: "key" : { "k.a" : 1, "k.b" : 1 }, "name" : "k.a_1_k.b_1" 如果我运行以下查询: db.foo.aggregate([ { $ma
> db.foo.find()
{ "_id" : 1, "k" : [ { "a" : 50, "b" : 10 } ] }
{ "_id" : 2, "k" : [ { "a" : 90, "b" : 80 } ] }
在k字段上使用复合索引:
"key" : {
"k.a" : 1,
"k.b" : 1
},
"name" : "k.a_1_k.b_1"
如果我运行以下查询:
db.foo.aggregate([
{ $match: { "k.a" : 50 } },
{ $project: { _id : 0, "dummy": {$literal:""} }}
])
如果使用索引(有意义),并且不需要获取阶段:
"winningPlan" : {
"stage" : "COUNT_SCAN",
"keyPattern" : {
"k.a" : 1,
"k.b" : 1
},
"indexName" : "k.a_1_k.b_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"k.a" : [ ],
"k.b" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"indexBounds" : {
"startKey" : {
"k.a" : 50,
"k.b" : { "$minKey" : 1 }
},
"startKeyInclusive" : true,
"endKey" : {
"k.a" : 50,
"k.b" : { "$maxKey" : 1 }
},
"endKeyInclusive" : true
}
}
但是,如果我运行以下使用
$elemMatch
的查询:
db.foo.aggregate([
{ $match: { k: {$elemMatch: {a : 50, b : { $in : [5, 6, 10]}}}} },
{ $project: { _id : 0, "dummy" : {$literal : ""}} }
])
存在提取阶段(不需要AFAIK):
我正在使用MongoDB 3.4。
我这样问是因为我有一个包含大量文档的数据库,并且有一个查询使用了aggregate()
和$elemMatch
(它比在C问题中不投影任何内容更有用,但理论上不需要提取阶段)。我发现查询速度慢的主要原因是FETCH阶段,不需要AFAIK
当使用
$elemMatch
时,是否有一些逻辑强制MongoDB使用FETCH,或者这只是缺少的优化?看起来,即使是单个“$elemMatch”也会强制mongo执行FETCH->COUNT而不是COUNT\u扫描。通过简单的复制步骤在Jira中打开一张票据-TLDR:这是多键索引与$elemMatch组合的预期行为
从多键索引文档的部分:
多键索引不能覆盖数组字段上的查询
这意味着关于子文档的所有信息都不在多键索引中
让我们设想以下场景:
//doc1
{
"k" : [ { "a" : 50, "b" : 10 } ]
}
//doc2
{
"k" : { "a" : 50, "b" : 10 }
}
因为Mongo会“展平”它所索引的数组,一旦建立了索引,Mongo就无法区分这两个文档,$elemMatch
特别需要一个数组对象来匹配(即doc2永远不会匹配$elemMatch
查询)。
这意味着Mongo必须获取
文档以确定哪些文档将匹配,这是导致您看到的行为的前提
//doc1
{
"k" : [ { "a" : 50, "b" : 10 } ]
}
//doc2
{
"k" : { "a" : 50, "b" : 10 }
}