如何与group by聚合并正确排序

时间:2014-05-27 21:38:39

标签: mongodb aggregation-framework

我正在使用Mongodb。 考虑我的下一个文件:

{ uid: 1, created: ISODate("2014-05-02..."), another_col : "x" },
{ uid: 1, created: ISODate("2014-05-05..."), another_col : "y" },
{ uid: 2, created: ISODate("2014-05-10..."), another_col : "z" },
{ uid: 3, created: ISODate("2014-05-05..."), another_col : "w" },
{ uid: 1, created: ISODate("2014-05-01..."), another_col : "f" },
{ uid: 2, created: ISODate("2014-05-22..."), another_col : "a" }

我想要做的是在uid上进行简单的groupby并按降序排序,这样我就可以得到每个uid的第一行。

预期输出的示例

{ uid: 1, created: ISODate("2014-05-05..."), another_col: "y" },
{ uid: 2, created: ISODate("2014-05-22..."), another_col: "a" },
{ uid: 3, created: ISODate("2014-05-05..."), another_col: "w" }

我能得到的最好的是:

db.mycollection.aggregate( {$group: {_id: "$uid", rows: {$push: { "created" : "$created" }}}}, sort { // doesnt work well }  )

任何人都可以指导我进行分组和排序的正确组合吗? 它只是没有像我期望的那样工作。 (注意:我检查了很多线程,但我无法为我的案例找到正确的答案)

3 个答案:

答案 0 :(得分:4)

这里有一些需要了解的内容。

当您使用$group时,边界将按照发现它们的顺序排序,而无需进行初始或结束阶段$sort操作。因此,如果您的文档最初是这样的顺序:

{ uid: 1, created: ISODate("2014-05-02..."), another_col : "x" },
{ uid: 1, created: ISODate("2014-05-05..."), another_col : "y" },
{ uid: 3, created: ISODate("2014-05-05..."), another_col : "w" },
{ uid: 2, created: ISODate("2014-05-10..."), another_col : "z" },

然后在管道末端使用$group而不使用$sort会返回如下结果:

{ uid: 1, created: ISODate("2014-05-05..."), another_col : "y" },
{ uid: 3, created: ISODate("2014-05-05..."), another_col : "w" },
{ uid: 2, created: ISODate("2014-05-10..."), another_col : "z" },

这是一个概念,但它实际上似乎是您在结果中所期望的,需要按照您要查找的uid的排序顺序返回“最后的其他字段”。在这种情况下,获取结果的方法实际上是首先 $sort ,然后使用$last运算符:

db.mycollection.aggregate([

    // Sorts everything first by _id and created
    { "$sort": { "_id": 1, "created": 1 } },

    // Group with the $last results from each boundary
    { "$group": {
        "_id": "$uid",
        "created": { "$last": "$created" },
        "another_col": { "$last": "$created" }
    }}
])

或者基本上将排序应用于您想要的内容。

$last$max之间的区别在于,后者将为分组_id中的给定字段选择“最高”值,而不管当前在未分类的排序中订购。另一方面,$last将选择与“最后”分组_id值相同的“行”中出现的值。


如果您实际上想要对数组的值进行排序,那么方法是类似的。保持阵列成员处于“创建”顺序,你也可以先排序:

db.mycollection.aggregate([

    // Sorts everything first by _id and created
    { "$sort": { "_id": 1, "created": 1 } },

    // Group with the $last results from each boundary
    { "$group": {
        "_id": "$uid",
        "row": {
            "$push": {
                "created": "$created",
                "another_col": "$another_col"
            }
        }
    }}
])

带有这些字段的文档将按照已经排序的顺序添加到数组中。

答案 1 :(得分:0)

如果您正在寻找的是第一行意味着您正在寻找最大值。只需使用内置的$max累加器。

db.mycollection.aggregate([{$group: {_id: "$uid", rows: {$max:"$created"}}}])

如果需要处理所有创建日期,可以使用$push累加器。有关累加器的更多信息,请参阅:http://docs.mongodb.org/manual/reference/operator/aggregation/group/

如果您想要返回完整的文档,并希望能够遍历所有文档,那么您根本不需要聚合结果。这样的事情可以让你得到你想要的东西。

db.mycollection.find({$query:{}, $orderby:{uid:1,created:-1}})

答案 2 :(得分:0)

使用$ project和

db.mycollection.aggregate([{$group: {_id: "$uid", rows: {$max:"$created"}}}])

应该帮助你,参考这些链接

http://docs.mongodb.org/manual/reference/operator/aggregation/project/

Mongodb group and project operators

mongodb aggregation framework group + project