In MongoDB, quando crei un indice su un campo che contiene un array, viene creato automaticamente come indice multichiave.
Gli indici multichiave supportano query efficienti sui campi dell'array.
È possibile creare indici multichiave per array che contengono dati scalari (ad es. stringhe, numeri, ecc.) e documenti nidificati.
Esempio
Supponiamo di avere una collezione chiamata products
che contiene i seguenti documenti:
{ "_id" : 1, "product" : "Bat", "sizes" : [ "S", "M", "L" ] }
{ "_id" : 2, "product" : "Hat", "sizes" : [ "S", "L", "XL" ] }
{ "_id" : 3, "product" : "Cap", "sizes" : [ "M", "L" ] }
Possiamo creare un indice multichiave su quella raccolta in questo modo:
db.products.createIndex(
{
"sizes": 1
}
)
È proprio come creare un normale indice. Non è necessario specificare esplicitamente che si tratta di un indice multichiave. MongoDB è in grado di determinare che il campo contiene un array e quindi di crearlo come indice multichiave.
Con gli indici multichiave, MongoDB crea una chiave di indice per ogni elemento nell'array.
Indice multichiave composto su documenti incorporati
Come accennato, puoi creare indici multichiave per array che contengono documenti incorporati.
Puoi creare un indice composto su questi, in modo che il tuo indice venga creato su più campi nell'array.
Supponiamo di avere una collezione chiamata restaurants
con documenti come questo:
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 }] } ])
Potremmo creare un indice multichiave composto come questo:
db.restaurants.createIndex(
{
"reviews.ordered": 1,
"reviews.rating": -1
}
)
Ora, l'indice multichiave verrà utilizzato ogni volta che eseguiamo query che coinvolgono quei campi.
Ecco come appare il piano di query quando eseguiamo una ricerca in uno di questi campi:
db.restaurants.find( { "reviews.ordered": "Dinner" } ).explain()
Risultato:
{ "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 }
La parte che legge IXSCAN
significa che ha eseguito una scansione dell'indice. Se non avesse utilizzato l'indice, avrebbe eseguito una scansione della raccolta (COLLSCAN
).
È lo stesso quando eseguiamo una query che coinvolge entrambi i campi nell'indice:
db.restaurants.find( { "reviews.ordered": "Dinner", "reviews.rating": { $gt: 3 } } ).explain()
Risultato:
{ "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 }
Tuttavia, se uno dei campi della query non è incluso nell'indice, viene eseguita una scansione della raccolta:
db.restaurants.find( { "reviews.name": "Lisa", "reviews.rating": { $gt: 3 } } ).explain()
Risultato:
{ "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 }