这是预期的行为。驱动程序返回循环中的项目,然后在最后返回null 表示没有剩余项目。您也可以在驱动程序的examples 中看到这一点:
// Find returns a Cursor, which is Enumerable. You can iterate:
collection.find().each(function(err, item) {
if(item != null) console.dir(item);
});
如果您对详细信息感兴趣,可以查看source code for each:
if(this.items.length > 0) {
// Trampoline all the entries
while(fn = loop(self, callback)) fn(self, callback);
// Call each again
self.each(callback);
} else {
self.nextObject(function(err, item) {
if(err) {
self.state = Cursor.CLOSED;
return callback(utils.toError(err), item);
}
>> if(item == null) return callback(null, null); <<
callback(null, item);
self.each(callback);
})
}
在此代码中,each 使用 loop 遍历项目,这会将项目从数组 (var doc = self.items.shift();) 中移出。当this.items.length 变为0 时,执行else 块。这个 else 块尝试从光标处获取下一个文档。如果没有更多文档,nextObject 返回null(item 的值变为null)这使得if(item == null) return callback(null, null); 被执行。如您所见,回调是使用null 调用的,这就是您可以在控制台中看到的null。
这是必需的,因为 MongoDB 使用 cursor 返回匹配的文档。如果集合中有数百万个文档并且您运行 find(),则不会立即返回所有文档,因为您会耗尽内存。相反,MongoDB 使用游标遍历项目。 “对于大多数查询,第一批返回 101 个文档或刚好超过 1 兆字节的文档。”所以this.items.length 成为第一批中的项目数,但这不一定是查询结果的文档总数。这就是为什么当您遍历文档并且this.items.length 变为0 时,MongoDB 使用游标检查是否有更多匹配的文档。如果有,则加载下一批,否则返回null。
如果您使用较大的限制,则更容易理解这一点。例如,在limit(100000) 的情况下,如果 MongoDB 立即返回所有 100000 个文档,您将需要大量内存。更不用说处理速度有多慢了。相反,MongoDB 分批返回结果。假设第一批包含 101 个文档。然后this.items.length变成101,但这只是第一批的大小,不是结果的总数。当您遍历结果并到达当前批次中最后一个项目之后的下一个项目(在本例中为第 102 个)时,MongoDB 使用游标检查是否有更多匹配的文档。如果有,则加载下一批文档,否则null。
但是您不必在代码中使用nextObject(),您只需检查null,就像在MongoDB 示例中一样。