Aggregate with count of sub documents matching the condition and grouping

£可爱£侵袭症+ 提交于 2019-12-11 03:15:01

问题


I've collections of documents as like as below:

{
    "_id" : ObjectId("55d4410544c96d6f6578f893"),
    "executionProject" : "Project1",
    "suiteList" : [ 
        {
            "suiteStatus" : "PASS"
        }
    ],
    "runEndTime" : ISODate("2015-08-19T08:40:47.049Z"),
    "runStartTime" : ISODate("2015-08-19T08:40:37.621Z"),
    "runStatus" : "PASS",
    "__v" : 1
}

{
    "_id" : ObjectId("55d44eb4c0422e7b8bffe76b"),
    "executionProject" : "Project1",
    "suiteList" : [ 
        {
            "suiteStatus" : "PASS"
        }
    ],
    "runEndTime" : ISODate("2015-08-19T09:39:13.528Z"),
    "runStartTime" : ISODate("2015-08-19T09:39:00.406Z"),
    "runStatus" : "PASS",
    "__v" : 1
}

{
    "_id" : ObjectId("55d44f0bc0422e7b8bffe76f"),
    "executionProject" : "Project1",
    "suiteList" : [ 
        {
            "suiteStatus" : "FAIL"
        }
    ],
    "runEndTime" : ISODate("2015-08-19T09:46:31.108Z"),
    "runStartTime" : ISODate("2015-08-19T09:40:27.377Z"),
    "runStatus" : "PASS",
    "__v" : 1
}

{
    "_id" : ObjectId("55d463d0c0422e7b8bffe789"),
    "executionProject" : "Project2",
    "suiteList" : [ 
        {
            "suiteStatus" : "PASS"
        },
        {
            "suiteStatus" : "PASS"
        }
    ],
    "runEndTime" : ISODate("2015-08-19T11:09:52.537Z"),
    "runStartTime" : ISODate("2015-08-19T11:09:04.539Z"),
    "runStatus" : "FAIL",
    "__v" : 1
}

{
    "_id" : ObjectId("55d464ebc0422e7b8bffe7c2"),
    "executionProject" : "Project3",
    "suiteList" : [ 
        {
            "suiteStatus" : "FAIL"
        }
    ],
    "runEndTime" : ISODate("2015-08-19T11:18:41.460Z"),
    "runStartTime" : ISODate("2015-08-19T11:13:47.268Z"),
    "runStatus" : "FAIL",
    "__v" : 10
} 

And I'm expecting output as follows:

[
    {
        "executionProject": "Project1",
        "suite-pass": 0,
        "suite-fail": 1,
        "runEndTime": ISODate("2015-08-19T09:46:31.108Z")
    },
    {
        "executionProject": "Project2",
        "suite-pass": 2,
        "suite-fail": 0,
        "runEndTime": ISODate("2015-08-19T11:09:52.537Z")
    },
    {
        "executionProject": "Project3",
        "suite-pass": 0,
        "suite-fail": 1,
        "runEndTime": ISODate("2015-08-19T11:18:41.460Z")
    },
]

I want to group by project and order by runEndTime and show the pass and fail counts of suiteList.

I tried this as suggested by Blakes in Mongodb: Group by element and show the sub-document count based on condition and sort the document by date:

db.testruns.aggregate([
      { "$sort": { "runEndTime": 1 } },
      { "$group": {
      "_id": "$executionProject",
        "suite-pass": {
          "$last": {
            "$cond": [
              { "$anyElementTrue": {
                "$map": {
                  "input": "$suiteList",
                  "as": "suite",
                  "in": {
                    "$eq": [ "$$suite.suiteStatus", "PASS" ]
                  }
                }
              }},
              1,
              0
            ]
          }
        },
        "suite-fail": {
          "$last": {
            "$cond": [
              { "$anyElementTrue": {
                "$map": {
                  "input": "$suiteList",
                  "as": "suite",
                  "in": {
                    "$eq": [ "$$suite.suiteStatus", "FAIL" ]
                  }
                }
              }},
              1,
              0
            ]
          }
        },
        "runEndTime": { "$last": "$runEndTime" }
      }},
      { "$sort": { "runEndTime": 1 } }
    ]);

I was expecting the suite-pass count for Project2 as 2 since there are 2 elements in suiteList, but it returns 1.


回答1:


You should have read the answer properly, as there already was another alternate listing and explanation of why the expected result you want from the one you used would be different.

Instead you want this one, which respects the possible multiple "PASS" or "FAIL":

  Model.aggregate(
    [
      { "$sort": { "executionProject": 1, "runEndTime": 1 } },
      { "$group": {
        "_id": "$executionProject",
        "suiteList": { "$last": "$suiteList" },
        "runEndTime": { "$last": "$runEndTime" }
      }},
      { "$unwind": "$suiteList" },
      { "$group": {
        "_id": "$_id",
        "suite-pass": { 
          "$sum": {
            "$cond": [
              { "$eq": [ "$suiteList.suiteStatus", "PASS" ] },
              1,
              0
            ]
          }
        },
        "suite-fail": { 
          "$sum": {
            "$cond": [
              { "$eq": [ "$suiteList.suiteStatus", "FAIL" ] },
              1,
              0
            ]
          }
        },
        "runEndTime": {"$first": "$runEndTime"}
      }},
      { "$sort": { "runEndTime": 1 }}
    ],
    function(err,result) {

    }
  );

Which is sort of a "combination" of approaches. The first is to get the "last" by runTime as you were expecting. The next is to break down the array and this time actually "sum up" the possible occurances of pass or fail, rather than just record a 1 for either pass or fail in the array, the actual "pass" or "fail" are counted.

With results:

{
        "_id" : "Project1",
        "suite-pass" : 0,
        "suite-fail" : 1,
        "runEndTime" : ISODate("2015-08-19T09:46:31.108Z")
}
{
        "_id" : "Project2",
        "suite-pass" : 2,
        "suite-fail" : 0,
        "runEndTime" : ISODate("2015-08-19T11:09:52.537Z")
}
{
        "_id" : "Project3",
        "suite-pass" : 0,
        "suite-fail" : 1,
        "runEndTime" : ISODate("2015-08-19T11:18:41.460Z")
}



回答2:


Unwind suiteList and used $sum in group as below :

db.testruns.aggregate({
    "$unwind": "$suiteList"
}, {
    "$group": {
        "_id": "$executionProject",
        "suite-pass": {
            "$sum": {
                "$cond": {
                    "if": {
                        "$eq": ["$suiteList.suiteStatus", "PASS"]
                    },
                    "then": 1,
                    "else": 0
                }
            }
        },
        "suite-fail": {
            "$sum": {
                "$cond": {
                    "if": {
                        "$eq": ["$suiteList.suiteStatus", "FAIL"]
                    },
                    "then": 1,
                    "else": 0
                }
            }
        },
        "runEndTime": {
            "$last": "$runEndTime"
        }
    }
}, {
    "$sort": {
        "runEndTime": 1
    }
})


来源:https://stackoverflow.com/questions/32197955/aggregate-with-count-of-sub-documents-matching-the-condition-and-grouping

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!