Overview
返回多个文档的读取操作不会立即返回与查询匹配的所有值。由于查询可能匹配数量极大的文档集,因此这些操作会返回一个称为游标的对象,该对象会引用查询所标识的文档。游标会分批获取文档,以减少内存消耗和网络带宽占用。游标具有高度可配置性,并为不同的使用案例提供多种交互范例。
以下函数会直接返回游标:
Collection.find()Collection.aggregate()Collection.listIndexes()Collection.listSearchIndexes()Db.aggregate()Db.listCollections()
其他方法,例如 Collection.findOne() 和 Collection.watch()在内部使用游标,并返回操作结果而不是游标。
游标范例
您可以使用几种不同的游标范例来访问数据。大多数游标范例允许一次访问一个文档的查询结果,对网络和缓存逻辑抽象化。然而,由于使用案例不同,其他范例会提供不同的访问模式,例如将所有匹配的文档拉入进程内存中的集合。
警告
不要在单个游标上组合不同的游标范例。不要在单个游标上组合不同的游标范例。对于 hasNext() 和 toArray() 等操作,每个操作都会对原始游标进行可预测的修改。如果您在单个游标上结合使用这些调用,您可能会收到意外的结果。
警告
由于异步调用会直接修改游标,因此在同一个游标上同时执行多个异步调用会导致未定义的行为。请始终等待前一个异步操作完成后,再执行下一个异步操作。
注意
当您通过迭代或一次性获取到达最后一个结果时,游标已耗尽,这意味着它将停止响应访问权限结果的方法。
异步迭代
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); }
手动遍历
您可以使用 hasNext() 方法检查游标能否检索更多数据,然后使用 next() 方法检索游标的后续元素:
const cursor = myColl.find({}); while (await cursor.hasNext()) { console.log(await cursor.next()); }
返回所有文档的数组
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
游标会公开 stream() 方法以将其转换为节点可读流。这些流在对象模式下运行,该模式通过管道传递 JavaScript 对象,而不是传递缓冲区或字符串。
const cursor = myColl.find({}); cursor.stream().on("data", doc => console.log(doc));
事件 API
作为可读流,游标还支持 Event API 的 close、data、end 和 readable 事件:
const cursor = myColl.find({}); // the "data" event is fired once per document cursor.on("data", data => console.log(data));
游标实用程序方法
Rewind
要将游标重置为所返回文档集中的初始位置,请使用 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);
关闭
游标会消耗客户端应用程序和连接的MongoDB实例中的内存和网络资源。使用 close() 在客户端应用程序和MongoDB Server中释放游标的资源,如以下示例所示:
await cursor.close();
我们建议手动关闭应用应用程序不再使用的所有未耗尽游标,以防止资源泄漏。
提示
显式资源管理
Node.js驾驶员本身支持对MongoClient 、ClientSession 、ChangeStreams 和游标进行显式资源管理。此功能为实验性功能,可能会发生更改。要学习;了解如何使用显式资源管理,请参阅 v.6 9发布说明。
中止
您可以使用中止信号取消游标操作。如果不再需要光标使用的内存和网络资源,这可以释放这些资源,从而有助于管理资源。
注意
此功能是实验性的。中止信号会关闭连接,这可能会导致不必要的连接重建。
您可以将 signal 命令传递给以下方法:
collection.find()collection.findOne()collection.aggregate()collection.countDocuments()db.listCollections()db.command()
要使用中止信号,请创建 AbortController 实例并从控制器中提取 signal。在此代码示例中,进程侦听 SIGINT (Ctrl+C) 以触发 abort() 方法。您可以将 signal 选项传递给 find() 方法,以便在信号触发时中止游标操作,如下例所示:
const controller = new AbortController(); const { signal } = controller; process.on('SIGINT', () => controller.abort(new Error('^C pressed'))); try { const cursor = myColl.find({}, { signal }); for await (const doc of cursor) { console.log(doc); } } catch (error) { if (error === signal.reason) { console.error('Operation aborted:', error); } else { console.error('Unexpected error:', error); } }