在MongoDB中创建多键索引
在MongoDB中,当您在包含数组的字段上创建索引时,它会自动创建为多键索引。
多键索引支持对数组字段进行高效查询。
可以为保存标量数据(例如字符串、数字等)和嵌套文档的数组创建多键索引。
例
假设我们有一个名为 products 的集合,其中包含以下文档:
{ "_id" : 1, "product" : "Bat", "sizes" : [ "S", "M", "L" ] }
{ "_id" : 2, "product" : "Hat", "sizes" : [ "S", "L", "XL" ] }
{ "_id" : 3, "product" : "Cap", "sizes" : [ "M", "L" ] }
我们可以在该集合上创建一个多键索引,如下所示:
db.products.createIndex(
{
"sizes": 1
}
)
这就像创建常规索引一样。无需显式指定它是多键索引。MongoDB能够确定该字段是否包含一个数组,因此将其创建为多键索引。
使用多键索引,MongoDB为数组中的每个元素创建一个索引键。
嵌入式文档上的复合多键索引
如前所述,您可以为保存嵌入文档的数组创建多键索引。
您可以对这些字段创建复合索引,以便针对数组中的多个字段创建索引。
假设我们有一个集合,其中包含如下文档:restaurants
db.restaurants.insertMany([ { _id: 1, name: “The Rat”, reviews: [{ name: “Stanley”, date: “04 December, 2020”, ordered: “Dinner”, rating: 1 }, { name: “Tom”, date: “04 October, 2020”, ordered: “Lunch”, rating: 2 }] }, { _id: 2, name: “Yum Palace”, reviews: [{ name: “Stacey”, date: “08 December, 2020”, ordered: “Lunch”, rating: 3 }, { name: “Tom”, date: “08 October, 2020”, ordered: “Breakfast”, rating: 4 }] }, { _id: 3, name: “Boardwalk Cafe”, reviews: [{ name: “Steve”, date: “20 December, 2020”, ordered: “Breakfast”, rating: 5 }, { name: “Lisa”, date: “25 October, 2020”, ordered: “Dinner”, rating: 5 }, { name: “Kim”, date: “21 October, 2020”, ordered: “Dinner”, rating: 5 }] } ])
我们可以创建一个复合多键索引,如下所示:
db.restaurants.createIndex(
{
"reviews.ordered": 1,
"reviews.rating": -1
}
)
现在,每当我们运行涉及这些字段的查询时,都会使用多键索引。
以下是我们搜索其中一个字段时查询计划的外观:
db.restaurants.find( { "reviews.ordered": "Dinner" } ).explain()
输出:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "krankykranes.restaurants",
"indexFilterSet" : false,
"parsedQuery" : {
"reviews.ordered" : {
"$eq" : "Dinner"
}
},
"queryHash" : "A01226B4",
"planCacheKey" : "0E761583",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"reviews.ordered" : 1,
"reviews.rating" : -1
},
"indexName" : "reviews.ordered_1_reviews.rating_-1",
"isMultiKey" : true,
"multiKeyPaths" : {
"reviews.ordered" : [
"reviews"
],
"reviews.rating" : [
"reviews"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"reviews.ordered" : [
"[\"Dinner\", \"Dinner\"]"
],
"reviews.rating" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"ok" : 1
}读取的部分表示它进行了索引扫描。如果它没有使用索引,它将执行集合扫描 ()。IXSCAN``COLLSCAN
当我们执行涉及索引中两个字段的查询时,情况相同:
db.restaurants.find( { "reviews.ordered": "Dinner", "reviews.rating": { $gt: 3 } } ).explain()
输出:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "krankykranes.restaurants",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"reviews.ordered" : {
"$eq" : "Dinner"
}
},
{
"reviews.rating" : {
"$gt" : 3
}
}
]
},
"queryHash" : "C770E210",
"planCacheKey" : "447B5666",
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"reviews.rating" : {
"$gt" : 3
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"reviews.ordered" : 1,
"reviews.rating" : -1
},
"indexName" : "reviews.ordered_1_reviews.rating_-1",
"isMultiKey" : true,
"multiKeyPaths" : {
"reviews.ordered" : [
"reviews"
],
"reviews.rating" : [
"reviews"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"reviews.ordered" : [
"[\"Dinner\", \"Dinner\"]"
],
"reviews.rating" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"ok" : 1
}但是,如果查询中的某个字段未包含在索引中,则会导致集合扫描:
db.restaurants.find( { "reviews.name": "Lisa", "reviews.rating": { $gt: 3 } } ).explain()
输出:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "krankykranes.restaurants",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"reviews.name" : {
"$eq" : "Lisa"
}
},
{
"reviews.rating" : {
"$gt" : 3
}
}
]
},
"queryHash" : "49EF83EC",
"planCacheKey" : "3C60321C",
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"reviews.name" : {
"$eq" : "Lisa"
}
},
{
"reviews.rating" : {
"$gt" : 3
}
}
]
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"ok" : 1
}