Skip to content

Difference between nscanned and nscannedObjects is unclear #1861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions source/reference/method/cursor.explain.txt
Original file line number Diff line number Diff line change
Expand Up @@ -238,28 +238,43 @@ sharded*. For queries on sharded collections, see

.. data:: explain.n

:data:`~explain.n` is a number that reflects the number of documents
that match the query selection criteria.
:data:`~explain.n` is the number of documents that match the query
selection criteria.

For more information on :data:`~explain.n` and query optimization, see
:doc:`/tutorial/analyze-query-plan` and
:doc:`/administration/optimization`.

.. data:: explain.nscannedObjects

Specifies the total number of documents scanned during the query.
The :data:`~explain.nscannedObjects` may be lower than
:data:`~explain.nscanned`, such as if the index :ref:`covers
<indexes-covered-queries>` a query. See
:data:`~explain.indexOnly`. Additionally, the
:data:`~explain.nscannedObjects` may be lower than
:data:`~explain.nscanned` in the case of multikey index on an array
field with duplicate documents.
:data:`~explain.nscannedObjects` is the total number of documents
scanned during the query.

The :data:`~explain.nscannedObjects` value may be lower than
:data:`~explain.nscanned`, if the number of documents or index entries
scanned. :data:`~explain.nscannedObjects` will be 0 if there is a
covered query, a query where all the query fields are part of an
index and all fields returned are in the same index. For information
about covered queries, see :data:`~explain.indexOnly`, which indicates
if the query is covered by the index, and :ref:`Create Indexes that
Support Covered Queries <indexes-covered-queries>`.

A multikey index on an array field with duplicate documents also
results in an :data:`~explain.nscannedObjects` value lower than
:data:`~explain.nscanned`.

For more information on :data:`~explain.nscannedOjects` and query
optimization, see :doc:`/tutorial/analyze-query-plan` and
:doc:`/administration/optimization`.

.. data:: explain.nscanned

Specifies the total number of documents or index entries scanned
during the database operation. You want :data:`~explain.n` and
:data:`~explain.nscanned` to be close in value as possible. The
:data:`~explain.nscanned` value may be higher than the
:data:`~explain.nscannedObjects` value, such as if the index :ref:`covers
<indexes-covered-queries>` a query. See :data:`~explain.indexOnly`.
:data:`~explain.nscanned` is the total number of documents or index
entries scanned during the database operation.

For more information on :data:`~explain.nscanned` and query
optimization, see :doc:`/tutorial/analyze-query-plan` and
:doc:`/administration/optimization`.

.. data:: explain.nscannedObjectsAllPlans

Expand Down
261 changes: 212 additions & 49 deletions source/tutorial/analyze-query-plan.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,228 @@ Analyze Query Performance

.. default-domain:: mongodb

The :method:`~cursor.explain()` cursor method allows you to inspect the
operation of the query system. This method is useful for analyzing the
efficiency of queries, and for determining how the query uses the
index. The :method:`~cursor.explain()` method tests the query
operation, and *not* the timing of query performance. Because
:method:`~cursor.explain()` attempts multiple query plans, it does not
reflect an accurate timing of query performance.
The MongoDB query optimizer processes queries and chooses the most optimal
:doc:`query plan </core/query-plans>` for a query given the available
indexes. Sometimes you can optimize a query or index based on their
impact on database performance.

The :method:`~cursor.explain()` cursor method provides statistics about
the performance of a query. This data output can be useful in measuring
how optimally a query uses indexes, document lookups, and other database
functionality to retrieve information.

The :method:`~cursor.explain()` method output does not reflect an accurate
timing of query performance.

See :doc:`/core/query-optimization`,
:doc:`/tutorial/optimize-query-performance-with-indexes-and-projections`,
and :doc:`/applications/indexes` for more information about building
optimal database queries.

Evaluate the Performance of a Query
-----------------------------------

To use the :method:`~cursor.explain()` method, call the method on a
cursor returned by :method:`~db.collection.find()`.
You can use the :method:`~cursor.explain()` method to understand and
evaluate the performance characteristics of different query and index
combinations.

.. example:: Evaluate a query on the ``type`` field on the collection
``inventory`` that has an index on the ``type`` field.
For the following examples, consider a collection with these six documents with fields
for ``id``, ``section``, and ``status``:

.. code-block:: javascript
.. code-block:: javascript

db.inventory.find( { type: 'food' } ).explain()
{ "_id" : 1, "section" : 1, "status" : 1 }
{ "_id" : 2, "section" : 2, "status" : 1 }
{ "_id" : 3, "section" : 1, "status" : 1 }
{ "_id" : 4, "section" : 3, "status" : 1 }
{ "_id" : 5, "section" : 4, "status" : 0 }
{ "_id" : 6, "section" : 4, "status" : 0 }

Range Query with No Index
~~~~~~~~~~~~~~~~~~~~~~~~~

The following range query retrieves documents where the ``section``
field has a value between ``2`` and ``4``:

.. code-block:: javascript

db.posts.find( { section: { $gte: 2, $lte: 4 } } )

The query returns these four documents:

.. code-block:: javascript

{ "_id" : 2, "section" : 2, "status" : 1 }
{ "_id" : 4, "section" : 3, "status" : 1 }
{ "_id" : 5, "section" : 4, "status" : 0 }
{ "_id" : 6, "section" : 4, "status" : 0 }

