从游标访问数据
Overview
返回多个文档的读取操作不会立即返回与查询匹配的所有值。由于查询可能会匹配到数量非常多的文档集,因此这些操作将依赖于一个称为游标的对象。游标会分批获取文档,以减少内存消耗和网络带宽占用。游标高度可配置,并为不同的应用场景提供了多种交互范例。
以下函数会直接返回游标:
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();
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 服务器中的游标资源:
await cursor.close();