从游标访问数据
概述
返回多个文档的读取操作不会立即返回所有匹配查询的值。因为一个查询可能匹配非常大的文档集,这些操作返回一个称为光标的对象,该对象引用由查询标识的文档。光标以批量方式检索文档,以减少内存消耗和网络带宽使用。光标高度可配置,并为不同的用例提供多种交互范式。
以下函数直接返回光标:
Collection.find()
Collection.aggregate()
Collection.listIndexes()
Collection.listSearchIndexes()
Db.aggregate()
Db.listCollections()
其他方法,如Collection.findOne() 和 Collection.watch() 在内部使用光标,并返回操作的结果而不是光标。
光标范式
您可以使用几种不同的 光标范式 来访问数据。大多数光标范式允许您逐个访问查询结果,抽象出网络和缓存逻辑。然而,由于用例不同,其他范式提供了不同的访问模式,例如将所有匹配的文档拉入进程内存中的集合。
警告
不要在单个光标上组合不同的光标范式。例如 hasNext()
和 toArray()
这样的操作会按照预期修改原始光标。如果在单个光标上混合这些调用,可能会收到意外结果。
警告
由于异步调用直接修改光标,同时在一个光标上执行异步调用也可能导致未定义的行为。始终在运行另一个之前等待前一个异步操作完成。
注意
在通过迭代或一次性获取到达最后一个结果时,光标耗尽,这意味着它不再响应对结果访问的方法。
异步迭代
游标实现了AsyncIterator 接口,该接口允许你在 for await...of
循环中使用游标
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()); }
返回所有文档的数组
对于需要同时将所有与查询匹配的文档保留在内存中的用例,请使用 toArray() 方法。请注意,大量匹配的文档可能会导致性能问题或失败,如果操作超出内存限制。考虑使用 for await...of
语法来遍历结果,而不是一次性返回所有文档。
const cursor = myColl.find({}); const allValues = await cursor.toArray();
流API
游标公开了 stream()
方法,将其转换为 Node 可读流。这些流在 对象模式 下操作,通过管道传递 JavaScript 对象,而不是缓冲区或字符串。
const cursor = myColl.find({}); cursor.stream().on("data", doc => console.log(doc));
事件API
作为可读流,游标也支持事件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().
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服务器上的资源
await cursor.close();