Warm tip: This article is reproduced from stackoverflow.com, please click
aggregate aggregate-functions grouping mongodb

Mongo DB aggregate grouping multiple values that belong to the same document

发布于 2020-04-08 09:24:32

I have documents that look like this

{
"_id": "5e3334cede31d9555e38dbee",
"time": 400,
"datetime": "2020-01-05T16:35:42.315Z",
"version": "2.0.30",
"hostname": "bvasilchik-lt.extron.com",
"testfile": "cards.txt",
"tests": 5,
"failures": 3,
"skips": 0,
"status": "Failed",
"__v": 0
}

I want to create a result that includes the documents that have the highest number of time per testfile name, so if the top 10 were all the same testfile name I'd only want to show the top one that had the same testfile name.

I have done this but I also wanted to include another field that also shows the number of tests matching that grouping, but the only ways I found were to add the $first or the $last or the $max or the $min for the tests field, but that wouldn't be correct b/c the highest time might have a different number of tests.

I am also matching results from a specific date range

const times = await Suite.aggregate([
    {
        "$match": {
            datetime: { "$gte": dateRange.startDate, "$lt": dateRange.endDate, }
        }
    },
    {
        "$group": {
            _id: "$testfile",
            time: { "$max" : "$time" },
        }
    },
    {
        "$sort": {
            time: order
        }
    },
    {
        "$project": {
            _id: 0,
            testfile: "$_id",
            time: "$time"
        }
    }
])

this produces these results

[
    {
    "testfile": "lists.txt",
    "time": 900
    },
    {
    "testfile": "buttons.txt",
    "time": 800
    },
    {
    "testfile": "cards.txt",
    "time": 400
    },
    {
    "testfile": "popover.txt",
    "time": 300
    },
    {
    "testfile": "about-pages.neb",
    "time": 76
    }
]

but what I want it to return is

[
    {
    "testfile": "lists.txt",
    "tests": 5, 
    "time": 900
    },
    {
    "testfile": "buttons.txt",
    "tests": 4,
    "time": 800
    },
    {
    "testfile": "cards.txt",
    "tests": 8,
    "time": 400
    },
    {
    "testfile": "popover.txt",
    "tests": 1,
    "time": 300
    },
    {
    "testfile": "about-pages.neb",
    "tests": 2,
    "time": 76
    }
]
Questioner
BarretV
Viewed
57
Valijon 2020-02-04 06:41

You need to add extra field into $group and $project stages.

You need to use $max operator for time field and accumulatetests field time:tests values. In the last stage, we $reduce tests field taking highest value

{
  "$group": {
    _id: "$testfile",
    time: {
      $max: "$time"
    },
    tests: {
      "$push": {
        time: "$time",
        tests: "$tests"
      }
    }
  }
},
{
  "$sort": {
    time: 1
  }
},
{
  "$project": {
    _id: 0,
    testfile: "$_id",
    time: "$time",
    tests: {
      $reduce: {
        input: "$tests",
        initialValue: 0,
        in: {
          $add: [
            "$$value",
            {
              $cond: [
                {
                  $and: [
                    {
                      $eq: [
                        "$time",
                        "$$this.time"
                      ]
                    },
                    {
                      $gt: [
                        "$$this.tests",
                        "$$value"
                      ]
                    }
                  ]
                },
                {
                  $subtract: [
                    "$$this.tests",
                    "$$value"
                  ]
                },
                0
              ]
            }
          ]
        }
      }
    }
  }
}

MongoPlayground