MongoDB:通过计算每个字段的平均值将对象数组简化为单个对象

时间:2019-05-23 16:24:08

标签: mongodb mongodb-query aggregation-framework

我使用MongoDB聚合API每天聚合一些数据。 这种聚合的结果具有以下格式:

[
  {
    aggDate: '2019-05-23',
    results: [
      {
        foo: 0.58,
        bar: 0.42
      }, {
        foo: 0.32,
        bar: 0.98
      }
    ]
  }
]

关于日期的聚合很好,但是现在我想聚合results数组中的对象。

此聚合的结果应采用以下格式:

[
  {
    aggDate: '2019-05-23',
    result: {
      foo: 0.45 // avg of all the `foo`, here: (0.58 + 0.32) / 2
      bar: 0.7 // avg of all the `bar`, here: (0.42 + 0.98) / 2
    }
  }
]

我的问题是键foobar可以更改/可以在results对象中添加新字段。为了避免每次发生查询时都重新编码,我想使用某种通用的方式对MongoDB进行说明

  

采用此对象数组并将其简化为单个对象,其中每个值都是所有对象中同一字段的平均值。

我知道MongoDB中存在$reduce运算符,但是我不知道如何使用它,甚至不确定它是否可以在这里为我提供帮助。

3 个答案:

答案 0 :(得分:4)

您应该运行$unwind并使用$group阶段汇总数据。您还需要$arrayToObject$objectToArray才能使用动态键。 $reduce在这里是不可选择的,因为键是unknwon

db.col.aggregate([
    {
        $project: {
            aggDate: 1,
            results: {
                $map: { input: "$results", in: { $objectToArray: "$$this" } }
            }
        }
    },
    {
        $unwind: "$results"
    },
    {
        $unwind: "$results"
    },
    {
        $group: {
            _id: { aggDate: "$aggDate", k: "$results.k" },
            sum: { $sum: "$results.v" },
            count: { $sum: 1 }
        }
    },
    {
        $project: {
            _id: 1,
            v: { $divide: [ "$sum", "$count" ] }
        }
    },
    {
        $group: {
            _id: "$_id.aggDate",
            results: { $push: { k: "$_id.k", v: "$v" } }
        }
    },
    {
        $project: {
            _id: 0,
            aggDate: "$_id",
            results: { $arrayToObject: "$results" }
        }
    }
])

Mongo Playground

答案 1 :(得分:3)

您不需要使用$reduce。只需$sum即可完成工作。

db.collection.aggregate([
  { "$project": {
    "result": {
      "foo": { "$divide": [{ "$sum": "$results.foo" }, { "$size": "$results" }] },
      "bar": { "$divide": [{ "$sum": "$results.bar" }, { "$size": "$results" }] }
    }
  }}
])
根据{{​​1}}数组中的动态键

更新->

results

MongoPlayground

具有更简化的版本和单个db.collection.aggregate([ { "$project": { "aggDate": 1, "results": { "$reduce": { "input": { "$map": { "input": "$results", "in": { "$objectToArray": "$$this" }} }, "initialValue": [], "in": { "$concatArrays": ["$$value", "$$this"] } } } }}, { "$project": { "aggDate": 1, "result": { "$arrayToObject": { "$map": { "input": { "$setUnion": ["$results.k"] }, "as": "m", "in": { "$let": { "vars": { "fil": { "$filter": { "input": "$results", "as": "d", "cond": { "$eq": ["$$d.k", "$$m"] } } } }, "in": { "k": "$$m", "v": { "$divide": [{ "$sum": "$$fil.v" }, { "$size": "$$fil" }] } } } } } } } }} ]) 阶段

$project

MongoPlayground

两者都输出为

db.collection.aggregate([
  { "$project": {
    "aggDate": 1,
    "result": {
      "$let": {
        "vars": {
          "red": {
            "$reduce": {
              "input": {
                "$map": { "input": "$results", "in": { "$objectToArray": "$$this" }}
              },
              "initialValue": [],
              "in": { "$concatArrays": ["$$value", "$$this"] }
            }
          }
        },
        "in": {
          "$arrayToObject": {
            "$map": { "input": { "$setUnion": ["$$red.k"] },
              "as": "m",
              "in": {
                "$let": {
                  "vars": {
                    "fil": {
                      "$filter": {
                        "input": "$$red",
                        "as": "d",
                        "cond": { "$eq": ["$$d.k", "$$m"] }
                      }
                    }
                  },
                  "in": {
                    "k": "$$m",
                    "v": { "$divide": [{ "$sum": "$$fil.v" }, { "$size": "$$fil" }] }
                  }
                }
              }
            }
          }
        }
      }
    }
  }}
])

答案 2 :(得分:0)

编辑: 不是最优雅的,但是:

    { 
        "$unwind" : "$result"
    }, 
    { 
        "$addFields" : {
            "value" : {
                "$objectToArray" : "$result"
            }
        }
    }, 
    { 
        "$unwind" : "$value"
    }, 
    { 
        "$group" : {
            "_id" : null, 
            "length" : {
                "$sum" : 1.0
            }, 
            "total" : {
                "$sum" : "$value.v"
            }
        }
    }, 
    { 
        "$addFields" : {
            "avg" : {
                "$divide" : [
                    "$total", 
                    "$length"
                ]
            }
        }
    }