Skip to content

DOCSP-48381 make specify docs page #1037

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

Merged
Merged
Show file tree
Hide file tree
Changes from 13 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
4 changes: 3 additions & 1 deletion source/connect/connection-targets.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ Choose a Connection Target
:local:
:backlinks: none
:depth: 2
:class: singlecol
:class: singlecol

.. _node-other-ways-to-connect:
311 changes: 308 additions & 3 deletions source/crud/query/specify-documents-to-return.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,311 @@ Overview
In this guide, you can learn how to specify which documents to return from a read
operation by using the following methods:

- ``limit``: Specifies the maximum number of documents to return from a query.
- ``sort``: Specifies the sort order for the returned documents.
- ``skip``: Specifies the number of documents to skip before returning query results.
- ``sort()``: Specifies the sort order for the returned documents.
- ``limit()``: Specifies the maximum number of documents to return from a query.
- ``skip()``: Specifies the number of documents to skip before returning query results.

You can use these methods either by chaining them to your read operation or by specifying them in an
``options`` object in your call to your read operation.

.. note::

If you chain ``sort()``, ``limit()``, or ``skip()`` to a read operation, you must
specify all methods before iterating the cursor. If you specify a method after
iterating the cursor, the method you specified does not apply to the operation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the best place to mention this or honestly if it is even worth mentioning - but if you use the chaining API you must specify all options before starting to iterate the cursor.

const cursor = find();
await cursor.next();
cursor.limit(5); // does not work

Should we mention this limitation of the builder API somewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good information for users to have, thanks for informing me! I added a note about it in the overview section, let me know if the wording is technically correct! Do you know where the API documentation about this would be/if there is any? I was looking in https://mongodb.github.io/node-mongodb-native/6.15/ but couldn't find anything specifically about the chaining API.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't document this, although it might be nice to add to our doc comments. I looked at the code though and we actually throw an error in this scenario. For example, here's the source code for limit:

/**
   * Set the limit for the cursor.
   *
   * @param value - The limit for the cursor query.
   */
  limit(value: number): this {
    this.throwIfInitialized();  // <-- here we throw
    if (this.findOptions.tailable) {
      throw new MongoTailableCursorError('Tailable cursor does not support limit');
    }

    if (typeof value !== 'number') {
      throw new MongoInvalidArgumentError('Operation "limit" requires an integer');
    }

    this.findOptions.limit = value;
    return this;
  }

So I guess, anybody who tries to set these values after the cursor is initialized would see errors. Given that, do you think its worth documenting explicitly here as well? We could also just add a note to the API docs that specify that they will throw if the cursor is initialized.

Also, its worth mentioning that this applies to all our cursor chaining APIs, not just skip/limit/sort. So if we document it, it would be nice to document this behavior more generally than just for these three options, I think? This error is thrown for any option that affects the actual cursor command the driver sends (ex: project, allowDiskUse, comment, etc).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can still leave this note in these docs for now, and based on our discussion in slack about standardization, we also made another ticket about adding a note about this cursor chaining behavior.

Sample Data for Examples
~~~~~~~~~~~~~~~~~~~~~~~~

To run the examples in this guide, use the following code snippet to insert documents
that describe books into the ``myDB.books`` collection:

.. code-block:: javascript

const myDB = client.db("myDB");
const myColl = myDB.collection("books");

await myColl.insertMany([
{ "_id": 1, "name": "The Brothers Karamazov", "author": "Dostoyevsky", "length": 824 },
{ "_id": 2, "name": "Les Misérables", "author": "Hugo", "length": 1462 },
{ "_id": 3, "name": "Atlas Shrugged", "author": "Rand", "length": 1088 },
{ "_id": 4, "name": "Infinite Jest", "author": "Wallace", "length": 1104 },
{ "_id": 5, "name": "Cryptonomicon", "author": "Stephenson", "length": 918 },
{ "_id": 6, "name": "A Dance With Dragons", "author": "Martin", "length": 1104 },
]);

.. include:: /includes/access-cursor-note.rst

.. _node-fundamentals-sort:

Sort
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I actually think the Sort section should come first (before both skip and limit) since it's used in both of them, should've clarified that last time oops

----

Use the ``sort()`` method to change the order in which read operations return
documents. This method tells MongoDB to order returned documents by the
values of one or more fields in a certain direction. To sort returned
documents by a field in ascending (lowest first) order, use a value of
``1``. To sort in descending (greatest first) order instead, use ``-1``.
If you do not specify a sort, MongoDB does not guarantee the order of
query results.

