使用包含对象数组的文档进行聚合查询

时间:2017-08-29 18:11:58

标签: mongodb aggregation-framework

我在MongoDB上遇到聚合查询问题。

我有一个以下结构的文件:

FileReference

我想要计算与arr_vs项目相关的每个值。

预期产出:

[{
    "_id": ObjectId("19a5070b808028108101"),
    "arr_vs": [
    {
        "arr_id": "one",
        "val": 5
    },
    {
        "arr_id": "two",
        "val": 5
    }]
},
{
    "_id": ObjectId("19a5070b80802810810"),
    "arr_vs": [
    {
        "arr_id": "one",
        "val": 5
    },
    {
        "arr_id": "two",
        "val": 2
    },{
        "arr_id": "three",
        "val": 1
    }]
}]

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

输出到命名键绝不是一些人似乎认为它的奇妙之处。实际上,我通常希望使用返回的结果,因此一个" list / array"更有意义。

这基本上是每个新人基本上被告知放弃他们的"命名键"概念,并意识到他们正在使用数据库和命名键的固有问题。这也是为什么收藏本质上是"列表"同样。

所以你最好习惯这个概念:

db.collection.aggregate([
  { "$unwind": "$arr_vs" },
  { "$group": {
    "_id": { "id": "$arr_vs.arr_id", "val": "$arr_vs.val" },
    "total_count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.id",
    "v": {
      "$push": { 
        "val": "$_id.val", 
        "total_count": "$total_count"
      }
    }  
  }}
])

这基本上会给你:

/* 1 */
{
    "_id" : "two",
    "v" : [ 
        {
            "val" : 2.0,
            "total_count" : 1.0
        }, 
        {
            "val" : 5.0,
            "total_count" : 1.0
        }
    ]
}

/* 2 */
{
    "_id" : "one",
    "v" : [ 
        {
            "val" : 5.0,
            "total_count" : 2.0
        }
    ]
}

/* 3 */
{
    "_id" : "three",
    "v" : [ 
        {
            "val" : 1.0,
            "total_count" : 1.0
        }
    ]
}

聚合数据是一种可迭代且易于使用的形式。

如果您打算使用输出格式并至少拥有MongoDB 3.4.4版本,则可以通过压缩文档并使用$arrayToObject来进一步实现:

db.collection.aggregate([
  { "$unwind": "$arr_vs" },
  { "$group": {
    "_id": { "id": "$arr_vs.arr_id", "val": "$arr_vs.val" },
    "total_count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.id",
    "v": {
      "$push": { 
        "val": "$_id.val", 
        "total_count": "$total_count"
      }
    }  
  }},
  { "$group": {
    "_id": null,
    "arr_vs": {
      "$push": {
        "k": "$_id",
        "v": "$v"    
      }
    }
  }},
  { "$project": {
    "_id": 0,
    "arr_vs": { "$arrayToObject": "$arr_vs" } 
  }}   
])

或者甚至只是应用最终的"重塑"客户端,如果你的MongoDB版本不支持new运算符:

db.collection.aggregate([
  { "$unwind": "$arr_vs" },
  { "$group": {
    "_id": { "id": "$arr_vs.arr_id", "val": "$arr_vs.val" },
    "total_count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.id",
    "v": {
      "$push": { 
        "val": "$_id.val", 
        "total_count": "$total_count"
      }
    }  
  }},
  { "$group": {
    "_id": null,
    "arr_vs": {
      "$push": {
        "k": "$_id",
        "v": "$v"    
      }
    }
  }},
  /*
  { "$project": {
    "_id": 0,
    "arr_vs": { "$arrayToObject": "$arr_vs" } 
  }}
  */   
]).map( d => ({
  "arr_vs": d.arr_vs.reduce((acc,curr) =>
    Object.assign(acc,({ [curr.k]: curr.v })),{})    
}))

两者产生相同的输出:

    {
        "arr_vs" : {
            "two" : [ 
                {
                    "val" : 2.0,
                    "total_count" : 1.0
                }, 
                {
                    "val" : 5.0,
                    "total_count" : 1.0
                }
            ],
            "one" : [ 
                {
                    "val" : 5.0,
                    "total_count" : 2.0
                }
            ],
            "three" : [ 
                {
                    "val" : 1.0,
                    "total_count" : 1.0
                }
            ]
        }
    }