为什么.aggregate()以相反的顺序返回键

时间:2015-03-04 13:08:25

标签: mongodb mongodb-query aggregation-framework

这一直困扰着我一段时间。对于大多数情况,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在预分组密钥时所做的不同方法。有任何好处或具体原因吗?或者可以做得更好。

2 个答案:

答案 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 }
]

总体而言,在上述情景中,没有“反转”的情况。结果中的关键顺序。