Wanneer u in MongoDB een index maakt op een veld dat een array bevat, wordt deze automatisch gemaakt als een index met meerdere toetsen.
Multikey-indexen ondersteunen efficiënte zoekopdrachten voor arrayvelden.
Multikey-indexen kunnen worden gemaakt voor arrays die scalaire gegevens bevatten (bijvoorbeeld tekenreeksen, getallen, enz.) en geneste documenten.
Voorbeeld
Stel dat we een verzameling hebben met de naam products
die de volgende documenten bevat:
{ "_id" : 1, "product" : "Bat", "sizes" : [ "S", "M", "L" ] }
{ "_id" : 2, "product" : "Hat", "sizes" : [ "S", "L", "XL" ] }
{ "_id" : 3, "product" : "Cap", "sizes" : [ "M", "L" ] }
We kunnen een multikey-index voor die verzameling als volgt maken:
db.products.createIndex(
{
"sizes": 1
}
)
Het is net als het maken van een gewone index. U hoeft niet expliciet aan te geven dat het een index met meerdere toetsen is. MongoDB kan bepalen dat het veld een array bevat en het daarom maken als een index met meerdere sleutels.
Met multikey-indexen maakt MongoDB een indexsleutel voor elk element in de array.
Samengestelde multikey-index op ingesloten documenten
Zoals vermeld, kunt u indexen met meerdere sleutels maken voor arrays die ingesloten documenten bevatten.
U kunt hierop een samengestelde index maken, zodat uw index wordt gemaakt voor meerdere velden in de array.
Stel dat we een verzameling hebben met de naam restaurants
met documenten zoals deze:
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 }] } ])
We zouden een samengestelde multikey-index als volgt kunnen maken:
db.restaurants.createIndex(
{
"reviews.ordered": 1,
"reviews.rating": -1
}
)
Nu wordt de multikey-index gebruikt wanneer we query's uitvoeren die betrekking hebben op die velden.
Zo ziet het zoekplan eruit als we op een van die velden zoeken:
db.restaurants.find( { "reviews.ordered": "Dinner" } ).explain()
Resultaat:
{ "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 }
Het deel dat IXSCAN
. leest betekent dat het een indexscan heeft uitgevoerd. Als het de index niet had gebruikt, zou het een collectiescan hebben gedaan (COLLSCAN
).
Het is hetzelfde als we een zoekopdracht uitvoeren waarbij beide velden in de index betrokken zijn:
db.restaurants.find( { "reviews.ordered": "Dinner", "reviews.rating": { $gt: 3 } } ).explain()
Resultaat:
{ "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 }
Als een van de velden in de zoekopdracht echter niet in de index is opgenomen, resulteert dit in een collectiescan:
db.restaurants.find( { "reviews.name": "Lisa", "reviews.rating": { $gt: 3 } } ).explain()
Resultaat:
{ "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 }