这一直困扰着我一段时间。对于大多数情况,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()
的作用。而且我不会直接引用一段文件,而是" sic社论":
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存储引擎如何在结果中处理这种聚合工作,那么它只是有意义的#34;以有序的关键方式返回。
真的,"这里有什么问题?"。作为回答许多社区问题的常客,我可以说"并且具有相当大的权限,一般的期望是应该通过"分组键"来排序。正如人们合理期望的那样。此外,还有一些常见的用例需要进一步分析"聚合结果在"系列"因为他们应该自然发生。一个常见的情况是"分析每个聚合结果之间的差异"例如"determining the difference between each average on days"。只是一个例子,但类似的事情经常被问到。
我们大多数人(至少是那些有经验的人)都非常了解$sort
聚合管道阶段。但我认为,本着这里真正被问到的精神是"为什么我们必须这样做?"。
通过.mapReduce()
的原始聚合选项就像人们期望的那样。那么.aggregate()
为什么不以同样的方式去做呢?
这是否有JIRA?是否有实际行动可以采取行动?
将$sort
作为附加阶段应用的当前解决方法实际上非常“非常”#34;我认为使用该产品的社区应该比这更好。任何"聚合的预期行为"操作是"键"应该在结果中订购。那么为什么我们不能这样做以及目前正在做些什么来解决这个问题?
如果没有立竿见影的努力,那将是一种真正的耻辱,因为这会削弱一种非常有用的工具"当人们将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在预分组密钥时所做的不同方法。有任何好处或具体原因吗?或者可以做得更好。
答案 0 :(得分:7)
我真的不认为MongoDB应该默认排序结果。如果您需要按特定顺序排列文档,则应始终对其进行排序。
通过.mapReduce()的原始聚合选项就像人们期望的那样
我不确定人们是否期望这样 - 除非我特别要求,否则我当然不希望事情按某种顺序排列。如果它们恰好是某种顺序,那么它就是一个实现细节,你通常不应该依赖它。
如果您认为它有用,请打开一个关于它的Jira错误,但我个人认为没有必要。如果用户可以撰写$group
,那么他们也可以找出$sort
。
答案 1 :(得分:1)
我尝试了与你相同的插入,并在MongoDB版本3.4.4上得到了与你相同的结果!感兴趣并尝试了更多,如下所述,
db.example.insert([
{ "field" : "A", "value" : 1 },
{ "field" : "B", "value" : 2 },
{ "field" : "C", "value" : 3 },
{ "field" : "D", "value" : 4 },
{ "field" : "E", "value" : 5 },
{ "field" : "A", "value" : 6 },
{ "field" : "B", "value" : 7 },
{ "field" : "C", "value" : 8 },
{ "field" : "D", "value" : 9 },
{ "field" : "E", "value" : 10 },
])
收集到位后,运行相同的聚合操作:
db.example.aggregate([
{ "$group": {
"_id": "$field",
"value": { "$sum": "$value" }
}}
])
它返回的结果如下:
[
{ "_id" : "E", "value" : 15 },
{ "_id" : "D", "value" : 13 },
{ "_id" : "C", "value" : 11 },
{ "_id" : "A", "value" : 7 },
{ "_id" : "B", "value" : 9 }
]
聚合输出似乎没有遵循您提到的顺序,即。相反的发现顺序。如果确实如此,那么聚合的结果不应该是E,D,C,B,A而不是E,D,C,A,B?
删除了这些内容并使用不同的文档再次尝试
db.example.insert([
{ "field" : "A", "value" : 1 },
{ "field" : "B", "value" : 2 },
{ "field" : "C", "value" : 3 },
{ "field" : "D", "value" : 4 },
{ "field" : "E", "value" : 5 },
{ "field" : "E", "value" : 6 },
{ "field" : "D", "value" : 7 },
{ "field" : "C", "value" : 8 },
{ "field" : "B", "value" : 9 },
{ "field" : "A", "value" : 10 },
])
执行相同的聚合操作:
db.example.aggregate([
{ "$group": {
"_id": "$field",
"value": { "$sum": "$value" }
}}
])
返回的结果遵循与上述相同的顺序,即。 E,d,C,A,B:
[
{ "_id" : "E", "value" : 11 },
{ "_id" : "D", "value" : 11 },
{ "_id" : "C", "value" : 11 },
{ "_id" : "A", "value" : 11 },
{ "_id" : "B", "value" : 11 }
]
总体而言,在上述情景中,没有“反转”的情况。结果中的关键顺序。