Как перегруппировать (или объединить) объекты в конвейере агрегации в MongoDB?

#mongodb

#mongodb

Вопрос:

В настоящее время у меня есть конвейер агрегации:

 db.getCollection('forms').aggregate([
    { $unwind: //unwind },
    { 
        $match: {
            //some matches
        }   
    },
    {
        $project: {
            //some projections
        }
    }, 
    {        
        //Finally, im grouping the results
        $group: {
            _id: {
                year: { $year: '$createdAt' },
                month: { $month: '$createdAt' },
                raceEthnicity: '$demographic.raceEthnicity'
            },
            count: { $sum: 1 },
    }
]
  

Мои текущие результаты похожи на:

 [{
    "_id" : {
        "year" : 2020,
        "month" : 11,
        "raceEthnicity" : "Asian"
    },
    "count" : 1.0
},
{
    "_id" : {
        "year" : 2020,
        "month" : 11,
        "raceEthnicity" : "Multiracial"
    },
    "count" : 3.0
},
{
    "_id" : {
        "year" : 2020,
        "month" : 11,
        "raceEthnicity" : "White"
    },
    "count" : 3.0
},
{
    "_id" : {
        "year" : 2020,
        "month" : 10,
        "raceEthnicity" : "White"
    },
    "count" : 33.0
}]
  

Есть ли способ добавить новый этап в конвейер, чтобы «объединить» результаты того же года / месяца в один объект?
Я хочу добиться чего-то вроде:

 {
    "_id" : {
        "year" : 2020,
        "month" : 11,
    },
    "Asian" : 1.0,
    "Multiracial": 3.0,
    "White": 1.0
},
{
    "_id" : {
        "year" : 2020,
        "month" : 10,
    },
    "White": 33
}
  

Возможно ли это? Как я могу это сделать?

Ответ №1:

Добавьте это в свой конвейер агрегации.

 db.collection.aggregate([
   { $set: { "data": { k: "$_id.raceEthnicity", v: "$count" } } },
   { $group: { _id: { year: "$_id.year", month: "$_id.month" }, data: { $push: "$data" } } },
   { $set: { "data": { $arrayToObject: "$data" } } },
   { $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$data"] } } },
   { $unset: "data" }
])
  

В отличие от решения от @wak786 вам не нужно знать всю этническую принадлежность во время разработки. Это работает для произвольной этнической принадлежности.

Ответ №2:

Добавьте эти этапы в свой конвейер.

 db.collection.aggregate([
  {
    $replaceRoot: {
      newRoot: {
        "$mergeObjects": [
          "$$ROOT",
          {
            $arrayToObject: [
              [
                {
                  k: "$_id.raceEthnicity",
                  v: "$count"
                }
              ]
            ]
          }
        ]
      }
    }
  },
  {
    "$group": {
      "_id": {
        year: "$_id.year",
        month: "$_id.month",
        
      },
      "Asian": {
        "$sum": "$Asian"
      },
      "Multiracial": {
        "$sum": "$Multiracial"
      },
      "White": {
        "$sum": "$White"
      }
    }
  }
])
  

Ниже приведена ссылка на игровую площадку mongo. Я взял текущий результат вашего конвейера в качестве входных данных для своего запроса.

Попробуйте это здесь

Комментарии:

1. Решетчатое решение!, но я выбрал ответ @wernfried, потому что работает для произвольной этнической принадлежности. Спасибо.