Mongo 聚合游标和计数

Posted

技术标签:

【中文标题】Mongo 聚合游标和计数【英文标题】:Mongo aggregation cursor & counting 【发布时间】:2014-10-04 01:16:29 【问题描述】:

根据mongodb node driver docs,聚合函数现在返回一个游标(从 2.6 开始)。

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

我看到创建的游标有一个 on data 事件(这是否意味着它是一个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 data 事件存在:

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

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

编辑 2:返回的光标似乎是 aggregation cursor,而不是文档中列出的光标之一

【问题讨论】:

您能否向我们展示一些无法按预期工作的代码?在cursors 上定义的count() 函数是否不起作用?请注意,Node 驱动程序中的聚合框架只有在您设置游标选项时才会返回游标。 我在 mongo shell 中添加了一些代码示例(它可以按照我的方式工作)并使用 nodejs 驱动程序 我answered这个重复的问题,但Martijn Pieters指出is bad etiquette to answer duplicated questions,并继续删除我的答案,所以去这个链接:***.com/a/60534958/218418我没有时间用于标记重复和该死的戏剧。 这能回答你的问题吗? How to get the length of a cursor from mongodb using python? 【参考方案1】:

这可能值得那些可能搜索它的人完整解释,因此为后代添加一个。

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

类似于游标对象可用的 .stream() 方法返回的结果,当您考虑实现时,“计数”在这里没有多大意义,因为它意味着作为最终您所在的“流”处理会到达一个“终点”,但除此之外只想处理直到到达那里。

如果您从驱动程序中考虑标准的“光标”接口,聚合光标不一样的原因有很多:

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

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

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

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

虽然聚合框架有点使用这个概念。因为不仅有与通过标准查询分析器处理的结果相同的结果,而且还有其他阶段。这些阶段中的任何一个都有可能“修改”结果“计数”,这些“计数”实际上会在要处理的“流”中返回。

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

最后一个论点是合理的,但也要考虑到目前为聚合管道结果实现“游标”概念对于 MongoDB 来说是一个新概念。可以公平地说,在第一个设计点的所有“合理”期望都是组合文档的“大多数”结果不会具有限制 BSON 限制的大小。但随着使用范围的扩大,认知会发生变化,事情也会随之改变。

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

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

【讨论】:

感谢详细解释 也感谢您的详细解释!暂时忽略实现细节,从我的角度来看,最好的接口是聚合游标和查询游标具有相同的方法。并不是说我介意流式传输和手动计数,而是对某些游标而不是其他游标进行一些更改,尤其是在游标计算期间可能会缓存计数时。说真的,感谢您抽出宝贵时间彻底回答这个问题。

以上是关于Mongo 聚合游标和计数的主要内容,如果未能解决你的问题,请参考以下文章

Mongo 聚合计数子文档(计数回复评论)

Mongo 不同的计数和位置

为啥mongo计数不加起来?

Mongo中的分箱和制表(唯一/计数)

查找 mongo 文档计数的最佳做法是啥?

PL/SQL 游标:将计数提取为整数,语法错误?