# MongoDB 索引

# MongoDB 测试表的 schema

```js
{
    "index": string,
    "accountId": string,
    "memberId": string,
    "cardId": string,
}
```

```js
// 插入测试数据
for (var i = 1; i <= 90000; i++) {
    db.test.insert({ index : i , 
            name: "name"+ i , 
            accountId:"accountId" + i, 
            cardId:"cardId" + i, 
            memberId:"memberId" + i,
        })
}
```

# $or

官方文档中提到 https://docs.mongodb.com/manual/reference/operator/query/or/#or-clauses-and-indexes ,对于为包含 $or 的文档创建索引,应为 $or 中 每一个查询子句都创建索引。

# example1

```js
// query 语句
db.test.find(
    {
        "$or":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId123","cardId456","cardId789"]}}
        ]
    }
).explain().queryPlanner.winningPlan
```

测试发现,该例子中需要创建的索引为 {accountId:1} 和 {cardId:1} 两个,不可以创建一个复合索引 {accountId:1, cardId:1}

```js
// 创建 {accountId:1} 和 {cardId:1} 之后的 explain 结果如下,可以发现两个查询条件都正常命中索引
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "OR",
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1
                    },
                    "indexName" : "accountId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1
                    },
                    "indexName" : "cardId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId123\", \"cardId123\"]",
                            "[\"cardId456\", \"cardId456\"]",
                            "[\"cardId789\", \"cardId789\"]"
                        ]
                    }
                }
            ]
        }
    }
}

// 使用 hint 指定 {accountId:1, cardId:1} 索引测试如下:
db.test.find(
    {
        "$or":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId123","cardId456","cardId789"]}}
        ]
    }
).hint({accountId:1, cardId:1}).explain().queryPlanner.winningPlan

// 结果分析发现相当于没有索引,执行了全表扫描:
{
    "stage" : "FETCH",
    "filter" : {
        "$or" : [
            {
                "accountId" : {
                    "$eq" : "accountId100"
                }
            },
            {
                "cardId" : {
                    "$in" : [
                        "cardId123",
                        "cardId456",
                        "cardId789"
                    ]
                }
            }
        ]
    },
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "accountId" : 1,
            "cardId" : 1
        },
        "indexName" : "accountId_1_cardId_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "accountId" : [
                "[MinKey, MaxKey]"
            ],
            "cardId" : [
                "[MinKey, MaxKey]"
            ]
        }
    }
}
```

# example2

```js
// 若查询条件包含 sort
db.test.find(
    {
        "$or":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId123","cardId456","cardId789"]}}
        ]
    }
).sort({"index":1}).explain().queryPlanner.winningPlan
```

表中保留 {accountId:1} 和 {cardId:1} 两个索引,测试发现,过滤阶段正常命中索引,但排序阶段是 `SORT_KEY_GENERATOR`,意味着在内存做的排序

```js
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "SORT",
        "sortPattern" : {
            "index" : 1
        },
        "inputStage" : {
            "stage" : "SORT_KEY_GENERATOR",
            "inputStage" : {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "OR",
                    "inputStages" : [
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "accountId" : 1,
                                "cardId" : 1
                            },
                            "indexName" : "accountId_1_cardId_1",
                            "isMultiKey" : false,
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 1,
                            "direction" : "forward",
                            "indexBounds" : {
                                "accountId" : [
                                    "[\"accountId100\", \"accountId100\"]"
                                ],
                                "cardId" : [
                                    "[MinKey, MaxKey]"
                                ]
                            }
                        },
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "cardId" : 1
                            },
                            "indexName" : "cardId_1",
                            "isMultiKey" : false,
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 1,
                            "direction" : "forward",
                            "indexBounds" : {
                                "cardId" : [
                                    "[\"cardId123\", \"cardId123\"]",
                                    "[\"cardId456\", \"cardId456\"]",
                                    "[\"cardId789\", \"cardId789\"]"
                                ]
                            }
                        }
                    ]
                }
            }
        }
    }
}
```

分别创建索引 {accountId:1,index:1} 和 {cardId:1, index:1},重新执行 explain,结果显示,排序阶段变成了 `SORT_MERGE`, 而且在 `fetch` 之前,说明 sort 已经命中索引

