1

根据Mongo 文档

{ item : null }查询匹配包含 item其值为null或不包含该item 字段的字段的文档。

我找不到这方面的文档,但据我所知,这两种情况(值是null或字段缺失)都存储在索引中作为null.

因此,如果我这样做db.orders.createIndex({item: 1})then db.orders.find({item: null}),我希望 anIXSCAN找到所有包含item其值为null或不包含该item字段的字段的文档,并且仅找到那些文档。

那么为什么在db.orders.find({item: null}).explain()表演完之后还要filter: {item: {$eq: null}}FETCH舞台上表演IXSCAN呢?可能需要过滤掉哪些可能的文档?

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "temp.orders",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "item" : {
                "$eq" : null
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "item" : {
                    "$eq" : null
                }
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "item" : 1
                },
                "indexName" : "item_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "item" : [
                        "[null, null]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "Andys-MacBook-Pro-2.local",
        "port" : 27017,
        "version" : "3.2.8",
        "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0"
    },
    "ok" : 1
}

我想也许undefined值会被索引为null,但简单的实验排除了这一点:

> db.orders.createIndex({item: 1})
{
    "createdCollectionAutomatically" : true,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}
> db.orders.insert({item: undefined})
WriteResult({ "nInserted" : 1 })
> db.orders.find({item: {$type: 6}}).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "temp.orders",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "item" : {
                "$type" : 6
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "item" : {
                    "$type" : 6
                }
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "item" : 1
                },
                "indexName" : "item_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "item" : [
                        "[undefined, undefined]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "Andys-MacBook-Pro-2.local",
        "port" : 27017,
        "version" : "3.2.8",
        "gitVersion" : "ed70e33130c977bda0024c125b56d159573dbaf0"
    },
    "ok" : 1
}
4

1 回答 1

2

空相等匹配谓词(例如{"a.b": null})的语义足够复杂,因为字段可能包含子文档,仅靠索引扫描不足以提供正确的结果。

根据https://jira.mongodb.org/browse/SERVER-18653?focusedCommentId=931817&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-931817

服务器的 2.6.0 版更改了空相等匹配谓词的语义,使得文档 {a: []} 不再被视为查询谓词 {"ab": null} 的匹配项(在之前版本的服务器,该文档被认为是该谓词的匹配项)。这记录在 2.6 兼容性说明中的“空比较”部分下。

对于键模式为 {"ab": 1} 的索引,此文档 {a: []} 生成索引键 {"": null}。{a: null} 和空文档 {} 等其他文档也会生成索引键 {"": null}。因此,如果带有谓词 {"ab": null} 的查询使用此索引,则查询系统不能仅从索引键 {"": null} 判断相关文档是否与谓词匹配。结果,分配了 INEXACT_FETCH 边界而不是 EXACT 边界,因此将 FETCH 阶段添加到查询执行树中。

补充说明:

  1. 文档 {} 为具有键模式 {"ab": 1} 的索引生成索引键 {"": null}。
  2. 文档 {a: []} 还为具有键模式 {"ab": 1} 的索引生成索引键 {"": null}。
  3. 文档 {} 匹配查询 {"ab": null}。
  4. 文档 {a: []} 与查询 {"ab": null} 不匹配。

因此,由具有键模式 {"ab": 1} 的索引回答的查询 {"ab": null} 必须获取文档并重新检查谓词,以确保文档 {} 包含在结果集中并且文档 {a: []} 不包含在结果集中。

于 2017-08-23T04:20:26.830 回答