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 聚合游标和计数的主要内容,如果未能解决你的问题,请参考以下文章