```js
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "SORT_MERGE",
            "sortPattern" : {
                "index" : 1
            },
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "index" : 1
                    },
                    "indexName" : "accountId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId123\", \"cardId123\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId456\", \"cardId456\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId789\", \"cardId789\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                }
            ]
        }
    }
}
```

# example3

```js
db.test.find(
    {
        "$or":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId123","cardId456","cardId789"]}}
        ],
        "memberId":"memberId140"
    }
).sort({"index":1}).explain().queryPlanner.winningPlan

```
当不存在 {memberId:1,index:1} 时,查询可以命中 {accountId:1,index:1} 和 {cardId:1,index:1} ,但是仅仅命中了过滤条件,没有命中排序。

{memberId:1,index:1} 命中了外层的过滤条件和排序。

{accountId:1,memberId:1,index:1} 和 {cardId:1,memberId,index:1} 无法命中

```js
// {accountId:1,index:1} 和 {cardId:1,index:1},
{
    "stage" : "SORT",
    "sortPattern" : {
        "index" : 1
    },
    "inputStage" : {
        "stage" : "SORT_KEY_GENERATOR",
        "inputStage" : {
            "stage" : "FETCH",
            "filter" : {
                "memberId" : {
                    "$eq" : "memberId140"
                }
            },
            "inputStage" : {
                "stage" : "OR",
                "inputStages" : [
                    {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "cardId" : 1,
                            "index" : 1
                        },
                        "indexName" : "cardId_1_index_1",
                        "isMultiKey" : false,
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 1,
                        "direction" : "forward",
                        "indexBounds" : {
                            "cardId" : [
                                "[\"cardId123\", \"cardId123\"]",
                                "[\"cardId456\", \"cardId456\"]",
                                "[\"cardId789\", \"cardId789\"]"
                            ],
                            "index" : [
                                "[MinKey, MaxKey]"
                            ]
                        }
                    },
                    {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "accountId" : 1,
                            "index" : 1
                        },
                        "indexName" : "accountId_1_index_1",
                        "isMultiKey" : false,
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 1,
                        "direction" : "forward",
                        "indexBounds" : {
                            "accountId" : [
                                "[\"accountId100\", \"accountId100\"]"
                            ],
                            "index" : [
                                "[MinKey, MaxKey]"
                            ]
                        }
                    }
                ]
            }
        }
    }
}

//  {memberId:1,index:1}
{
    "stage" : "FETCH",
    "filter" : {
        "$or" : [
            {
                "accountId" : {
                    "$eq" : "accountId100"
                }
            },
            {
                "cardId" : {
                    "$in" : [
                        "cardId123",
                        "cardId456",
                        "cardId789"
                    ]
                }
            }
        ]
    },
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "memberId" : 1,
            "index" : 1
        },
        "indexName" : "memberId_1_index_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "memberId" : [
                "[\"memberId140\", \"memberId140\"]"
            ],
            "index" : [
                "[MinKey, MaxKey]"
            ]
        }
    }
}
```

# $and

$and 的索引执行情况没有文档介绍,下面是一些实际测试结果

# example1

```js
db.test.find(
    {
        "$and":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
        ]
    }
).explain().queryPlanner.winningPlan

```

创建 {accountId:1,cardId:1} 索引后再次执行上述命令,发现两个字段都命中了索引

```js
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "accountId" : 1,
            "cardId" : 1
        },
        "indexName" : "accountId_1_cardId_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "accountId" : [
                "[\"accountId100\", \"accountId100\"]"
            ],
            "cardId" : [
                "[\"cardId100\", \"cardId100\"]",
                "[\"cardId456\", \"cardId456\"]",
                "[\"cardId789\", \"cardId789\"]"
            ]
        }
    }
}
```

# example2

```js
// 若查询条件包含 sort
db.test.find(
    {
        "$and":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
        ]
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```

创建 {accountId:1,cardId:1,index:1},发现全部命中索引:

```js
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "SORT_MERGE",
        "sortPattern" : {
            "index" : 1
        },
        "inputStages" : [
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId100\", \"cardId100\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            },
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId456\", \"cardId456\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            },
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId789\", \"cardId789\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            }
        ]
    }
}
```

