当使用$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 }
}