Wenn Sie in MongoDB einen Index für ein Feld erstellen, das ein Array enthält, wird dieser automatisch als Multikey-Index erstellt.
Multikey-Indizes unterstützen effiziente Abfragen von Array-Feldern.
Multikey-Indizes können für Arrays erstellt werden, die skalare Daten (z. B. Zeichenfolgen, Zahlen usw.) und verschachtelte Dokumente enthalten.
Beispiel
Angenommen, wir haben eine Sammlung namens products
die folgende Dokumente enthält:
{ "_id" : 1, "product" : "Bat", "sizes" : [ "S", "M", "L" ] }
{ "_id" : 2, "product" : "Hat", "sizes" : [ "S", "L", "XL" ] }
{ "_id" : 3, "product" : "Cap", "sizes" : [ "M", "L" ] }
Wir können einen Multikey-Index für diese Sammlung wie folgt erstellen:
db.products.createIndex(
{
"sizes": 1
}
)
Es ist wie beim Erstellen eines normalen Index. Sie müssen nicht explizit angeben, dass es sich um einen Multikey-Index handelt. MongoDB kann feststellen, dass das Feld ein Array enthält, und es daher als Multikey-Index erstellen.
Bei Multikey-Indizes erstellt MongoDB einen Indexschlüssel für jedes Element im Array.
Zusammengesetzter Multikey-Index für eingebettete Dokumente
Wie bereits erwähnt, können Sie Multikey-Indizes für Arrays erstellen, die eingebettete Dokumente enthalten.
Sie können daraus einen zusammengesetzten Index erstellen, sodass Ihr Index für mehrere Felder im Array erstellt wird.
Angenommen, wir haben eine Sammlung namens restaurants
mit Dokumenten wie diesem:
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 }] } ])
Wir könnten einen zusammengesetzten Multikey-Index wie diesen erstellen:
db.restaurants.createIndex(
{
"reviews.ordered": 1,
"reviews.rating": -1
}
)
Jetzt wird der Multikey-Index immer dann verwendet, wenn wir Abfragen ausführen, die diese Felder betreffen.
So sieht der Abfrageplan aus, wenn wir nach einem dieser Felder suchen:
db.restaurants.find( { "reviews.ordered": "Dinner" } ).explain()
Ergebnis:
{ "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 }
Der Teil, der IXSCAN
liest bedeutet, dass es einen Index-Scan durchgeführt hat. Wenn es den Index nicht verwendet hätte, hätte es einen Sammlungsscan durchgeführt (COLLSCAN
).
Dasselbe gilt für eine Abfrage, die beide Felder im Index umfasst:
db.restaurants.find( { "reviews.ordered": "Dinner", "reviews.rating": { $gt: 3 } } ).explain()
Ergebnis:
{ "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 }
Wenn jedoch eines der Felder in der Abfrage nicht im Index enthalten ist, führt dies zu einem Sammlungsscan:
db.restaurants.find( { "reviews.name": "Lisa", "reviews.rating": { $gt: 3 } } ).explain()
Ergebnis:
{ "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 }