# example3

```js
// 若查询条件不只包含 $and
db.test.find(
    {
        "$and":[
            {"accountId":"accountId100"},
            {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
        ],
        "memberId": "memberId100"
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```

创建索引 {accountId:1,cardId:1,memberId:1,index:1}, 测试发现成功命中

```js
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "SORT_MERGE",
        "sortPattern" : {
            "index" : 1
        },
        "inputStages" : [
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "memberId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_memberId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId100\", \"cardId100\"]"
                    ],
                    "memberId" : [
                        "[\"memberId100\", \"memberId100\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            },
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "memberId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_memberId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId456\", \"cardId456\"]"
                    ],
                    "memberId" : [
                        "[\"memberId100\", \"memberId100\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            },
            {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "accountId" : 1,
                    "cardId" : 1,
                    "memberId" : 1,
                    "index" : 1
                },
                "indexName" : "accountId_1_cardId_1_memberId_1_index_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "forward",
                "indexBounds" : {
                    "accountId" : [
                        "[\"accountId100\", \"accountId100\"]"
                    ],
                    "cardId" : [
                        "[\"cardId789\", \"cardId789\"]"
                    ],
                    "memberId" : [
                        "[\"memberId100\", \"memberId100\"]"
                    ],
                    "index" : [
                        "[MinKey, MaxKey]"
                    ]
                }
            }
        ]
    }
}
```

# $and 和 $or 嵌套查询

# $or 中包含 $or

# example1

```js
db.test.find(
    {
        "$or":[
            {
                "$or":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            }
        ]
    }
).explain().queryPlanner.winningPlan
```

依旧可以命中 {accountdId:1} 和 {cardId:1}

```js
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "OR",
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1
                    },
                    "indexName" : "cardId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId100\", \"cardId100\"]",
                            "[\"cardId456\", \"cardId456\"]",
                            "[\"cardId789\", \"cardId789\"]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1
                    },
                    "indexName" : "accountId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ]
                    }
                }
            ]
        }
    }
}
```

# example2

```js
// 相比上一个查询条件,多了 {"memberId":"memberId101"}
db.test.find(
    {
        "$or":[
            {
                "$or":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "memberId":"memberId101"
            }
        ]
    }
).explain().queryPlanner.winningPlan
```

必须这三个索引都存在时才能命中索引,{accountId:1} {cardId:1} 和 {memberId:1} ,所以上面这种写法其实和一个 `$or` 中包含 3 个查询条件没有区别

```js
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "OR",
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "cardId" : 1
                    },
                    "indexName" : "cardId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "cardId" : [
                            "[\"cardId100\", \"cardId100\"]",
                            "[\"cardId456\", \"cardId456\"]",
                            "[\"cardId789\", \"cardId789\"]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1
                    },
                    "indexName" : "accountId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "memberId" : 1
                    },
                    "indexName" : "memberId_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "memberId" : [
                            "[\"memberId101\", \"memberId101\"]"
                        ]
                    }
                }
            ]
        }
    }
}
```

# $or 中包含 $and

# example1

```js
db.test.find(
    {
        "$or":[
            {
                "$and":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            }
        ]
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```
可以命中 {accountId:1,cardId:1,index:1}

# example2

```js
db.test.find(
    {
        "$or":[
            {
                "$and":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "memberId":"memberId140"
            }
        ]
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```
最合适的索引应该为 {accountId:1,cardId:1,index:1} 和 {member:1,index:1},所有字段均可命中

```js
{
    "stage" : "SUBPLAN",
    "inputStage" : {
        "stage" : "FETCH",
        "inputStage" : {
            "stage" : "SORT_MERGE",
            "sortPattern" : {
                "index" : 1
            },
            "inputStages" : [
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "accountId_1_cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ],
                        "cardId" : [
                            "[\"cardId100\", \"cardId100\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "accountId_1_cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ],
                        "cardId" : [
                            "[\"cardId456\", \"cardId456\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accountId" : 1,
                        "cardId" : 1,
                        "index" : 1
                    },
                    "indexName" : "accountId_1_cardId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accountId" : [
                            "[\"accountId100\", \"accountId100\"]"
                        ],
                        "cardId" : [
                            "[\"cardId789\", \"cardId789\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                },
                {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "memberId" : 1,
                        "index" : 1
                    },
                    "indexName" : "memberId_1_index_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "memberId" : [
                            "[\"memberId140\", \"memberId140\"]"
                        ],
                        "index" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                }
            ]
        }
    }
}

```

