Choosing best index for your MongoDB query
In this article, I will walk you through the process of finding the right index for a common tuning scenario. Consider an example collection with 81K documents similar to the one below:
MongoDB Enterprise repl:PRIMARY> db.city_inspections.findOne() { "_id" : ObjectId("56d61033a378eccde8a83550"), "id" : "10057-2015-ENFO", "certificate_number" : 6007104, "business_name" : "LD BUSINESS SOLUTIONS", "date" : "Feb 25 2015", "result" : "Violation Issued", "sector" : "Tax Preparers - 891", "address" : { "city" : "NEW YORK", "zip" : 10030, "street" : "FREDERICK DOUGLASS BLVD", "number" : 2655 } }
db.city_inspections.find({certificate_number:{$gt:6000000,$lte:9400000},"address.zip":10030}).sort({date:-1}) The query is looking for all documents where certificate number is between 6M to 9.4M and the zip code is equal to 10030. The result will be sorted by date, in descending order. Let’s now observe how it’s handled by MongoDB:
MongoDB Enterprise > explain.find({certificate_number:{$gt:6000000,$lte:9400000},"address.zip":10030}).sort({date:-1})
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "blog.city_inspections",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"address.zip" : {
"$eq" : 10030
}
},
{
"certificate_number" : {
"$lte" : 9400000
}
},
{
"certificate_number" : {
"$gt" : 6007104
}
}
]
},
"winningPlan" : { "stage" : "SORT", "sortPattern" : { "date" : -1 }, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "COLLSCAN", "filter" : { "$and" : [ { "address.zip" : { "$eq" : 10030 } }, { "certificate_number" : { "$lte" : 9400000 } }, { "certificate_number" : { "$gt" : 6007104 } } ] }, "direction" : "forward"
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 82, "executionTimeMillis" : 149, "totalKeysExamined" : 0, "totalDocsExamined" : 81047,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 82,
"executionTimeMillisEstimate" : 20,
"works" : 81133,
"advanced" : 82,
"needTime" : 81050,
"needYield" : 0,
"saveState" : 633,
"restoreState" : 633,
"isEOF" : 1,
"invalidates" : 0,
"sortPattern" : {
"date" : -1
},
"memUsage" : 24329,
"memLimit" : 33554432,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 82,
"executionTimeMillisEstimate" : 20,
"works" : 81050,
"advanced" : 82,
"needTime" : 80967,
"needYield" : 0,
"saveState" : 633,
"restoreState" : 633,
"isEOF" : 1,
"invalidates" : 0,
"inputStage" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"address.zip" : {
"$eq" : 10030
}
},
{
"certificate_number" : {
"$lte" : 9400000
}
},
{
"certificate_number" : {
"$gt" : 6007104
}
}
]
},
"nReturned" : 82,
"executionTimeMillisEstimate" : 120,
"works" : 81049,
"advanced" : 82,
"needTime" : 80966,
"needYield" : 0,
"saveState" : 633,
"restoreState" : 633,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 81047
}
}
}
},
"serverInfo" : {
"host" : "m103",
"port" : 27000,
"version" : "3.6.5-rc0",
"gitVersion" : "a20ecd3e3a174162052ff99913bc2ca9a839d618"
},
"ok" : 1
}
MongoDB Enterprise >
From the execution stats, we can observe that MongoDB has performed a collection scan (totalDocsExamined:81047 and totalKeysScanned:0) to return 82 documents (nReturned) in 149 ms. The time taken might look small here but actually, the ratio between totalDocsExamined:totalKeysExamine:nReturned is what we are interested in. We might decide to create an index as per the keys’ order in the query:
{certificate_number:1,"address.zip":1,date:-1}
MongoDB Enterprise > db.city_inspections.createIndex({certificate_number:1,"address.zip":1,date:-1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
MongoDB Enterprise > explain.find({certificate_number:{$gt:6007104,$lte:9400000},"address.zip":10030}).sort({date:-1}) { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "blog.city_inspections", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "address.zip" : { "$eq" : 10030 } }, { "certificate_number" : { "$lte" : 9400000 } }, { "certificate_number" : { "$gt" : 6007104 } } ] }, "winningPlan" : { "stage" : "SORT", "sortPattern" : { "date" : -1 }, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "certificate_number" : 1, "address.zip" : 1, "date" : -1 }, "indexName" : "certificate_number_1_address.zip_1_date_-1", "isMultiKey" : false, "multiKeyPaths" : { "certificate_number" : [ ], "address.zip" : [ ], "date" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "certificate_number" : [ "(6007104.0, 9400000.0]" ], "address.zip" : [ "[10030.0, 10030.0]" ], "date" : [ "[MaxKey, MinKey]" ] } } } } }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 82, "executionTimeMillis" : 174, "totalKeysExamined" : 40270, "totalDocsExamined" : 82, "executionStages" : { "stage" : "SORT", "nReturned" : 82, "executionTimeMillisEstimate" : 171, "works" : 40354, "advanced" : 82, "needTime" : 40271, "needYield" : 0, "saveState" : 316, "restoreState" : 316, "isEOF" : 1, "invalidates" : 0, "sortPattern" : { "date" : -1 }, "memUsage" : 24329, "memLimit" : 33554432, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "nReturned" : 82, "executionTimeMillisEstimate" : 171, "works" : 40271, "advanced" : 82, "needTime" : 40188, "needYield" : 0, "saveState" : 316, "restoreState" : 316, "isEOF" : 1, "invalidates" : 0, "inputStage" : { "stage" : "FETCH", "nReturned" : 82, "executionTimeMillisEstimate" : 171, "works" : 40270, "advanced" : 82, "needTime" : 40187, "needYield" : 0, "saveState" : 316, "restoreState" : 316, "isEOF" : 1, "invalidates" : 0, "docsExamined" : 82, "alreadyHasObj" : 0, "inputStage" : { "stage" : "IXSCAN", "nReturned" : 82, "executionTimeMillisEstimate" : 171, "works" : 40270, "advanced" : 82, "needTime" : 40187, "needYield" : 0, "saveState" : 316, "restoreState" : 316, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "certificate_number" : 1, "address.zip" : 1, "date" : -1 }, "indexName" : "certificate_number_1_address.zip_1_date_-1", "isMultiKey" : false, "multiKeyPaths" : { "certificate_number" : [ ], "address.zip" : [ ], "date" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "certificate_number" : [ "(6007104.0, 9400000.0]" ], "address.zip" : [ "[10030.0, 10030.0]" ], "date" : [ "[MaxKey, MinKey]" ] }, "keysExamined" : 40270, "seeks" : 40188, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0 } } } } }, "serverInfo" : { "host" : "m103", "port" : 27000, "version" : "3.6.5-rc0", "gitVersion" : "a20ecd3e3a174162052ff99913bc2ca9a839d618" }, "ok" : 1 }
MongoDB Enterprise > db.city_inspections.createIndex({"address.zip":1,certificate_number:1,date:-1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 }
After creating the index and running the query again, we get the following execution plan:
MongoDB Enterprise > explain.find({certificate_number:{$gt:6007104,$lte:9400000},"address.zip":10030}).sort({date:-1})
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "blog.city_inspections",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"address.zip" : {
"$eq" : 10030
}
},
{
"certificate_number" : {
"$lte" : 9400000
}
},
{
"certificate_number" : {
"$gt" : 6007104
}
}
]
},
"winningPlan" : { "stage" : "SORT", "sortPattern" : { "date" : -1 }, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "address.zip" : 1, "certificate_number" : 1, "date" : -1 }, "indexName" : "address.zip_1_certificate_number_1_date_-1", "isMultiKey" : false, "multiKeyPaths" : { "address.zip" : [ ], "certificate_number" : [ ], "date" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "address.zip" : [ "[10030.0, 10030.0]" ], "certificate_number" : [ "(6007104.0, 9400000.0]" ], "date" : [ "[MaxKey, MinKey]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"date" : -1
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"certificate_number" : 1,
"address.zip" : 1,
"date" : -1
},
"indexName" : "certificate_number_1_address.zip_1_date_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"certificate_number" : [ ],
"address.zip" : [ ],
"date" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"certificate_number" : [
"(6007104.0, 9400000.0]"
],
"address.zip" : [
"[10030.0, 10030.0]"
],
"date" : [
"[MaxKey, MinKey]"
]
}
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 82, "executionTimeMillis" : 2, "totalKeysExamined" : 82, "totalDocsExamined" : 82,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 168,
"advanced" : 82,
"needTime" : 84,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"sortPattern" : {
"date" : -1
},
"memUsage" : 24329,
"memLimit" : 33554432,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 84,
"advanced" : 82,
"needTime" : 1,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 83,
"advanced" : 82,
"needTime" : 0,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 82,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 83,
"advanced" : 82,
"needTime" : 0,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"address.zip" : 1,
"certificate_number" : 1,
"date" : -1
},
"indexName" : "address.zip_1_certificate_number_1_date_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"address.zip" : [ ],
"certificate_number" : [ ],
"date" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"address.zip" : [
"[10030.0, 10030.0]"
],
"certificate_number" : [
"(6007104.0, 9400000.0]"
],
"date" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 82,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
},
"serverInfo" : {
"host" : "m103",
"port" : 27000,
"version" : "3.6.5-rc0",
"gitVersion" : "a20ecd3e3a174162052ff99913bc2ca9a839d618"
},
"ok" : 1
}
{"address.zip":1,date:-1,certificate_number:1}
And the execution plan with the above index would be:
MongoDB Enterprise > db.city_inspections.createIndex({"address.zip":1,date:-1,certificate_number:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 3,
"numIndexesAfter" : 4,
"ok" : 1
}
After creating the index, let's see the execution plan:
MongoDB Enterprise > explain.find({certificate_number:{$gt:6007104,$lte:9400000},"address.zip":10030}).sort({date:-1})
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "blog.city_inspections",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"address.zip" : {
"$eq" : 10030
}
},
{
"certificate_number" : {
"$lte" : 9400000
}
},
{
"certificate_number" : {
"$gt" : 6007104
}
}
]
},
"winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "address.zip" : 1, "date" : -1, "certificate_number" : 1 }, "indexName" : "address.zip_1_date_-1_certificate_number_1", "isMultiKey" : false, "multiKeyPaths" : { "address.zip" : [ ], "date" : [ ], "certificate_number" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "address.zip" : [ "[10030.0, 10030.0]" ], "date" : [ "[MaxKey, MinKey]" ], "certificate_number" : [ "(6007104.0, 9400000.0]" ] } } }, "rejectedPlans" : [ { "stage" : "SORT", "sortPattern" : { "date" : -1 }, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "certificate_number" : 1, "address.zip" : 1, "date" : -1 }, "indexName" : "certificate_number_1_address.zip_1_date_-1", "isMultiKey" : false, "multiKeyPaths" : { "certificate_number" : [ ], "address.zip" : [ ], "date" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "certificate_number" : [ "(6007104.0, 9400000.0]" ], "address.zip" : [ "[10030.0, 10030.0]" ], "date" : [ "[MaxKey, MinKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"date" : -1
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"address.zip" : 1,
"certificate_number" : 1,
"date" : -1
},
"indexName" : "address.zip_1_certificate_number_1_date_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"address.zip" : [ ],
"certificate_number" : [ ],
"date" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"address.zip" : [
"[10030.0, 10030.0]"
],
"certificate_number" : [
"(6007104.0, 9400000.0]"
],
"date" : [
"[MaxKey, MinKey]"
]
}
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 82, "executionTimeMillis" : 3, "totalKeysExamined" : 129, "totalDocsExamined" : 82,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 130,
"advanced" : 82,
"needTime" : 46,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 82,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 82,
"executionTimeMillisEstimate" : 0,
"works" : 129,
"advanced" : 82,
"needTime" : 46,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"address.zip" : 1,
"date" : -1,
"certificate_number" : 1
},
"indexName" : "address.zip_1_date_-1_certificate_number_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"address.zip" : [ ],
"date" : [ ],
"certificate_number" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"address.zip" : [
"[10030.0, 10030.0]"
],
"date" : [
"[MaxKey, MinKey]"
],
"certificate_number" : [
"(6007104.0, 9400000.0]"
]
},
"keysExamined" : 129,
"seeks" : 47,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
"host" : "m103",
"port" : 27000,
"version" : "3.6.5-rc0",
"gitVersion" : "a20ecd3e3a174162052ff99913bc2ca9a839d618"
},
"ok" : 1
}
MongoDB Enterprise >
From the execution stats, we can see that totalDocsExamined:totalKeysExamined:nReturned is not exactly 1:1:1 but, as
Share this
Share this
More resources
Learn more about Pythian by reading the following blogs and articles.
Text index usage within MongoDB

Slow Query in eBS AutoConfig with DB 19c
Oracle 11g's Query Result Cache -- Introducing the RC Enqueue
Ready to unlock value from your data?
With Pythian, you can accomplish your data transformation goals and more.