【问题标题】:Calculate avg in group for a field which represents array为表示数组的字段计算组中的平均值
【发布时间】:2018-01-09 07:41:43
【问题描述】:

我有以下文件:

{
"name":"St Peterburg",
"checkInAt" : ISODate(...),
"areas": [
        {"length":2.0; "width":3.0}, 
        {"length":4.0; "width":3.0}, 
        {"length":2.0; "width":1.0}
       ]

}

我需要一个查询,它将按天对这些文档进行分组,并为每个组计算一个小数点,该小数点等于组中所有区域数组中所有(长度 * 宽度)的平均值。因此,如果在一个组中我有两个文档:

A { areas:[{"length":2; "width":3}; {"length":3; "width":4};], 
B { areas:[{"length":4; "width":5}; {"length":5; "width":6};]

我需要计算X = avg(2*3;3*4;4*5;5*6)

到目前为止,我的查询如下所示:

db.getCollection('areas').aggregate([
        {
            "$group": {
                "_id": {
                    "$dateToString": {
                        "format": "%Y-%m-%d",
                        "date": "$checkInAt"
                    }
                },

                "avgArea": {$avg: {"$multiply":["$$areas.length","$$areas.width"]}}
            }
        }
    ])

但这不起作用,

非常感谢我如何使它工作的提示

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    这应该可以帮助您:

    db.getCollection('areas').aggregate([{
        $unwind: "$areas" // flatten the "areas" array into multiple documents
    }, {
        $group: {
            "_id": "$checkInAt", // group everything by "checkInAt"
            "avgArea": { $avg: { $multiply: [ "$areas.length", "$areas.width" ] } } // calculate the average
        }
    }])
    

    【讨论】:

    • 感谢您的回答!没有投影可以做到吗?说实话,我的第一个解决方案看起来和你的完全一样。但是,我的一位同事拒绝了此解决方案,并提供了 mongo 文档的链接。
    【解决方案2】:

    您可以尝试以下聚合。

    db.getCollection('areas').aggregate(
    [{"$unwind":"$areas"},
     {"$group":{
      "_id":{
        "$dateToString":{"format":"%Y-%m-%d","date":"$checkInAt"}
      },
      "avgArea":{
        "$avg":{"$multiply":["$areas.length","$areas.width"]}
      }
    }}])
    

    您也可以在没有$unwind 的情况下计算平均值。计算所有数组的$sum,首先计算每个数组的$sum,然后计算每个组的所有数组。

    将数组计数保存在单独的字段中。

    $project avg 将 sum 除以 count。

    db.getCollection('areas').aggregate(
    [{"$group":{
      "_id":{
          "$dateToString":{"format":"%Y-%m-%d","date":"$checkInAt"}
       },
      "sumArea":{
        "$sum":{
          "$sum":{
            "$map":{
              "input":"$areas",
              "as":"val",
              "in":{"$multiply":["$$val.length","$$val.width"]}
            }
          }
        }
      },
      "countArea":{"$sum":{"$size":"$areas"}}
    }},
    {"$project":{
      "avgArea":{"$divide":["$sumArea","$countArea"]}
    }}])
    

    【讨论】:

    • 我很困惑,但如果没有最后的 $project 阶段,这也可以吗? db.getCollection('test').aggregate([{ "$group": { "_id": { "$dateToString": { "format": "%Y-%m-%d", "date":"$checkInAt" } }, "avgArea": { "$avg": { "$avg": { "$map": { "input": "$areas", "as": "val", "in": { "$multiply": [ "$$val.length", "$$val.width" ] } } } } } }}])
    • 不,你是对的。这就是我的目标,但在如何评估运营商方面没有意义。我会多玩一点。谢谢你的评论。已更新。
    • @dnickless 好吧,事实证明我认为最初是正确的。这可以通过不同大小的数组来最好地实现。例如,取两个数组。 [1] 和 [1,2]。预期输出为 1.33,但您的查询将输出 1.25 ( ( 1/1 + 3/2 ) / 2 )。所以你必须使用$project stage来手动计算avg。
    • 这是有道理的。昨晚我也在试验一个基于 $reduce 的版本,偶然发现了同样的问题。
    猜你喜欢
    • 2012-06-03
    • 2015-07-29
    • 1970-01-01
    • 2014-05-14
    • 2013-10-25
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多