# $and 中包含 $and

# example1

```js
db.test.find(
    {
        "$and":[
            {
                "$and":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            }
        ]
    }
).hint({accountId:1,cardId:1}).explain().queryPlanner.winningPlan
```

使用 hint() 指定 {accountId:1,cardId:1} 发现可以命中该索引,使用 hint 的原因是默认选择了 {accountId:1},默认行为的原因应该是因为 accountId 已经把文档过滤的只剩下一条了,不需要第 2 个字段的索引

```js
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "accountId" : 1,
            "cardId" : 1
        },
        "indexName" : "accountId_1_cardId_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "accountId" : [
                "[\"accountId100\", \"accountId100\"]"
            ],
            "cardId" : [
                "[\"cardId100\", \"cardId100\"]",
                "[\"cardId456\", \"cardId456\"]",
                "[\"cardId789\", \"cardId789\"]"
            ]
        }
    }
}
```

# example2

```js
db.test.find(
    {
        "$and":[
            {
                "$and":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "memberId":"memberId100"
            }
        ]
    }
).hint({accountId:1,cardId:1,memberId:1}).explain().queryPlanner.winningPlan
```

{accountId:1,cardId:1,memberId:1} 索引可以完全命中,行为与 一个 $and 中包含 3 个查询条件的查询完全一致 

```js
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "keyPattern" : {
            "accountId" : 1,
            "cardId" : 1,
            "memberId" : 1
        },
        "indexName" : "accountId_1_cardId_1_memberId_1",
        "isMultiKey" : false,
        "isUnique" : false,
        "isSparse" : false,
        "isPartial" : false,
        "indexVersion" : 1,
        "direction" : "forward",
        "indexBounds" : {
            "accountId" : [
                "[\"accountId100\", \"accountId100\"]"
            ],
            "cardId" : [
                "[\"cardId100\", \"cardId100\"]",
                "[\"cardId456\", \"cardId456\"]",
                "[\"cardId789\", \"cardId789\"]"
            ],
            "memberId" : [
                "[\"memberId100\", \"memberId100\"]"
            ]
        }
    }
}

```

# $and 中包含 $or

# example1

```js
db.test.find(
    {
        "$and":[
            {
                "$or":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "memberId":"memberId100"
            }
        ]
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```
行为与 $or 中的 example3 是一样的
可以命中 {memberId:1,index:1} 也可以命中 {accountId:1,cardId:1}
无法命中 {accountId:1,memberId:1,index:1} 或者 {cardId:1,memberId:1,index:1}

# example2

```js
db.test.find(
    {
        "$and":[
            {
                "$or":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "$or":[
                    {"memberId":"memberId100"},
                ]
            }
        ]
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```

行为与 example1 是一样的
可以命中 {accountId:1} 和 {cardId:1} 和 {memberId:1}

# example3

```js
db.test.find(
    {
        "$and":[
            {
                "$or":[
                    {"accountId":"accountId100"},
                    {"cardId": {"$in":["cardId100","cardId456","cardId789"]}}
                ]
            },
            {
                "memberId":"memberId100"
            }
        ],
        "name": "name100"
    }
).sort({index:1}).explain().queryPlanner.winningPlan
```

可以命中 {memberId:1,name:1,index:1}

# 结论

  • 对于包含 $or 和 $and 的索引,创建方式为:
    • 只包含 $or ,则拆分查询子句,分别为每个子句创建索引
    • 只包含 $and,则将查询子句以及和 $and 并列的查询字段合并起来建一个索引
    • 若 $and 嵌套 $or , $or 部分遵循 $or 的逻辑,剩余字段合起来建一个索引
    • 若 sort 存在,为每个索引都拼接上 sort 的字段
上次更新: 5/12/2020, 2:19:47 AM