The following example passes the sort document to a read operation to ensure that the
operation returns books with longer lengths before books with shorter
lengths:

.. code-block:: javascript
:emphasize-lines: 4

// define an empty query document
const query = {};
// sort in descending (-1) order by length
const sort = { length: -1 };
const cursor = myColl.find(query).sort(sort);
for await (const doc of cursor) {
console.dir(doc);
}

In this case, the number ``-1`` tells the read operation to sort the
books in descending order by length. ``find()`` returns the following
documents when this sort is used with an empty query:

.. code-block:: json
:copyable: false

{ "_id": 2, "title": "Les Misérables", "author": "Hugo", "length": 1462 }
{ "_id": 4, "title": "Infinite Jest", "author": "Wallace", "length": 1104 }
{ "_id": 6, "title": "A Dance with Dragons", "author": "Martin", "length": 1104 }
{ "_id": 3, "title": "Atlas Shrugged", "author": "Rand", "length": 1088 }
{ "_id": 5, "title": "Cryptonomicon", "author": "Stephenson", "length": 918 }
{ "_id": 1, "title": "The Brothers Karamazov", "author": "Dostoyevsky", "length": 824 }

Sometimes, the order of two or more documents is ambiguous using a specified sort. In the
preceding example, the documents that have ``title`` values of ``"A Dance with Dragons"`` and
``"Infinite Jest"`` both have a ``length`` of ``1104``, so the order in which they are
returned is not guaranteed. To resolve ties in your sorted results in a repeatable way,
add more fields to the sort document:

.. code-block:: javascript
:emphasize-lines: 4

// define an empty query document
const query = {};
// sort in ascending (1) order by length
const sort = { length: 1, author: 1 };
const cursor = myColl.find(query).sort(sort);
for await (const doc of cursor) {
console.dir(doc);
}

With the addition of the ``author`` field to the sort document, the read operation sorts
matching documents first by ``length`` then, if there is a tie, by ``author``. Matched
document fields are compared in the same order as fields are specified in the sort
document. ``find()`` returns the following ordering of documents when this sort is used on
the documents matching the query:

.. code-block:: json
:copyable: false

{ "_id": 1, "title": "The Brothers Karamazov", "author": "Dostoyevsky", "length": 824 }
{ "_id": 5, "title": "Cryptonomicon", "author": "Stephenson", "length": 918 }
{ "_id": 3, "title": "Atlas Shrugged", "author": "Rand", "length": 1088 }
{ "_id": 6, "title": "A Dance with Dragons", "author": "Martin", "length": 1104 }
{ "_id": 4, "title": "Infinite Jest", "author": "Wallace", "length": 1104 }
{ "_id": 2, "title": "Les Misérables", "author": "Hugo", "length": 1462 }

.. _node-fundamentals-limit:

Limit
-----

Use the ``limit()`` method to cap the number of documents that can be returned from a read
operation. This method specifies the maximum number of documents that the operation can
return, but the operation can return a smaller number of documents if there are not enough
documents present to reach the limit. If ``limit()`` is used with the :ref:`skip()
<node-fundamentals-skip>` method, the skip applies first and the limit only applies to the
documents left over after the skip.

This example performs the following actions:

- Uses an empty query filter to match all documents in the collection
- Calls the ``sort()`` method to apply a descending sort on the ``length`` field to the results
- Calls the ``limit()`` method to return only the first ``3`` results

.. code-block:: javascript
:emphasize-lines: 5

// define an empty query document
const query = {};
// sort in descending (-1) order by length
const sort = { length: -1 };
const limit = 3;
const cursor = myColl.find(query).sort(sort).limit(limit);
for await (const doc of cursor) {
console.dir(doc);
}

The code example above outputs the following three documents, sorted by
length:

.. code-block:: json
:copyable: false

{ "_id": 2, "title": "Les Misérables", "author": "Hugo", "length": 1462 }
{ "_id": 6, "title": "A Dance With Dragons", "author": "Martin", "length": 1104 }
{ "_id": 4, "title": "Infinite Jest", "author": "Wallace", "length": 1104 }

.. note::

The order in which you call ``limit()`` and ``sort()`` does not matter because the
driver reorders the calls to apply the sort first. The following two calls are
equivalent:

.. code-block:: javascript

myColl.find(query).sort({ length: -1 }).limit(3);
myColl.find(query).limit(3).sort({ length: -1 });

You can also apply ``sort()`` and ``limit()`` by specifying them in an
``options`` object in your call to the ``find()`` method. The following two
calls are equivalent:

.. code-block:: javascript

