Navigation
This version has reached end of life on October 2016. Learn more about upgrading your version of MongoDB.

Analyze Query Performance

The explain() cursor method provides statistics about the performance of a query. This data output can be useful in measuring if and how a query uses an index. [1]

Evaluate the Performance of a Query

Consider a collection inventory with the following documents:

{ "_id" : 1, "item" : "f1", type: "food", quantity: 500 }
{ "_id" : 2, "item" : "f2", type: "food", quantity: 100 }
{ "_id" : 3, "item" : "p1", type: "paper", quantity: 200 }
{ "_id" : 4, "item" : "p2", type: "paper", quantity: 150 }
{ "_id" : 5, "item" : "f3", type: "food", quantity: 300 }
{ "_id" : 6, "item" : "t1", type: "toys", quantity: 500 }
{ "_id" : 7, "item" : "a1", type: "apparel", quantity: 250 }
{ "_id" : 8, "item" : "a2", type: "apparel", quantity: 400 }
{ "_id" : 9, "item" : "t2", type: "toys", quantity: 50 }
{ "_id" : 10, "item" : "f4", type: "food", quantity: 75 }

Query with No Index

The following query retrieves documents where the quantity field has a value between 100 and 200, inclusive:

db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } )

The query returns the following documents:

{ "_id" : 2, "item" : "f2", "type" : "food", "quantity" : 100 }
{ "_id" : 3, "item" : "p1", "type" : "paper", "quantity" : 200 }
{ "_id" : 4, "item" : "p2", "type" : "paper", "quantity" : 150 }

To view the query plan selected, use the explain() method:

db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } ).explain()

The explain() method returns this output:

{
   "cursor" : "BasicCursor",
   "isMultiKey" : false,
   "n" : 3,
   "nscannedObjects" : 10,
   "nscanned" : 10,
   "nscannedObjectsAllPlans" : 10,
   "nscannedAllPlans" : 10,
   "scanAndOrder" : false,
   "indexOnly" : false,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "server" : "myMongoDB.local:27017",
   "filterSet" : false
}
  • cursor displays BasicCursor to indicate a collection scan.
  • n displays 3 to indicate that the query matches and returns three documents.
  • nscanned and nscannedObjects display 10 to indicate that MongoDB had to scan ten documents (i.e. all documents in the collection) to find the three matching documents.

The difference between the number of matching documents and the number documents scanned may suggest that, to improve efficiency, the query might benefit from the use of an index.

Query with Index

To support the query on the quantity field, add an index on the quantity field:

db.inventory.ensureIndex( { quantity: 1 } )

To view the query plan statistics, use the explain() method:

db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } ).explain()

The explain() method returns this output:

{
   "cursor" : "BtreeCursor quantity_1",
   "isMultiKey" : false,
   "n" : 3,
   "nscannedObjects" : 3,
   "nscanned" : 3,
   "nscannedObjectsAllPlans" : 3,
   "nscannedAllPlans" : 3,
   "scanAndOrder" : false,
   "indexOnly" : false,
   "nYields" : 0,
   "nChunkSkips" : 0,
   "millis" : 0,
   "indexBounds" : { "quantity" : [ [ 100, 200 ] ] },
   "server" : "myMongoDB.local:27017",
   "filterSet" : false
}
  • cursor displays BtreeCursor quantity_1 to indicate index use and the name of the index.
  • n displays 3 to indicate that the query matches and returns three documents.
  • nscanned displays 3 to indicate that MongoDB scanned three index entries.
  • nscannedObjects displays 3 to indicate that MongoDB scanned three documents.

When run with an index, the query scanned 3 index entries and 3 documents to return 3 matching documents. Without the index, to return the 3 matching documents, the query had to scan the whole collection, scanning 10 documents.

Compare Performance of Indexes

To manually compare the performance of a query using more than one index, you can use the hint() method in conjunction with the explain() method.

Consider the following query:

db.inventory.find( { quantity: { $gte: 100, $lte: 300 }, type: "food" } )

The query returns the following documents:

{ "_id" : 2, "item" : "f2", "type" : "food", "quantity" : 100 }
{ "_id" : 5, "item" : "f3", "type" : "food", "quantity" : 300 }

To support the query, add a compound index. With compound indexes, the order of the fields matter.

For example, add the following two compound indexes. The first index orders by quantity field first, and then the type field. The second index orders by type first, and then the quantity field.

db.inventory.ensureIndex( { quantity: 1, type: 1 } )
db.inventory.ensureIndex( { type: 1, quantity: 1 } )

Evaluate the effect of the first index on the query:

db.inventory.find( { quantity: { $gte: 100, $lte: 300 }, type: "food" } ).hint({ quantity: 1, type: 1 }).explain()

The explain() method returns the following output:

{
   "cursor" : "BtreeCursor quantity_1_type_1",
   "isMultiKey" : false,
   "n" : 2,
   "nscannedObjects" : 2,
   "nscanned" : 5,
   ...
}

MongoDB scanned 5 index keys (nscanned) to return 2 matching documents (n).

Evaluate the effect of the second index on the query:

db.inventory.find( { quantity: { $gte: 100, $lte: 300 }, type: "food" } ).hint({ type: 1, quantity: 1 }).explain()

The explain() method returns the following output:

{
   "cursor" : "BtreeCursor type_1_quantity_1",
   "isMultiKey" : false,
   "n" : 2,
   "nscannedObjects" : 2,
   "nscanned" : 2,
   ...
}

MongoDB scanned 2 index keys (nscanned) to return 2 matching documents (n).

For this example query, the compound index { type: 1, quantity: 1 } is more efficient than the compound index { quantity: 1, type: 1 }.

[1]Because explain() attempts multiple query plans, the explain() method does not reflect an accurate timing of query performance. For more information on its behavior, see explain().