为什么索引不能覆盖包含$或表达式的查询?

时间:2016-10-04 20:33:25

标签: mongodb indexing

我在MongoDB 3.2中做了几个“覆盖查询”测试,并注意到它没有涵盖包含$or表达式的查询(仅在同一个字段上?)。但是,如果我在同一查询中用$or表达式替换$in表达式,它就可以正常工作。

我用于测试的索引如下:

db.test_collection.createIndex({ a: 1, b: 1, c: 1 });

这是$or查询的索引

db.test_collection.find({
    a: "string",
    $or: [
        { b: true },
        { b: false },
    ],
    c: "string"
}, { a: 1, b: 1, c: 1, _id: 0 });

这是 涵盖的$in查询

db.test_collection.find({
    a: "string",
    b: { $in: [ true, false ] },
    c: "string"
}, { a: 1, b: 1, c: 1, _id: 0 });

字段相同,投影相同。那么,为什么索引不能涵盖$or查询?

这是explain()查询的$or输出:

{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.test_collection",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "$or" : [
                                                {
                                                        "b" : {
                                                                "$eq" : true
                                                        }
                                                },
                                                {
                                                        "b" : {
                                                                "$eq" : false
                                                        }
                                                }
                                        ]
                                },
                                {
                                        "a" : {
                                                "$eq" : "string"
                                        }
                                },
                                {
                                        "c" : {
                                                "$eq" : "string"
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "PROJECTION",
                        "transformBy" : {
                                "a" : 1,
                                "b" : 1,
                                "c" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "FETCH",
                                "filter" : {
                                        "$or" : [
                                                {
                                                        "b" : {
                                                                "$eq" : true
                                                        }
                                                },
                                                {
                                                        "b" : {
                                                                "$eq" : false
                                                        }
                                                }
                                        ]
                                },
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "keyPattern" : {
                                                "a" : 1,
                                                "b" : 1,
                                                "c" : 1
                                        },
                                        "indexName" : "a_1_b_1_c_1",
                                        "isMultiKey" : false,
                                        "isUnique" : false,
                                        "isSparse" : false,
                                        "isPartial" : false,
                                        "indexVersion" : 1,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "a" : [
                                                        "[\"string\", \"string\"]"
                                                ],
                                                "b" : [
                                                        "[MinKey, MaxKey]"
                                                ],
                                                "c" : [
                                                        "[\"string\", \"string\"]"
                                                ]
                                        }
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 2,
                "executionTimeMillis" : 9,
                "totalKeysExamined" : 2,
                "totalDocsExamined" : 2,
                "executionStages" : {
                        "stage" : "PROJECTION",
                        "nReturned" : 2,
                        "executionTimeMillisEstimate" : 10,
                        "works" : 3,
                        "advanced" : 2,
                        "needTime" : 0,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "transformBy" : {
                                "a" : 1,
                                "b" : 1,
                                "c" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "FETCH",
                                "filter" : {
                                        "$or" : [
                                                {
                                                        "b" : {
                                                                "$eq" : true
                                                        }
                                                },
                                                {
                                                        "b" : {
                                                                "$eq" : false
                                                        }
                                                }
                                        ]
                                },
                                "nReturned" : 2,
                                "executionTimeMillisEstimate" : 10,
                                "works" : 3,
                                "advanced" : 2,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "docsExamined" : 2,
                                "alreadyHasObj" : 0,
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "nReturned" : 2,
                                        "executionTimeMillisEstimate" : 10,
                                        "works" : 3,
                                        "advanced" : 2,
                                        "needTime" : 0,
                                        "needYield" : 0,
                                        "saveState" : 0,
                                        "restoreState" : 0,
                                        "isEOF" : 1,
                                        "invalidates" : 0,
                                        "keyPattern" : {
                                                "a" : 1,
                                                "b" : 1,
                                                "c" : 1
                                        },
                                        "indexName" : "a_1_b_1_c_1",
                                        "isMultiKey" : false,
                                        "isUnique" : false,
                                        "isSparse" : false,
                                        "isPartial" : false,
                                        "indexVersion" : 1,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "a" : [
                                                        "[\"string\", \"string\"]"
                                                ],
                                                "b" : [
                                                        "[MinKey, MaxKey]"
                                                ],
                                                "c" : [
                                                        "[\"string\", \"string\"]"
                                                ]
                                        },
                                        "keysExamined" : 2,
                                        "dupsTested" : 0,
                                        "dupsDropped" : 0,
                                        "seenInvalidated" : 0
                                }
                        }
                }
        },
        "serverInfo" : {
                "host" : "VM-TOMLIN-HP",
                "port" : 27017,
                "version" : "3.2.6",
                "gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25"
        },
        "ok" : 1
}

