【问题标题】:MongoDB Node.js each methodMongoDB Node.js 的每个方法
【发布时间】:2014-06-27 20:25:07
【问题描述】:

我有一组数据,我将存储在数据库中。当我查看数据是否已经存在时,each() 将调用两次,即使我使用的是 limit(1)。我不知道这里发生了什么......

collection.find({
    month: 'april'
}).limit(1).count(function(err, result){
    console.log('counter', result);
});

collection.find({
    month: 'april'
}).limit(1).each(function(err, result){
    console.log('each', result);
});

collection.find({
    month: 'april'
}).limit(1).toArray(function(err, result){
    console.log('toArray', result);
});

此时,集合中已经存储了 4 月的确切 1 个数据集。 上述查询将生成如下输出:

count 1
each {...}
each null
toArray {...}

在 mongo shell 中,我检查了 count() 和 forEach() 方法。一切都按预期工作。是驱动问题吗?我做错什么了吗?

【问题讨论】:

  • 您能否在每次迭代中使用 console.log err 变量,看看第二次通过时是什么?

标签: node.js mongodb limit each


【解决方案1】:

这是预期的行为。驱动程序返回循环中的项目,然后在最后返回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 返回nullitem 的值变为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 示例中一样。

【讨论】:

  • 但是为什么当 this.items.length == 0 时需要检查 nextObject 呢?这应该暗示 item == null,不是吗?
  • 扩展了我的答案。只有当结果包含大量文档时才有意义。在limit(1) 的情况下,很容易看出没有更多项目。但是each 必须保持一致,这就是为什么它总是在最后一项之后返回null
  • 好的,我现在明白了,谢谢。我猜,each() 不是检查文档是否已经存在的正确函数,对吧?这就是 findOne() 存在的原因?在检查 null 的情况下,我会得到错误的结果,因为它返回 null,如果该集合中还没有匹配的文档并且它返回 null,如果文档存在并且已经到达集合的末尾。
  • each() 用于遍历find() 返回的项目。当您希望查询返回更多文档时,它很有用。如果你只是想检查一个文档是否存在,那么使用findOne() 是最简单的。如果项目不存在,则返回 null。您可以使用这样的代码:collection.findOne({}, function(err, item) { if (!err &amp;&amp; item != null) { // exists } }
猜你喜欢
  • 2015-11-18
  • 2018-09-18
  • 2015-01-08
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
  • 2018-12-13
  • 2013-06-11
  • 2015-06-22
相关资源
最近更新 更多