No MongoDB, quando você cria um índice em um campo que contém uma matriz, ele é criado automaticamente como um índice multichave.
Índices de várias chaves suportam consultas eficientes em campos de matriz.
Índices multichave podem ser criados para arrays que contêm dados escalares (por exemplo, strings, números, etc) e documentos aninhados.
Exemplo
Suponha que tenhamos uma coleção chamada
products
que contém os seguintes documentos:{ "_id" : 1, "product" : "Bat", "sizes" : [ "S", "M", "L" ] }
{ "_id" : 2, "product" : "Hat", "sizes" : [ "S", "L", "XL" ] }
{ "_id" : 3, "product" : "Cap", "sizes" : [ "M", "L" ] }
Podemos criar um índice multikey nessa coleção assim:
db.products.createIndex(
{
"sizes": 1
}
)
É como criar um índice regular. Você não precisa especificar explicitamente que é um índice multichave. O MongoDB é capaz de determinar que o campo contém um array e, portanto, criá-lo como um índice multichave.
Com índices multikey, o MongoDB cria uma chave de índice para cada elemento na matriz.
Índice multichave composto em documentos incorporados
Conforme mencionado, você pode criar índices multichave para matrizes que contêm documentos incorporados.
Você pode criar um índice composto neles, para que seu índice seja criado em vários campos na matriz.
Suponha que tenhamos uma coleção chamada
restaurants
com documentos como este: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 }] } ])
Poderíamos criar um índice multichave composto como este:
db.restaurants.createIndex(
{
"reviews.ordered": 1,
"reviews.rating": -1
}
)
Agora, o índice multichave será usado sempre que executarmos consultas que envolvam esses campos.
Veja como é o plano de consulta quando pesquisamos em um desses campos:
db.restaurants.find( { "reviews.ordered": "Dinner" } ).explain()
Resultado:
{ "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 }
A parte que lê
IXSCAN
significa que ele fez uma varredura de índice. Se não tivesse usado o índice, teria feito uma varredura de coleção (COLLSCAN
). É o mesmo quando fazemos uma consulta que envolve os dois campos do índice:
db.restaurants.find( { "reviews.ordered": "Dinner", "reviews.rating": { $gt: 3 } } ).explain()
Resultado:
{ "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 }
No entanto, se um dos campos da consulta não estiver incluído no índice, isso resultará em uma verificação de coleção:
db.restaurants.find( { "reviews.name": "Lisa", "reviews.rating": { $gt: 3 } } ).explain()
Resultado:
{ "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 }