这是explain()查询的$in输出:

{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.test_collection",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [
                                {
                                        "a" : {
                                                "$eq" : "string"
                                        }
                                },
                                {
                                        "c" : {
                                                "$eq" : "string"
                                        }
                                },
                                {
                                        "b" : {
                                                "$in" : [
                                                        false,
                                                        true
                                                ]
                                        }
                                }
                        ]
                },
                "winningPlan" : {
                        "stage" : "PROJECTION",
                        "transformBy" : {
                                "a" : 1,
                                "b" : 1,
                                "c" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "a" : 1,
                                        "b" : 1,
                                        "c" : 1
                                },
                                "indexName" : "a_1_b_1_c_1",
                                "isMultiKey" : false,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "a" : [
                                                "[\"string\", \"string\"]"
                                        ],
                                        "b" : [
                                                "[false, false]",
                                                "[true, true]"
                                        ],
                                        "c" : [
                                                "[\"string\", \"string\"]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 2,
                "executionTimeMillis" : 0,
                "totalKeysExamined" : 2,
                "totalDocsExamined" : 0,
                "executionStages" : {
                        "stage" : "PROJECTION",
                        "nReturned" : 2,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 3,
                        "advanced" : 2,
                        "needTime" : 0,
                        "needYield" : 0,
                        "saveState" : 0,
                        "restoreState" : 0,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "transformBy" : {
                                "a" : 1,
                                "b" : 1,
                                "c" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 2,
                                "executionTimeMillisEstimate" : 0,
                                "works" : 3,
                                "advanced" : 2,
                                "needTime" : 0,
                                "needYield" : 0,
                                "saveState" : 0,
                                "restoreState" : 0,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "keyPattern" : {
                                        "a" : 1,
                                        "b" : 1,
                                        "c" : 1
                                },
                                "indexName" : "a_1_b_1_c_1",
                                "isMultiKey" : false,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "a" : [
                                                "[\"string\", \"string\"]"
                                        ],
                                        "b" : [
                                                "[false, false]",
                                                "[true, true]"
                                        ],
                                        "c" : [
                                                "[\"string\", \"string\"]"
                                        ]
                                },
                                "keysExamined" : 2,
                                "dupsTested" : 0,
                                "dupsDropped" : 0,
                                "seenInvalidated" : 0
                        }
                }
        },
        "serverInfo" : {
                "host" : "VM-TOMLIN-HP",
                "port" : 27017,
                "version" : "3.2.6",
                "gitVersion" : "05552b562c7a0b3143a729aaa0838e558dc49b25"
        },
        "ok" : 1
}

1 个答案:

答案 0 :(得分:1)

由于 $或运算符的处理方式。根据官方MongoDB课程的video

  

$或运算符采用数组,数组包含每个文档   文档被视为单独的查询,$或运算符匹配   任何匹配数组内任何查询的文档等等   它实际上计算了这些查询的并集。

此外,正如documentation所述:

  

让MongoDB使用索引来评估 $或表达式   索引必须支持 $或表达式中的子句。   否则,MongoDB将执行集合扫描。

如果您将查询重写为以下内容,将使用给定的索引:

db.test_collection.find({
    $or: [
        { a: "string", b: true, c: "string"},
        { a: "string", b: false, c: "string" },
    ],
}, { a: 1, b: 1, c: 1, _id: 0 });

此外,文档直接recommends使用 $ in 运算符来检查相同字段的相等性。