【发布时间】:2015-05-05 11:49:05
【问题描述】:
这已经困扰我一段时间了。 MongoDB 的aggregation framework 是一个很棒的工具,并且在大多数情况下通常比.mapReduce() 更适合使用,除非后者实际上更适合。
与 JavaScript 解释相比,它当然确实使用在本机 C++ 编译中实现的方法实际执行操作,因此在大多数情况下比 mapReduce 替代方案“更快”。
但这里的主要问题是“结果中的'反转'键顺序给出了什么?”。如果并非总是如此,那么至少在过去几个主要版本中都是如此(这里没有真正测试每个版本,在撰写本文时只有 2.6.x 和 3.x 候选版本)。但它总是处于“反向”状态,这似乎真的违反直觉,我将在一个例子中给出。
以基本集合为例:
db.example.insert([
{ "field": "A", "value": 1 },
{ "field": "A", "value": 2 },
{ "field": "B", "value": 3 },
{ "field": "B", "value": 4 },
{ "field": "C", "value": 5 },
{ "field": "C", "value": 6 }
])
一旦集合到位,当您想要运行这样的示例聚合操作时:
db.example.aggregate([
{ "$group": {
"_id": "$field",
"value": { "$sum": "$value" }
}}
])
那么返回的结果总会像这样神秘地返回:
[
{ "_id" : "C", "value" : 11 },
{ "_id" : "B", "value" : 7 },
{ "_id" : "A", "value" : 3 }
]
这将始终保持一致,无论实际文档的插入顺序是什么,键都将“始终”以“相反的顺序”产生。
另一方面,现在让我们考虑一下.mapReduce() 做了什么。而且我不会直接引用一段文档,而是“原文社论”:
MapReduce 将始终将在处理之前发出的键排序到“reduce”函数作为一般优化。
或者基本上是这样说的。所以下面的代码:
db.example.mapReduce(
function() {
emit( this.field, this.value );
},
function(key,values) {
return Array.sum( values );
},
{ "out": { "inline": 1 } }
)
产生这种结果,并且符合记录行为的一般前提:
{
"results" : [
{
"_id" : "A",
"value" : 3
},
{
"_id" : "B",
"value" : 7
},
{
"_id" : "C",
"value" : 11
}
],
"timeMillis" : 231,
"counts" : {
"input" : 6,
"emit" : 6,
"reduce" : 3,
"output" : 3
},
"ok" : 1
}
现在,这当然是按照指定的分组键的自然“升序”顺序排序的,并且完全按照合理记录的方式进行排序。此外,考虑到大多数 SQL 存储引擎如何在结果中处理这种聚合工作,那么以有序键的方式返回就“有意义”了。
真的,“这里有什么问题?”。作为回答许多社区问题的频繁贡献者,我“可以说”并且具有相当大的权威,一般期望结果应该像人们一样按“分组键”排序合理预期。此外,还有一些常见用例希望进一步“分析”“系列”中的聚合结果,因为它们应该自然发生。一个常见的情况是“分析每个聚合结果之间的差异”,例如"determining the difference between each average on days"。只是一个例子,但类似的事情经常被问到。
我们中的大多数人(至少有一些经验的人)都非常了解$sort 聚合管道阶段。但我认为,这里真正要问的是“我们为什么要这样做?”。
.mapReduce() 的原始聚合选项就像人们期望的那样。那么为什么.aggregate() 不这样做呢?
这上面有JIRA 吗?有什么实际行动可以解决吗?
当前将$sort 作为附加阶段的解决方法确实非常“粗鲁”,我认为使用该产品的社区应该得到比这更好的东西。任何“聚合”操作的预期行为是“键”应在结果中排序。那么为什么我们不能这样做呢?目前正在采取什么措施来解决这个问题?
如果不立即努力,那真是太可惜了,因为这会削弱人们在考虑将 MongoDB 作为其应用程序的存储解决方案时应该涌向的“非常有用的工具”。
我希望我们能朝着更好的方向努力。
再澄清一点。值得注意的是,聚合输出没有特别排序,但它确实出现在密钥的“发现顺序”中,但当然是相反的。举个例子:
db.example.insert([
{ "field": "B", "value": 4 },
{ "field": "A", "value": 1 },
{ "field": "B", "value": 3 },
{ "field": "C", "value": 5 },
{ "field": "A", "value": 2 },
{ "field": "C", "value": 6 }
])
会产生:
{ "_id" : "C", "value" : 11 }
{ "_id" : "A", "value" : 3 }
{ "_id" : "B", "value" : 7 }
所以堆栈总是按照分组键被发现的顺序颠倒。
这就是问题所在,堆栈总是颠倒的,以及为什么 mapReduce 在对分组键进行预排序时采用不同的方法。有什么好处或具体原因吗?或者可以做得更好。
【问题讨论】:
标签: mongodb mongodb-query aggregation-framework