MongoDB:子文档数组中的聚合字段

时间:2014-06-18 17:54:34

标签: mongodb mongodb-query aggregation-framework

我有一个名为Events的mongodb系列,包含棒球比赛。以下是表中一条记录的示例:

{
    "name" : "Game# 814",
    "dateStart" : ISODate("2012-09-28T14:47:53.695Z"),
    "_id" : ObjectId("53a1b24de3f25f4443d9747e"),
    "stats" : [ 
        {
            "team" : ObjectId("53a11a43a8de6dd8375c940b"),
            "teamName" : "Reds",
            "_id" : ObjectId("53a1b24de3f25f4443d97480"),
            "score" : 17
        }, 
        {
            "team" : ObjectId("53a11a43a8de6dd8375c938d"),
            "teamName" : "Yankees",
            "_id" : ObjectId("53a1b24de3f25f4443d9747f"),
            "score" : 12
        }
    ]
    "__v" : 0
}

我需要帮助编写返回所有团队排名的查询。结果集应如下所示:

{
    "team" : ObjectId("53a11a43a8de6dd8375c938d"),
    "teamName" : "Yankees",
    "wins" : <<number of Yankees wins>>
    "losses" : <<number of Yankees losses>>
    "draws" : <<number of Yankees draws>>
}
{
    "team" : ObjectId("53a11a43a8de6dd8375c940b"),
    "teamName" : "Reds",
    "wins" : <<number of Reds wins>>
    "losses" : <<number of Reds losses>>
    "draws" : <<number of Reds draws>>
}
...

以下是我开始的查询...

db.events.aggregate(
    {"$unwind": "$stats" },
    { $group : {
        _id : "$stats.team",
        gamesPlayed : { $sum : 1},
        totalScore : { $sum : "$stats.score" }
    }}
);

...返回结果:

{
    "result" : [ 
        {
            "_id" : ObjectId("53a11a43a8de6dd8375c93cb"),
            "gamesPlayed" : 125,  // not a requirement... just trying to get $sum working
            "totalScore" : 1213 // ...same here
        }, 
        {
            "_id" : ObjectId("53a11a44a8de6dd8375c955f"),
            "gamesPlayed" : 128,
            "totalScore" : 1276
        }, 
        {
            "_id" : ObjectId("53a11a44a8de6dd8375c9661"),
            "gamesPlayed" : 152,
            "totalScore" : 1509
        }, 
....

1 个答案:

答案 0 :(得分:1)

在您创建或更新文档时,您最好保留文档中的“胜利”,“损失”和“抽奖”。但是如果有点长篇大论的话,可以用聚合作为

db.events.aggregate([

    // Unwind the "stats" array
    { "$unwind": "$stats" },

    // Combine the document with new fields
    { "$group": {
        "_id": "$_id",
        "firstTeam": { "$first": "$stats.team" },
        "firstTeamName": { "$first": "$stats.teamName" },
        "firstScore": { "$first": "$stats.score" },
        "lastTeam": { "$last": "$stats.team" },
        "lastTeamName": { "$last": "$stats.teamName" },
        "lastScore": { "$last": "$stats.score" },
        "minScore": { "$min": "$stats.score" },
        "maxScore": { "$max": "$stats.score" }
    }},

    // Calculate by comparing scores
    { "$project": {
        "firstTeam": 1,
        "firstTeamName": 1,
        "firstScore": 1,
        "lastTeam": 1,
        "lastTeamName": 1,
        "lastScore": 1,
        "firstWins": {
           "$cond": [
              { "$gt": [ "$firstScore", "$lastScore" ] },
              1,
              0
           ]
        },
        "firstLosses": {
           "$cond": [
              { "$lt": [ "$firstScore", "$lastScore" ] },
              1,
              0
           ]
        },
        "firstDraws": {
           "$cond": [
              { "$eq": [ "$firstScore", "$lastScore" ] },
              1,
              0
           ]
        },
        "lastWins": {
           "$cond": [
              { "$gt": [ "$lastScore", "$firstScore" ] },
              1,
              0
           ]
        },
        "lastLosses": {
           "$cond": [
              { "$lt": [ "$lastScore", "$firstScore" ] },
              1,
              0
           ]
        },
        "lastDraws": {
           "$cond": [
              { "$eq": [ "$lastScore", "$firstScore" ] },
              1,
              0
           ]
        },
        "type": { "$literal": [ true, false ] }
    }},

    // Unwind the "type"
    { "$unwind": "$type" },


    // Group teams conditionally on "type"
    { "$group": {
        "_id": { 
            "team": {
                "$cond": [ 
                    "$type",
                    "$firstTeam",
                    "$lastTeam"
                 ] 
            },
            "teamName": {
                "$cond": [
                    "$type",
                    "$firstTeamName",
                    "$lastTeamName"
                ]
            }
        },            
        "owins": { 
            "$sum": {
                "$cond": [
                    "$type", 
                    "$firstWins", 
                    "$lastWins"
                ]
            }
        },
        "olosses": { 
            "$sum": {
                "$cond": [
                    "$type", 
                    "$firstLosses", 
                    "$lastLosses"
                ]
            }
        },
        "odraws": { 
            "$sum": {
                "$cond": [
                    "$type", 
                    "$firstDraws", 
                    "$lastDraws"
                ]
            }
        }
    }},

    // Project your final form
    { "$project": {
        "_id": 0,
        "team": "$_id.team",
        "teamName": "$_id.teamName",
        "wins": "$owins",
        "losses": "$olosses",
        "draws": "$odraws"
    }}

])

第一部分是通过展开数组来“重新塑造”文档,然后使用“first”和“last”进行分组,以便为​​两个团队定义字段。

然后你希望通过这些文件$project计算你的“胜利”,“损失”和“抽奖”。另外一件事是为这两个值true/false添加一个数组字段很方便。如果您使用的是mongodb的2.6之前的版本,$literal可以替换为 $const ,但未记录在案,但会做同样的事情。

一旦你$unwind那个“类型”数组,通过评估是否通过使用选择“第一个”或“最后一个”团队字段值,可以在$group阶段拆分文档$cond。这是一个三元运算符,用于计算true/false条件并根据该条件返回适当的值。

通过最终$project,您的文档将按照您想要的方式形成。