Search code examples
c#mongodbmongodb-queryaggregatemongodb-.net-driver

Projecting results from a grouping that also need to be summed


I am trying to get my head around a specific problem to decide whether to take the plunge in converting some personal projects to MongoDb after completing a basic course last week. What I am trying to achieve is a representation of my data based on grouping and then ultimately selecting specific parts of that group to create a new projection which shows my final result. In the code presently, we do the grouping and then do a sub-select to create the final dataset, I am hoping this can be done in a single hit.

Example document

{
    "_id": {
        "$oid": "600d88b0d7016d5675cd59bd"
    },
    "DeviceId": {
        "$oid": "600d729764ea780882ac559b"
    },
    "UserId": {
        "$oid": "600b660eff59aab915985b1d"
    },
    "Date": {
        "$date": {
            "$numberLong": "1611499696095"
        }
    },
    
    "Records": [
        {
             "Count": {
                "$numberInt": "10"
            },
            "Test1": {
                "Inconclusive": null,
                "Passed": true,
                "Failed": null
            },
            "Test2": {
                "Inconclusive": null,
                "Passed": true,
                "Failed": null
            }
           
        },
    {
             "Count": {
                "$numberInt": "15"
            },
            "Test1": {
                "Inconclusive": true,
                "Passed": null,
                "Failed": null
            },
            "Test2": {
                "Inconclusive": null,
                "Passed": true,
                "Failed": null
            }
           
        },
    {
             "Count": {
                "$numberInt": "15"
            },
            "Test1": {
                "Inconclusive": true,
                "Passed": null,
                "Failed": null
            },
            "Test2": {
                "Inconclusive": null,
                "Passed": null,
                "Failed": true
            }
           
        }
    ]
}

Ultimately, what I am trying to get is this as close to this as possible;

{
    "DeviceId": "600d729764ea780882ac559b",
    "Test1Inconclusive": 30,
    "Test1Passed": 10,
    "Test1Failed": 0,
    "Test2Inconclusive": 0,
    "Test2Passed": 25,
    "Test2Failed": 15
}

So far, all I have managed to get is the data grouped and it is at this point in the existing code (Entity Framework/SQL server) that I would use Linq to pull out the SUM'd values.

[{
    $match: {
        UserId: ObjectId('600b660eff59aab915985b1d')
    }
}, {
    $unwind: {
        path: '$Records'
    }
}, {
    $group: {
        _id: {
            DeviceId: '$DeviceId',
            Test1Inconclusive: '$Records.Test1.Inconclusive',
            Test1Passed: '$Records.Test1.Passed',
            Test1Failed: '$Records.Test1.Failed',
            Test2Inconclusive: '$Records.Test2.Inconclusive',
            Test2Passed: '$Records.Test2.Passed',
            Test2Failed: '$Records.Test2.Failed',
        },
        Count: {
            $sum: '$Records.Count'
        }
    }
}, {}]

I am not sure if it is possible to do what I want, and if so how the do the next projection step while performing a subselect of this grouped data. It might even be that my approach is flawed from the start, so feel free to change it completely.

Bonus internet points if you can also give me the MongoDb C# syntax for doing the same (on a MongoCollection)


Solution

  • Following on from the initial version by @turivishal, the answer below worked;

    db.collection.aggregate([
      {
        $match: {
          UserId: ObjectId("600b660eff59aab915985b1d")
        }
      },
      {
        $unwind: {
          path: "$Records"
        }
      },
      {
        $group: {
          _id: "$DeviceId",
          Test1Inconclusive: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test1.Inconclusive",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Test1Passed: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test1.Passed",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Test1Failed: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test1.Failed",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Test2Inconclusive: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test2.Inconclusive",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Test2Passed: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test2.Passed",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Test2Failed: {
            $sum: {
              $cond: [
                {
                  $eq: [
                    "$Records.Test2.Failed",
                    true
                  ]
                },
                "$Records.Count",
                0
              ]
            }
          },
          Count: {
            $sum: "$Records.Count"
          }
        }
      }
    ])