The following range query with the :method:`~cursor.explain()` method
generates statistics about the database impact of this query:

.. code-block:: javascript

db.posts.find( { section: { $gte: 2, $lte: 4 } } ).explain()

Consider the results:
The :method:`~cursor.explain()` method returns this output:

.. code-block:: javascript
.. code-block:: javascript

{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 4,
"nscannedObjects" : 6,
"nscanned" : 6,
"nscannedObjectsAllPlans" : 6,
"nscannedAllPlans" : 6,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"server" : "machine.local:27017",
"filterSet" : false
}

This query returned 4 documents, as indicated by the :data:`~explain.n`
field.

{
"cursor" : "BtreeCursor type_1",
"isMultiKey" : false,
"n" : 5,
"nscannedObjects" : 5,
"nscanned" : 5,
"nscannedObjectsAllPlans" : 5,
"nscannedAllPlans" : 5,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : { "type" : [
[ "food",
"food" ]
] },
"server" : "mongodbo0.example.net:27017" }

The ``BtreeCursor`` value of the :data:`~explain.cursor` field
indicates that the query used an index.

This query returned 5 documents, as indicated by the
:data:`~explain.n` field.

To return these 5 documents, the query scanned 5 documents from the
index, as indicated by the :data:`~explain.nscanned` field, and then
read 5 full documents from the collection, as indicated by the
:data:`~explain.nscannedObjects` field.

Without the index, the query would have scanned the whole collection
to return the 5 documents.

See :ref:`explain-results` method for full details on the output.
The :data:`~explain.n` field indicates the query returned 4 documents. The
``BasicCursor`` value of the :data:`~explain.cursor` field indicates the
query did not use an index. See :ref:`explain-output` for descriptions of
the other fields in this output.

The differences between 4 returned documents, 6 documents scanned from the
index, and 6 documents read as a result of the query suggests this might
not be an optimal query. The database scanned and read 2 documents more
than it returned.

A Query with an Index on One Field
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Add an index on the ``section`` field with the
:method:`~db.collection.ensureIndex` method, then run the following range
query for documents with ``section`` fields with values between ``2`` and
``4`` with the :method:`~cursor.explain()` method:

.. code-block:: javascript

db.posts.find( { section: { $gte: 2, $lte: 4 } } ).explain()

The :method:`~cursor.explain()` method returns this output:

.. code-block:: javascript

{
"cursor" : "BtreeCursor section_1",
"isMultiKey" : false,
"n" : 4,
"nscannedObjects" : 4,
"nscanned" : 4,
"nscannedObjectsAllPlans" : 4,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"section" : [
[
2,
4
]
]
},
"server" : "machine.local:27017",
"filterSet" : false
}

This query returned 4 documents, as indicated by the :data:`~explain.n`
field.

To return these 4 documents, the :data:`~explain.nscanned` field indicates
the query scanned 4 documents from the index and the
:data:`~explain.nscannedObjects` field indicates the query read 4 full
documents from the collection.

The ``BtreeCursor section_1`` value of the :data:`~explain.cursor` field
indicates the query used an index on the ``section`` field. See
:ref:`explain-output` for descriptions of the other fields in this output.

The numeric equality between 4 returned documents, 4 documents scanned
from the index, and 4 documents read as a result of the query suggests
this might be an optimal query.

A Query with a Compound Index
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Add a compound index on the ``section`` and ``status`` fields, in that
order, with the :method:`~db.collection.ensureIndex()` method:

.. code-block:: javascript

db.collection.ensureIndex( { section: 1, status: 1 } )

Then run the following query with the :method:`~cursor.explain()` method
to evaluate the database impact of this query request:

.. code-block:: javascript

db.posts.find( { section: { $gte: 2, $lte: 4 }, status: 1 } ).explain()

The :method:`~cursor.explain()` method returns this output:

.. code-block:: javascript

{
"cursor" : "BtreeCursor section_1_status_1",
"isMultiKey" : false,
"n" : 2,
"nscannedObjects" : 2,
"nscanned" : 3,
"nscannedObjectsAllPlans" : 2,
"nscannedAllPlans" : 3,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"section" : [
[
2,
4
]
],
"status" : [
[
1,
1
]
]
},
"server" : "machine.local:27017",
"filterSet" : false
}

This query returned 2 documents, as indicated by the :data:`~explain.n`
field.

To return these 2 documents, the :data:`~explain.nscanned` field indicates
the query scanned 3 documents from the index and the
:data:`~explain.nscannedObjects` field indicates the query read 2 full
documents from the collection.

The ``BtreeCursor section_1_status_1`` value of the
:data:`~explain.cursor` field indicates the query used a compound index on
the ``section`` field and ``status`` field. See :ref:`explain-output` for
descriptions of the other fields in this output.

The differences between 2 returned documents, 3 documents scanned from the
index, and 2 documents read as a result of the query suggests a query with
the compound index might be optimal.

Compare Performance of Indexes
------------------------------
Expand Down
3 changes: 3 additions & 0 deletions source/tutorial/evaluate-operation-performance.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ query.
db.records.find( { a: 1 } ).explain()

.. todo Link to Kay's new explain doc

See :doc:`/tutorial/analyze-query-plan` for more details.