В 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 }