myColl.find(query).sort({ length: -1 }).limit(3);
myColl.find(query, { sort: { length: -1 }, limit: 3 });

For more information on the ``options`` settings for the ``find()``
method, see the
`API documentation on find() <{+api+}/classes/Collection.html#find>`__.

.. _node-fundamentals-skip:

Skip
----

Use the ``skip()`` method to omit documents from the beginning of the read operation
results. You can combine ``skip()`` with
:ref:`sort() <node-fundamentals-sort>` to omit the top
(for descending order) or bottom (for ascending order) results for a
given query. Since the :manual:`order of documents returned
</reference/method/cursor.sort/#result-ordering>` is not guaranteed in
the absence of a sort, using ``skip()`` without using ``sort()`` omits
arbitrary documents.

If the value of ``skip()`` exceeds the number of matched documents for
a query, then that query returns no documents.

This example queries the collection for the books with the fifth and sixth highest lengths
by performing the following actions:

- Uses an empty query filter to match all documents in the collection
- Calls the ``sort()`` method to apply a descending sort to the ``length`` field, which returns longer books before shorter books
- Calls the ``skip()`` method to omit the first four matching documents from the result

.. code-block:: javascript

// define an empty query document
const query = {};
const sort = { length: -1 };
const skip = 4;
const cursor = myColl.find(query).sort(sort).skip(skip);
for await (const doc of cursor) {
console.dir(doc);
}

Since the query skips the first four matching documents, the preceding code snippet prints
the fifth and sixth highest length documents:

.. code-block:: json
:copyable: false

{ "_id": 5, "title": "Cryptonomicon", "author": "Stephenson", "length": 918 }
{ "_id": 1, "title": "The Brothers Karamazov", "author": "Dostoyevsky", "length": 824 }

You can also apply ``skip()`` and ``sort()`` by specifying them in an
``options`` object in your call to the ``find()`` method. The following two
calls are equivalent:

.. code-block:: javascript

myColl.find(query).sort({ length: -1 }).skip(4);
myColl.find(query, { sort: { length: -1 }, skip: 4});

For more information on the ``options`` settings for the ``find()``
method, see the
`API documentation on find() <{+api+}/classes/Collection.html#find>`__.

.. _node-fundamentals-combine-lim-sort-skip:

Combine Limit, Sort, and Skip
-----------------------------

You can combine the ``limit``, ``sort``, and ``skip`` options in a single
operation. This allows you to set a maximum number of sorted documents to
return, skipping a specified number of documents before returning.

The following example returns documents with the ``length`` value of
``"1104"``. The results are sorted in alphabetical order, skipping the first
document and includes only the first result:

.. code-block:: javascript

// define a query to look for length value of 1104
const query = {length: "1104"};
const options = {
// sort in alphabetical (1) order by title
sort : { title: 1 },
// omit the first document
skip : 1,
// returns only the first result
limit: 1,
}
const cursor = myColl.find(query, options);
for await (const doc of cursor) {
console.dir(doc);
}

.. code-block:: json
:copyable: false

{ "_id": 4, "title": "Infinite Jest", "author": "Wallace", "length": 1104 }

.. note::

The order in which you call these methods doesn't change the documents
that are returned. The driver automatically reorders the calls to perform the
sort and skip operations first, and the limit operation afterward.

You can also limit, sort, and skip results by chaining each method to the ``find`` method.
The following example specifies the same query as the preceding example:

.. io-code-block::

.. input::
:language: javascript

myColl.find(query).sort({ title: 1 }).skip(1).limit(1);

.. output::
:language: json
:visible: false

{ "_id": 4, "title": "Infinite Jest", "author": "Wallace", "length": 1104 }

Additional Information
----------------------

For more information about specifying a query, see :ref:`node-query`.

For more information about retrieving documents, see :ref:`node-fundamentals-retrieve-data`.

API Documentation
~~~~~~~~~~~~~~~~~

To learn more about any of the methods discussed in this
guide, see the following API documentation:

- `find() <{+api+}/classes/Collection.html#find>`__
- `limit() <{+api+}/classes/FindCursor.html#limit>`__
- `sort() <{+api+}/classes/FindCursor.html#sort>`__
- `skip() <{+api+}/classes/FindCursor.html#skip>`__
2 changes: 1 addition & 1 deletion source/includes/access-cursor-note.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
Your query operation may return a reference to a
cursor that contains matching documents. To learn how to
examine data stored in the cursor, see the
:doc:`Cursor Fundamentals page </fundamentals/crud/read-operations/cursor>`.
:doc:`Access Data From a Cursor page </crud/query/cursor>`.
Loading