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

Nei*_*unn -8 mongodb mongodb-query aggregation-framework

这已经困扰了我一段时间了.MongoDB 的聚合框架是一个很好的工具,通常比.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 }
])
Run Code Online (Sandbox Code Playgroud)

一旦该集合到位,当您想要运行如下样本聚合操作时:

db.example.aggregate([
    { "$group": {
        "_id": "$field",
        "value": { "$sum": "$value" }
    }}
])
Run Code Online (Sandbox Code Playgroud)

然后返回的结果总是会像这样神秘地返回:

[
    { "_id" : "C", "value" : 11 },
    { "_id" : "B", "value" : 7 },
    { "_id" : "A", "value" : 3 }
]
Run Code Online (Sandbox Code Playgroud)

这将始终是一致的,并且无论实际文档的插入顺序是什么,密钥将"始终"以"相反顺序"生成.

另一方面,让我们考虑一下.mapReduce().而且我不打算直接引用一篇文档,而是"sic社论":

MapReduce将始终将处理前发出的键排序为"reduce"函数作为一般优化.

或者说基本上是这样的.所以下面的代码:

db.example.mapReduce(
    function() {
        emit( this.field, this.value );
    },
    function(key,values) {
        return Array.sum( values );
    },
    { "out": { "inline": 1 } }
)
Run Code Online (Sandbox Code Playgroud)

产生这种结果,并符合记录行为的一般前提:

{
    "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
}
Run Code Online (Sandbox Code Playgroud)

现在,这当然按照指定的分组键的自然"升序"顺序排序,并且完全按照合理记录的方式排序.此外,考虑到大多数SQL存储引擎如何处理结果中的这种聚合工作,那么以有序的密钥方式返回只是"有意义".

真的,"这里有什么问题?".作为回答许多社区问题的常见贡献者,我"可以说"并且具有相当大的权威性,一般的期望是结果应该按照人们合理期望的"分组密钥"进行排序.此外,还有一些常见的用例需要进一步"分析""系列"中的聚合结果,因为它们应该自然发生.常见的情况是"分析每个聚合结果之间的差异",例如"确定每天平均天数之间的差异".只是一个例子,但类似的事情经常被问到.

我们大多数人(至少是那些有经验的人)都非常了解$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 }
])
Run Code Online (Sandbox Code Playgroud)

会产生:

{ "_id" : "C", "value" : 11 }
{ "_id" : "A", "value" : 3 }
{ "_id" : "B", "value" : 7 }
Run Code Online (Sandbox Code Playgroud)

因此,堆栈始终按发现分组键的顺序颠倒.

这就是问题,堆栈总是颠倒过来,以及为什么mapReduce在预先分配分组键时所做的不同方法.有任何好处或具体原因吗?或者可以做得更好.

Mar*_*tin 7

我真的不认为MongoDB应该默认排序结果.如果您需要按特定顺序排列文档,则应始终对其进行排序.

通过.mapReduce()的原始聚合选项就像人们期望的那样

我不确定人们是否期望这样 - 除非我特别要求,否则我当然不希望事情按照某种顺序排列.如果它们恰好是某种顺序,那么它就是一个实现细节,你通常不应该依赖它.

如果您认为它有用,请打开一个关于它的Jira错误,但我个人认为没有必要.如果用户可以写一个,$group那么他们也可以搞清楚$sort.