Access Data From a Cursor
On this page
Overview
Read operations that return multiple documents do not immediately return all values matching the query. Because a query can potentially match very large sets of documents, these operations rely upon an object called a cursor. A cursor fetches documents in batches to reduce both memory consumption and network bandwidth usage. Cursors are highly configurable and offer multiple interaction paradigms for different use cases.
The following functions directly return cursors:
Collection.find()
Collection.aggregate()
Collection.listIndexes()
Collection.listSearchIndexes()
Db.aggregate()
Db.listCollections()
Other methods such as Collection.findOne() and Collection.watch() use cursors internally, and return the results of the operations instead of a cursor.
Cursor Paradigms
You can work with cursors using a number of cursor paradigms. Most cursor paradigms allow you to access query results one document at a time, abstracting away network and caching logic. However, since use cases differ, other paradigms offer different access patterns, like pulling all matching documents into a collection in process memory.
Warning
Do not combine different cursor paradigms on a single cursor.
Operations such as hasNext()
and toArray()
each predictably modify the original cursor. If you mix these calls
on a single cursor, you may receive unexpected results.
Warning
Because asynchronous calls directly modify the cursor, executing asynchronous calls on a single cursor simultaneously can also cause undefined behavior. Always wait for the previous asynchronous operation to complete before running another.
Note
When you reach the last result through iteration or through an at-once fetch, the cursor is exhausted which means it ceases to respond to methods that access the results.
Asynchronous Iteration
Cursors implement the AsyncIterator interface, which
allows you to use cursors in for await...of
loops:
const cursor = myColl.find({}); console.log("async"); for await (const doc of cursor) { console.log(doc); }
Manual Iteration
You can use the hasNext() method to check if a cursor can provide additional data, and then use the next() method to retrieve the subsequent element of the cursor:
const cursor = myColl.find({}); while (await cursor.hasNext()) { console.log(await cursor.next()); }
Return an Array of All Documents
For use cases that require all documents matched by a query to be held
in memory at the same time, use the toArray()
method. Note that large numbers of matched documents can cause performance issues
or failures if the operation exceeds memory constraints. Consider using
the for await...of
syntax to iterate
through results rather than returning all documents at once.
const cursor = myColl.find({}); const allValues = await cursor.toArray();
Stream API
Cursors expose the stream()
method to convert them to Node Readable Streams. These streams operate in Object
Mode, which passes JavaScript objects rather than Buffers or Strings through the pipeline.
const cursor = myColl.find({}); cursor.stream().on("data", doc => console.log(doc));
Event API
As Readable Streams, cursors also support the Event API's
close
, data
, end
and readable
events:
const cursor = myColl.find({}); // the "data" event is fired once per document cursor.on("data", data => console.log(data));
Cursor Utility Methods
Rewind
To reset a cursor to its initial position in the set of returned documents, use rewind().
const cursor = myColl.find({}); const firstResult = await cursor.toArray(); console.log("First count: " + firstResult.length); await cursor.rewind(); const secondResult = await cursor.toArray(); console.log("Second count: " + secondResult.length);
Close
Cursors consume memory and network resources both in the client application and in the connected instance of MongoDB. Use close() to free up a cursor's resources in both the client application and the MongoDB server:
await cursor.close();