Lets learn about multikey indexes and how they are efficient in MongoDB.
database name: daily
collections: gadgets, users
Insert documents into gadgets collection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| > use daily
switched to db daily
> db.gadgets.insert({_id: 1, gadget: "Nexus 5"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 2, gadget: "iPhone"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 3, gadget: "iPad"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 4, gadget: "Nexus 7"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.find()
{ "_id" : 1, "gadget" : "Nexus 5" }
{ "_id" : 2, "gadget" : "iPhone" }
{ "_id" : 3, "gadget" : "iPad" }
{ "_id" : 4, "gadget" : "Nexus 7" } |
> use daily
switched to db daily
> db.gadgets.insert({_id: 1, gadget: "Nexus 5"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 2, gadget: "iPhone"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 3, gadget: "iPad"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.insert({_id: 4, gadget: "Nexus 7"});
WriteResult({ "nInserted" : 1 })
> db.gadgets.find()
{ "_id" : 1, "gadget" : "Nexus 5" }
{ "_id" : 2, "gadget" : "iPhone" }
{ "_id" : 3, "gadget" : "iPad" }
{ "_id" : 4, "gadget" : "Nexus 7" }
Here we have 4 documents with “_id” as 1, 2, 3, 4.
Insert documents into users collection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| > db.users.insert({name: "Satish",
products: [db.gadgets.find()[0]._id,
db.gadgets.find()[3]._id]});
WriteResult({ "nInserted" : 1 })
> db.users.insert({name: "Kiran",
products: [db.gadgets.find()[0]._id,
db.gadgets.find()[3]._id,
db.gadgets.find()[2]._id]});
WriteResult({ "nInserted" : 1 })
> db.users.insert({name: "Jyothi", products: [1, 2]});
WriteResult({ "nInserted" : 1 })
> db.users.find().pretty()
{
"_id" : ObjectId("53c7a30efd7d3c9597ca2593"),
"name" : "Satish",
"products" : [
1,
4
]
}
{
"_id" : ObjectId("53c7a337fd7d3c9597ca2594"),
"name" : "Kiran",
"products" : [
1,
4,
3
]
}
{
"_id" : ObjectId("53c7a34efd7d3c9597ca2595"),
"name" : "Jyothi",
"products" : [
1,
2
]
} |
> db.users.insert({name: "Satish", products: [db.gadgets.find()[0]._id, db.gadgets.find()[3]._id]});
WriteResult({ "nInserted" : 1 })
> db.users.insert({name: "Kiran", products: [db.gadgets.find()[0]._id, db.gadgets.find()[3]._id, db.gadgets.find()[2]._id]});
WriteResult({ "nInserted" : 1 })
> db.users.insert({name: "Jyothi", products: [1, 2]});
WriteResult({ "nInserted" : 1 })
> db.users.find().pretty()
{ "_id" : ObjectId("53c7a30efd7d3c9597ca2593"), "name" : "Satish", "products" : [ 1, 4 ]
}
{ "_id" : ObjectId("53c7a337fd7d3c9597ca2594"), "name" : "Kiran", "products" : [ 1, 4, 3 ]
}
{ "_id" : ObjectId("53c7a34efd7d3c9597ca2595"), "name" : "Jyothi", "products" : [ 1, 2 ]
}
Here we insert documents into “users” collection and embed the “_id” value of “gadgets” collection as array elements of “products” key.
Multi-key Index: MongoDB
[youtube https://www.youtube.com/watch?v=9C1x-xrN_qQ]
Command to fetch documents with array element 1 and 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| > db.users.find({products: {$all: [1, 4]}}).pretty();
{
"_id" : ObjectId("53c7a30efd7d3c9597ca2593"),
"name" : "Satish",
"products" : [
1,
4
]
}
{
"_id" : ObjectId("53c7a337fd7d3c9597ca2594"),
"name" : "Kiran",
"products" : [
1,
4,
3
]
} |
> db.users.find({products: {$all: [1, 4]}}).pretty();
{ "_id" : ObjectId("53c7a30efd7d3c9597ca2593"), "name" : "Satish", "products" : [ 1, 4 ]
}
{ "_id" : ObjectId("53c7a337fd7d3c9597ca2594"), "name" : "Kiran", "products" : [ 1, 4, 3 ]
}
Both these documents have array values 1 and 4 in them.
Related Read: Comparison Operators: MongoDB
system.indexes content
1
2
3
4
5
6
7
8
| > show collections
gadgets
system.indexes
users
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.gadgets" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.users" } |
> show collections
gadgets
system.indexes
users
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.gadgets" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.users" }
Here we have only 1 key and it’s on “_id”.
Basic Cursor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| > db.users.find({products: {$all: [1, 4]}}).explain();
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 2,
"nscannedObjects" : 3,
"nscanned" : 3,
"nscannedObjectsAllPlans" : 3,
"nscannedAllPlans" : 3,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"server" : "Satish-PC:27017",
"filterSet" : false
} |
> db.users.find({products: {$all: [1, 4]}}).explain();
{ "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 2, "nscannedObjects" : 3, "nscanned" : 3, "nscannedObjectsAllPlans" : 3, "nscannedAllPlans" : 3, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "server" : "Satish-PC:27017", "filterSet" : false
}
If we chain explain() method to our command, we can know some details about the command and also evaluate about its efficiency. The above command doesn’t have multi-key enabled and it’s a Basic Cursor.
Lets create index on products field
1
2
3
4
5
6
7
| > db.users.ensureIndex({"products": 1});
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
} |
> db.users.ensureIndex({"products": 1});
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1
}
This creates index on field “products”.
Now the system.indexes content
1
2
3
4
5
6
| > db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.gadgets" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.users" }
{ "v" : 1, "key" : { "products" : 1 },
"name" : "products_1",
"ns" : "daily.users" } |
> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.gadgets" }
{ "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "daily.users" }
{ "v" : 1, "key" : { "products" : 1 }, "name" : "products_1", "ns" : "daily.users" }
So now we have 2 keys, “_id” and “products”.
BTree Cursor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| > db.users.find({products: {$all: [1, 4]}}).explain();
{
"cursor" : "BtreeCursor products_1",
"isMultiKey" : true,
"n" : 2,
"nscannedObjects" : 3,
"nscanned" : 3,
"nscannedObjectsAllPlans" : 3,
"nscannedAllPlans" : 8,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"products" : [
[
1,
1
]
]
},
"server" : "Satish-PC:27017",
"filterSet" : false
} |
> db.users.find({products: {$all: [1, 4]}}).explain();
{ "cursor" : "BtreeCursor products_1", "isMultiKey" : true, "n" : 2, "nscannedObjects" : 3, "nscanned" : 3, "nscannedObjectsAllPlans" : 3, "nscannedAllPlans" : 8, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "products" : [ [ 1, 1 ] ] }, "server" : "Satish-PC:27017", "filterSet" : false
}
Now if we run the same command chained with explain() method, we could see that its a Btree Cursor and multi-key index is true.
Note: Btree Cursors are faster and efficient than Basic Cursors.