MongoDB 按嵌套字段与组聚合并按另一个字段计数

MongoDB aggregate with group by nested field and count by another field

我有如下三个不同的文档

{
  "category" : "aaaaa",
  "summary" : {
    "details" : {
      "city" : "abc"
      "year_of_reg" : "2012",
      "dept" : "dev"
    }
  }
}

{
  "category" : "bbbb",
  "summary" : {
    "details" : {
      "city" : "abc",
      "year_of_reg" : "2016",
      "dept" : "dev"
    }
  }
}

{
  "category" : "aaaaa",
  "summary" : {
    "details" : {
      "dept" : "ui",
      "year_of_reg" : "2018"
    }
  }
}

我想根据摘要下详细信息中可用的键对结果进行分组,并根据类别进行计数。最终结果应该如下所示

{
  "dep_dev":[
      {
         "category":"aaaaa",
         "count":1.0
      },
      {
         "category":"bbbb",
         "count":1.0
      }
   ],
   "dep_ui":[
      {
         "category":"aaaaa",
         "count":1.0
      }
   ],
   "year_of_reg_2012":[
      {
         "category":"aaaaa",
         "count":1.0
      }
   ],
   "year_of_reg_2016":[
      {
         "category":"bbbb",
         "count":1.0
      }
   ],
   "year_of_reg_2018":[
      {
         "category":"aaaaa",
         "count":1.0
      }
   ],      
   "city_abc":[
      {
         "category":"aaaaa",
         "count":1.0
      },
      {
         "category":"bbbb",
         "count":1.0
      }
   ]
}

如何在 mongo 中实现这一点?这可以使用 facet 完成吗? 如何聚合动态生成输出键?是否有任何可能的方法来使用单个 mongo 查询获取详细信息下的所有可用键?

按照以下步骤操作: 通过 $objectToArray 将 "detail" 对象转换为数组。现在,详细信息字段看起来像 "details: [{k: "city", v: "abc"}, {v: "dep", k: "dev"}]

$展开 "details" 数组字段。

$project 新标签 "label: $details.k_$details.v".

$按新标签和类别分组。

按类别$分组。

db.collections.aggregate([
{
    $project: {
        "category": 1,
        "details": { $objectToArray: "details"}
    }
},
{
    $unwind: "details"
},
{
    $project: {
        "label": { $concat ["$details.k", '_', "$details.v"]}
    }
},
{
    $group: {
        "_id": {
            "label": "label",
            "category": "category"
        },
        "count": {$sum: 1}
    }
},
{
    $group: {
        "_id": "_id.label",
        "summary": {
            $push: {
                "category": "$_id.category",
                "count": "$count"
            }
        }
    }
}
])

您需要运行以下聚合管道才能获得所需的结果:

db.getCollection('test').aggregate([
    /*
    1. Create a field with an array of the summary details key concatenated with their 
       corresponding values. 
    */
    { "$addFields": { 
        "summary": {
            "$map": {
                "input": { "$objectToArray": "$summary.details" },
                "as": "el",
                "in": {
                    "$concat": ["$$el.k", "_", "$$el.v"]
                }
            }
        }
    } },

    /*
    2. Flatten the new array to produce a copy of each document per array entry. 
    */
    { "$unwind": "$summary" },

    /*
    3. Group the documents initially by the key and category. 
    */
    { "$group": {
        "_id": { 
            "key": "$summary",
            "category": "$category"
        },
        "count": { "$sum": 1 }
    } },

    /*
    4. Group the input documents from the previous pipeline by the key and aggregate the 
       category and corresponding counts  
    */
    {  "$group": {
        "_id": "$_id.key",
        "counts": {
            "$push": {
                "category": "$_id.category",
                "count": "$count"
            }
        }
    } },

    /*
    4. Calculate accumulated values for all the input documents as a whole.
    */
    {  "$group": {
        "_id": null,
        "counts": {
            "$push": {
                "k": "$_id",
                "v": "$counts"
            }
        }
    } },
    { "$replaceRoot": {
        "newRoot": { "$arrayToObject": "$counts" }
    } } 
])