问题 Mongo聚合游标和计数


根据 mongodb节点驱动程序文档 aggregate函数现在返回一个游标(从2.6开始)。

我希望我可以使用它来获取前限和跳过的项目数,但似乎没有任何计数功能在创建的光标上。如果我在mongo shell中运行相同的查询,则游标有一个itcount函数,我可以调用它来获取我想要的内容。

我看到创建的游标有一个on数据事件(这是否意味着它是一个 CursorStream?)似乎触发了预期的次数,但如果我将它与cursor.get一起使用,则没有结果传递给回调函数。

新光标功能是否可用于计算聚合查询?

编辑代码:

在mongo shell中:

> db.SentMessages.find({Type : 'Foo'})
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19dd9834184ad6d3675c"), "Name" : "789", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19d29834184ad6d3675b"), "Name" : "456", "Type" : "Foo" }

> db.SentMessages.find({Type : 'Foo'}).count()
3

> db.SentMessages.find({Type : 'Foo'}).limit(1)
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }

> db.SentMessages.find({Type : 'Foo'}).limit(1).count();
3

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ])
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19dd9834184ad6d3675c"), "Name" : "789", "Type" : "Foo" }
{ "_id" : ObjectId("53ea19d29834184ad6d3675b"), "Name" : "456", "Type" : "Foo" }

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ]).count()
2014-08-12T14:47:12.488+0100 TypeError: Object #<Object> has no method 'count'

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}} ]).itcount()
3

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}}, {$limit : 1} ])
{ "_id" : ObjectId("53ea19af9834184ad6d3675a"), "Name" : "123", "Type" : "Foo" }

> db.SentMessages.aggregate([ { $match : { Type : 'Foo'}}, {$limit : 1} ]).itcount()
1

> exit
bye

在节点中:

var cursor = collection.aggregate([ { $match : { Type : 'Foo'}}, {$limit : 1} ], { cursor : {}});

cursor.get(function(err, res){
  // res is as expected (1 doc)
});

cursor.count()不存在

cursor.itcount()不存在

on数据事件存在:

cursor.on('data', function(){
    totalItems++;
});

但是当与cursor.get结合使用时,.get回调函数现在包含0个文档

编辑2:返回的光标似乎是 聚合光标 而不是文档中列出的游标之一


2064
2017-08-11 08:58


起源

你能告诉我们一些代码没有像你期望的那样工作吗?是个 count() 函数定义 游标 不工作?请注意,节点驱动程序中的聚合框架仅在您设置游标选项时返回游标。 - wdberkeley
我在mongo shell中添加了一些代码示例(我的工作方式可以工作)并使用nodejs驱动程序 - Dan


答案:


对于那些可能会搜索这个的人来说,这可能值得一个完整的解释,所以为后代添加一个。

具体来说,返回的是node.js的一个事件流,它有效地包装了 stream.Readable 界面有几种方便的方法。一个 .count() 目前不是其中之一,考虑到目前使用的界面没有多大意义。

类似于从中返回的结果 .stream() 对于游标对象可用的方法,当你考虑实现时,“计数”在这里没有多大意义,因为它意味着作为“流”进行处理,最终你将达到“结束”但是只是想要处理直到那里

如果您考虑了驱动程序的标准“Cursor”接口,那么聚合游标不一样的原因有很多:

  1. 游标允许在执行之前处理“修饰符”动作。这些属于 .sort().limit() 和 .skip()。所有这些实际上都在聚合框架中具有在管道中指定的对应指令。作为可能出现在“任何地方”的管道阶段而不仅仅是简单查询的后处理选项,提供相同的“光标”处理没有多大意义。

  2. 其他光标修饰符包括特殊内容 .hint().min() 和 .max() 这是对“指数选择”和处理的改变。虽然这些可能对聚合管道有用,但目前还没有简单的方法将它们包含在查询选择中。大多数情况下,前一点的逻辑覆盖了为“光标”使用相同类型的接口的任何要点。

其他考虑因素是您实际想要使用游标以及为什么“想要”返回游标。由于光标通常是“单向行程”,因为它们通常只在到达终点并处于可用的“批次”中时才被处理,因此它得出一个合理的结论,即“计数”实际上是在最后,事实上,“队列”最终耗尽。

虽然事实上标准的“游标”实现确实存在一些技巧,但主要原因是这只是扩展了一个“元”数据概念,因为查询分析引擎必须“扫描”一定数量的文档才能确定哪个要在结果中返回的项目。

尽管如此,聚合框架仍然可以使用这个概念。由于不仅存在与通过标准查询分析器处理的结果相同的结果,而且还有其他阶段。任何这些阶段都有可能“修改”实际将在“流”中返回的“计数”以进行处理。

同样,如果你想从学术的角度来看这个并且说“当然,查询引擎应该保留计数的'元数据',但是我们不能跟踪之后被修改的内容吗?”。这将是一个公平的论点,和管道运营商如 $match 和 $group 要么 $unwind 甚至可能包括 $project 和新的 $redact,所有这些都可以被认为是在每个管道阶段保持自己对“已处理文档”的跟踪并在“元数据”中更新可能返回以解释完整管道结果计数的合理情况。

最后一个参数是合理的,但也要考虑到目前为聚合管道结果实现“光标”概念是MongoDB的一个新概念。可以相当地认为,在第一个设计点的所有“合理”期望可能是组合文件的“大多数”结果的大小不会限制BSON的限制。但随着使用量的增加,感知会发生变化,而事物也会随之改变。

所以这“可能”可能会改变,但不是“当前”实施的方式。而 .count() 在标准游标实现上可以访问记录扫描数量的“元数据”,当前实现的任何方法都将导致检索所有游标结果,就像 .itcount() 在shell中。

通过计算“data”事件处理“游标”项并在最后发出一些东西(可能是JSON流生成器)作为“count”。对于任何需要计数“预先”的用例,无论如何它似乎都不是游标的有效用途,因为输出肯定是合理大小的整个文档。


14
2017-08-19 06:42



谢谢你的详细解释 - Dan
感谢您的详细解释!暂时忽略实现细节,从我的角度来看,最好的接口是聚合游标和查询游标具有相同的方法。我并不介意流式传输和手动计数,而是为了某些游标而不是其他游标改变了一点流量,特别是在游标计算期间可能会缓存计数时。但说真的,谢谢花时间彻底回答这个问题。 - Owen