【问题标题】:Is there a way to use Aggregation or $sum on dynamic embedded object?有没有办法在动态嵌入对象上使用聚合或 $sum?
【发布时间】:2018-06-27 03:47:32
【问题描述】:

我有一个具有这种结构的集合:

{
  field1:'Foo',
  field2:11,
  stats:{
   A:10,
   B:15,
   C:10
 }
}

现在我想总结一下 stats 字段的所有属性。 但是 stats 字段是 Object 并且可能因文档而异(始终只有数字字段,但字段的名称可以更改), 所以它看起来像这样:

{
  field1:'Foo2',
  field2:12,
  stats:{
   A:10,
   B:10,
   D:5
 }
}

有没有办法使用聚合或 $sum 来获得这样的结果:

{
  sumStats:{
   A:20,
   B:25,
   C:10,
   D:5
 }
}

【问题讨论】:

    标签: node.js mongodb mongoose mongodb-query


    【解决方案1】:

    您可以尝试以下聚合:

    db.col.aggregate([
        {
            $project: {
                stats: { $objectToArray: "$stats" }
            }
        },
        {
            $unwind: "$stats"
        },
        {
            $group: {
                _id: "$stats.k",
                value: { $sum: "$stats.v" }
            }
        },
        {
            $group: {
                _id: null,
                values: { $push: { k: "$_id", v: "$value" } }
            }
        },
        {
            $replaceRoot: {
                newRoot: { $arrayToObject: "$values" }
            }
        }
    ])
    

    由于您的键是未知的,您需要 $objectToArraystats 转换为键值对列表。然后您可以使用$unwind 为每个键值对获取单独的文档。在下一步中,您可以使用$group 聚合所有文档的值。因此,您需要单个对象,因此您应该按null 分组以将一个对象中的所有值累积为键值对列表,然后您可以使用$arrayToObject$replaceRoot 来获得最终结构。

    【讨论】:

      【解决方案2】:

      您也可以尝试使用 mapReduce 聚合。文档说它可能效率较低,但很容易阅读(这是使用 Mongoose 语法,但它只是对 Mongo 驱动程序中同名函数的一个薄包装):

      const mrd = {
        //creates an output bucket for each property in stats object
        map: function () { for (const prop in this.stats) emit(prop, this.stats[prop])},
        //sums all the values in the output bucket for each property
        reduce: function (k, vals) { return vals.reduce((accumulator, currentValue) => accumulator + currentValue, 0)}
      };
      
      YourModel.mapReduce(mrd, function (err, results) {
        console.log(results)
        //something like this (ran at Mongo console, assuming Mongoose output is similar): 
        //[{ "_id" : "a", "value" : 20 },
        //{ "_id" : "b", "value" : 25 },
        //{ "_id" : "c", "value" : 10 }
        //{ "_id" : "d", "value" : 5 }
      })
      

      输出不在一个对象中,但这很容易在直接代码中完成(例如 Array.mapReduce());

      参考: http://mongoosejs.com/docs/api.html#mapreduce_mapReduce https://docs.mongodb.com/manual/tutorial/map-reduce-examples/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多