|
1 |
| -.. _rust-cursor-guide: |
| 1 | +.. _rust-cursor-guide: |
| 2 | + |
| 3 | +============================= |
| 4 | +Access Data by Using a Cursor |
| 5 | +============================= |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 2 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | +Overview |
| 14 | +-------- |
| 15 | + |
| 16 | +In this guide, you can learn how to use the {+driver-short+} to access |
| 17 | +data returned from a read operation or aggregation by using a **cursor**. A |
| 18 | +cursor is a mechanism that enables you to iterate through multiple documents |
| 19 | +while holding only a subset of them in memory at a given time. |
| 20 | + |
| 21 | +The driver offers the ``Cursor`` type to retrieve documents from a cursor. |
| 22 | +For example, when you run a find operation that can return multiple |
| 23 | +documents, the driver returns a ``Cursor`` instance from which you can access |
| 24 | +the matched documents. |
| 25 | + |
| 26 | +After you run a read operation or aggregation, the returned ``Cursor`` instance |
| 27 | +contains the first batch of results from the operation. As you iterate through |
| 28 | +the cursor, the server returns more individual results. If there are more |
| 29 | +matching documents after you reach the end of a batch of results, the ``Cursor`` |
| 30 | +instance fetches the next batch of documents until all the results are returned. |
| 31 | + |
| 32 | +This guide includes the following sections: |
| 33 | + |
| 34 | +- :ref:`Retrieve Documents Individually <rust-cursor-individual>`: |
| 35 | + describes how to use iteration or a stream to access results one at a time |
| 36 | + |
| 37 | +- :ref:`Retrieve Documents as an Array <rust-cursor-array>`: |
| 38 | + describes how to access all results as a single array by collecting the |
| 39 | + returned cursor results |
| 40 | + |
| 41 | +- :ref:`Specify Cursor Behavior <rust-cursor-options>`: describes how |
| 42 | + to configure the cursor that a method returns |
| 43 | + |
| 44 | +- :ref:`Additional Information <rust-cursor-addtl-info>`: |
| 45 | + provides links to related topics and API documentation for types |
| 46 | + and methods mentioned in this guide |
| 47 | + |
| 48 | +.. _rust-cursor-individual: |
| 49 | + |
| 50 | +Retrieve Documents Individually |
| 51 | +------------------------------- |
| 52 | + |
| 53 | +The driver provides the following access patterns to iterate through |
| 54 | +documents returned by a ``Cursor`` instance: |
| 55 | + |
| 56 | +- :ref:`Built-in Pattern <rust-cursor-indiv-builtin>`: advance the cursor, |
| 57 | + then retrieve and deserialize the current document |
| 58 | +- :ref:`Stream Implementation Pattern <rust-cursor-indiv-stream>`: iterate over |
| 59 | + the cursor and call methods provided by ``Stream`` to process single |
| 60 | + or multiple documents |
| 61 | + |
| 62 | +The following sections describe these access patterns and corresponding |
| 63 | +methods in more detail. |
| 64 | + |
| 65 | +.. _rust-cursor-indiv-builtin: |
| 66 | + |
| 67 | +Built-in Pattern |
| 68 | +~~~~~~~~~~~~~~~~ |
| 69 | + |
| 70 | +You can use the driver's built-in access pattern to retrieve and process |
| 71 | +documents one by one. |
| 72 | + |
| 73 | +The ``Cursor`` type includes the ``advance()`` and |
| 74 | +``deserialize_current()`` methods to iterate through a cursor and |
| 75 | +access documents individually. |
| 76 | + |
| 77 | +The ``advance()`` method moves the cursor forward and sends a request to the |
| 78 | +database for more results when the local buffer is exhausted, which occurs |
| 79 | +when the cursor reaches the end of a batch of results. Each time the cursor |
| 80 | +reaches the end of a batch of results, it requests the next batch. The cursor |
| 81 | +is exhausted when it has no more matching documents to return and is no longer |
| 82 | +usable. The ``advance()`` method returns a ``true`` result if new results are |
| 83 | +successfully returned and a ``false`` result if the cursor is closed. |
| 84 | + |
| 85 | +The ``deserialize_current()`` method returns a reference to the current |
| 86 | +result in the cursor and deserializes the result into the type |
| 87 | +associated with the cursor. Unless you specify a type, the method uses the |
| 88 | +same type that your collection is parameterized with. |
| 89 | + |
| 90 | +.. important:: |
| 91 | + |
| 92 | + You can only call the ``deserialize_current()`` method if the ``advance()`` |
| 93 | + method returns a ``true`` result. The driver generates an error if you call |
| 94 | + ``deserialize_current()`` on the cursor without a ``true`` result or |
| 95 | + without calling previously calling ``advance()``. |
| 96 | + |
| 97 | +The following example shows how to implement this access pattern to |
| 98 | +iterate through the results of a find operation on the ``fruits`` |
| 99 | +collection: |
| 100 | + |
| 101 | +.. io-code-block:: |
| 102 | + :copyable: true |
| 103 | + |
| 104 | + .. input:: /includes/fundamentals/code-snippets/crud/cursor.rs |
| 105 | + :start-after: start-indiv-builtin |
| 106 | + :end-before: end-indiv-builtin |
| 107 | + :language: rust |
| 108 | + :dedent: |
| 109 | + |
| 110 | + .. output:: |
| 111 | + :language: console |
| 112 | + :visible: false |
| 113 | + |
| 114 | + Document({"_id": ObjectId("..."), "name": String("strawberry"), "color": String("red")}) |
| 115 | + Document({"_id": ObjectId("..."), "name": String("pomegranate"), "color": String("red")}) |
| 116 | + |
| 117 | +.. _rust-cursor-indiv-stream: |
| 118 | + |
| 119 | +Stream Implementation Pattern |
| 120 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 121 | + |
| 122 | +You can access cursor results as a stream to retrieve individual documents |
| 123 | +or collect multiple documents at once. |
| 124 | + |
| 125 | +The ``Cursor`` type implements the ``Stream`` trait, so you can iterate |
| 126 | +through the cursor as a stream. This pattern may help you write more |
| 127 | +concise code than the built-in pattern, since the ``Stream`` extension trait |
| 128 | +``StreamExt`` provides numerous functions to combine operations and |
| 129 | +consolidate code. |
| 130 | + |
| 131 | +You can use the following methods to use the stream pattern: |
| 132 | + |
| 133 | +- ``next()``: advances the cursor to the next result and returns an |
| 134 | + ``Option<Result<T>>`` type |
| 135 | +- ``try_next()``: advances the cursor to the next result and returns |
| 136 | + a ``Result<Option<T>>`` type |
| 137 | + |
| 138 | +.. important:: Required Imports for Stream Pattern Methods |
| 139 | + |
| 140 | + To use the ``next()`` method, you must import the ``StreamExt`` |
| 141 | + trait. To use the ``try_next()`` method, you must import the |
| 142 | + ``TryStreamExt`` trait. |
| 143 | + |
| 144 | +The following example shows how to implement the two stream methods to |
| 145 | +iterate through the results of find operations on the ``fruits`` |
| 146 | +collection: |
| 147 | + |
| 148 | +.. io-code-block:: |
| 149 | + :copyable: true |
| 150 | + |
| 151 | + .. input:: /includes/fundamentals/code-snippets/crud/cursor.rs |
| 152 | + :start-after: start-indiv-stream |
| 153 | + :end-before: end-indiv-stream |
| 154 | + :language: rust |
| 155 | + :dedent: |
| 156 | + |
| 157 | + .. output:: |
| 158 | + :language: console |
| 159 | + :visible: false |
| 160 | + |
| 161 | + Output from next() iteration: |
| 162 | + { "_id": ObjectId("..."), "name": "strawberry", "color": "red" } |
| 163 | + { "_id": ObjectId("..."), "name": "pomegranate", "color": "red" } |
| 164 | + |
| 165 | + Output from try_next() iteration: |
| 166 | + { "_id": ObjectId("..."), "name": "banana", "color": "yellow" } |
| 167 | + { "_id": ObjectId("..."), "name": "pineapple", "color": "yellow" } |
| 168 | + |
| 169 | +.. _rust-cursor-array: |
| 170 | + |
| 171 | +Retrieve Documents as an Array |
| 172 | +------------------------------ |
| 173 | + |
| 174 | +Because the ``Cursor`` type implements the ``Stream`` trait, you can |
| 175 | +collect the results from a cursor into an array. |
| 176 | + |
| 177 | +You can use the following methods to retrieve documents as an array: |
| 178 | + |
| 179 | +- ``collect()``: collects results from a cursor into a |
| 180 | + ``Vec<Result<T>>`` type |
| 181 | +- ``try_collect()``: collects results from a cursor into a |
| 182 | + ``Result<Vec<T>>`` type |
| 183 | + |
| 184 | +.. note:: |
| 185 | + |
| 186 | + To use the ``collect()`` method, you must import the ``StreamExt`` |
| 187 | + trait. To use the ``try_collect()`` method, you must import the |
| 188 | + ``TryStreamExt`` trait. |
| 189 | + |
| 190 | +.. io-code-block:: |
| 191 | + :copyable: true |
| 192 | + |
| 193 | + .. input:: /includes/fundamentals/code-snippets/crud/cursor.rs |
| 194 | + :start-after: start-array |
| 195 | + :end-before: end-array |
| 196 | + :language: rust |
| 197 | + :dedent: |
| 198 | + |
| 199 | + .. output:: |
| 200 | + :language: console |
| 201 | + :visible: false |
| 202 | + |
| 203 | + Output from collect(): |
| 204 | + [Ok(Document({"_id": ObjectId("..."), "name": String("strawberry"), "color": String("red")})), Ok(Document({"_id": ObjectId("..."), "name": String("pomegranate"), "color": String("red")}))] |
| 205 | + |
| 206 | + Output from try_collect(): |
| 207 | + [Document({"_id": ObjectId("..."), "name": String("banana"), "color": String("yellow")}), Document({"_id": ObjectId("..."), "name": String("pineapple"), "color": String("yellow")})] |
| 208 | + |
| 209 | +.. warning:: Avoid Exceeding Application Memory Limits |
| 210 | + |
| 211 | + Avoid converting large sets of results to arrays. If the array exceeds |
| 212 | + the size of available application memory, your application might crash. |
| 213 | + If you expect a large result set, retrieve documents from the cursor individually. |
| 214 | + To learn how to iterate through the cursor, see the :ref:`Retrieve Documents Individually |
| 215 | + <rust-cursor-individual>` section of this guide. |
| 216 | + |
| 217 | +.. _rust-cursor-options: |
| 218 | + |
| 219 | +Specify Cursor Behavior |
| 220 | +----------------------- |
| 221 | + |
| 222 | +To modify the cursor that an operation returns, pass options to |
| 223 | +the method that returns the ``Cursor`` instance. For example, you can |
| 224 | +specify cursor-related options in a ``FindOptions`` type that you pass to the |
| 225 | +``find()`` method. |
| 226 | + |
| 227 | +The following table describes cursor-related options that you can set in |
| 228 | +an options instance: |
| 229 | + |
| 230 | +.. list-table:: |
| 231 | + :widths: 30 70 |
| 232 | + :header-rows: 1 |
| 233 | + |
| 234 | + * - Setting |
| 235 | + - Description |
| 236 | + |
| 237 | + * - ``batch_size`` |
| 238 | + - | Specifies the maximum number of documents the server returns per |
| 239 | + cursor batch. This option sets the number of documents the cursor |
| 240 | + keeps in memory rather than the number of documents the cursor |
| 241 | + returns. |
| 242 | + |
| 243 | + | Type: ``u32`` |
| 244 | + | Default: ``101`` documents initially, ``16 MB`` maximum for |
| 245 | + subsequent batches |
| 246 | + |
| 247 | + * - ``cursor_type`` |
| 248 | + - | Specifies the type of cursor to return. You can set this option |
| 249 | + to produce a tailable cursor. To learn more about tailable |
| 250 | + cursors, see :manual:`Tailable Cursors |
| 251 | + </core/tailable-cursors/>` in the Server manual. |
| 252 | + |
| 253 | + | Type: ``CursorType`` |
| 254 | + | Default: ``CursorType::NonTailable`` |
| 255 | + |
| 256 | + * - ``no_cursor_timeout`` |
| 257 | + - | Specifies whether the server closes the cursor after a period |
| 258 | + of inactivity. |
| 259 | + |
| 260 | + .. important:: |
| 261 | + |
| 262 | + Because the ``Cursor`` type implements the ``Drop`` trait, the |
| 263 | + server closes a cursor when it goes out of scope. The server |
| 264 | + runs an asynchronous ``killCursors`` command to close the |
| 265 | + cursor. See :manual:`killCursors </reference/command/killCursors/>` |
| 266 | + in the Server manual to learn more. |
| 267 | + |
| 268 | + | Type: ``bool`` |
| 269 | + | Default: ``false`` |
| 270 | + |
| 271 | +The following code shows how to construct a ``FindOptions`` |
| 272 | +instance and specify cursor-related settings: |
| 273 | + |
| 274 | +.. literalinclude:: /includes/fundamentals/code-snippets/crud/cursor.rs |
| 275 | + :start-after: start-options |
| 276 | + :end-before: end-options |
| 277 | + :language: rust |
| 278 | + :copyable: |
| 279 | + :dedent: |
| 280 | + |
| 281 | +.. _rust-cursor-addtl-info: |
| 282 | + |
| 283 | +Additional Information |
| 284 | +---------------------- |
| 285 | + |
| 286 | +To learn more about the operations in this guide, see the |
| 287 | +following Rust driver reference documentation: |
| 288 | + |
| 289 | +- :ref:`rust-retrieve-guide` |
| 290 | +- :ref:`rust-query-guide` |
| 291 | + |
| 292 | +.. TODO link to serialization guide |
| 293 | + |
| 294 | +API Documentation |
| 295 | +~~~~~~~~~~~~~~~~~ |
| 296 | + |
| 297 | +To learn more about the methods and types mentioned in this |
| 298 | +guide, see the following API documentation: |
| 299 | + |
| 300 | +- `Cursor <{+api+}/struct.Cursor.html>`__ |
| 301 | +- `advance() <{+api+}/struct.Cursor.html#method.advance>`__ |
| 302 | +- `deserialize_current() <{+api+}/struct.Cursor.html#method.deserialize_current>`__ |
| 303 | +- `next() <https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.next>`__ in the |
| 304 | + ``StreamExt`` trait |
| 305 | +- `try_next() <https://docs.rs/futures/latest/futures/stream/trait.TryStreamExt.html#method.try_next>`__ in the |
| 306 | + ``TryStreamExt`` trait |
| 307 | +- `collect() <https://docs.rs/futures/latest/futures/stream/trait.StreamExt.html#method.collect>`__ in the |
| 308 | + ``StreamExt`` trait |
| 309 | +- `try_collect() <https://docs.rs/futures/latest/futures/stream/trait.TryStreamExt.html#method.try_collect>`__ in the |
| 310 | + ``TryStreamExt`` trait |
| 311 | +- `find() <{+api+}/struct.Collection.html#method.find>`__ |
| 312 | +- `FindOptions <{+api+}/options/struct.FindOptions.html>`__ |
0 commit comments