MongoDB聚合 - 文档数组中特定值的平均值

时间:2016-10-04 10:13:17

标签: mongodb mongodb-query aggregation-framework

我目前正在开发一个具有以下结构的数据库:

{"_id" : ObjectId("1abc2"),
"startdatetime" : ISODate("2016-09-11T18:00:37Z"),
"diveValues" : [
    {
        "temp" : 15.269,
        "depth" : 0.0,
    },
    {
        "temp" : 14.779257384,
        "depth" : 1.0,
    },
    {
        "temp" : 14.3940253165,
        "depth" : 2.0,
    },
    {
        "temp" : 13.9225795455,
        "depth" : 3.0,
    },
    {
        "temp" : 13.8214431818,
        "depth" : 4.0,
    },
    {
        "temp" : 13.6899553571,
        "depth" : 5.0,
    }
]}

数据库包含有关水中深度n米和给定深度温度的信息。它存储在“diveValues”数组中。我已经成功地平均了日期之间的所有深度,包括月平均值和每日平均值。我遇到的一个严重问题是,在过去的6个月中,每个月的平均深度为1到4米之间。

以下是所有深度的1月至6月每个月的平均温度示例:

db.collection.aggregate(
  [
    {$unwind:"$diveValues"},
    {$match:
      {'startdatetime':
        {$gt:new ISODate("2016-01-10T06:00:29Z"),
        $lt:new ISODate("2016-06-10T06:00:29Z")}
        }
      },

    {$group:
      {_id: 
        { year: { $year: "$startdatetime" }, 
        month: { $month: "$startdatetime" }},
      avgTemp: { $avg: "$diveValues.temp" }}
    },
    {$sort:{_id:1}}
  ]
)

导致:

{ "_id" : { "year" : 2016, "month" : 1 }, "avgTemp" : 7.575706502958313 }
{ "_id" : { "year" : 2016, "month" : 3 }, "avgTemp" : 6.85037457740135 }
{ "_id" : { "year" : 2016, "month" : 4 }, "avgTemp" : 7.215702831902588 }
{ "_id" : { "year" : 2016, "month" : 5 }, "avgTemp" : 9.153453683614638 }
{ "_id" : { "year" : 2016, "month" : 6 }, "avgTemp" : 11.497953009390237 }

现在,我似乎无法弄清楚如何在同一时期内将平均温度控制在1到4米之间。

我一直在尝试按想要的深度对值进行分组,但是还没有对它进行管理 - 通常不会以错误的语法结束。此外,如果我没有错,只要潜水值为1米和4米,$ match管道就会返回所有深度,这样就无法使用了。

使用find()工具我使用$ slice来返回我想从数组中获取的值 - 但是与aggregate()函数一起没有成功。

有没有办法解决这个问题?非常感谢,非常感谢!

2 个答案:

答案 0 :(得分:0)

首先,日期 $ match 运算符should be used at the beginning of the pipeline so that indexes can be used

现在,对于这个问题,您只需像过滤日期一样过滤深度间隔:

db.col.aggregate([
    {"$match": {
        'startdatetime': {
            "$gt": new ISODate("2016-01-10T06:00:29Z"),
            "$lt": new ISODate("2016-11-10T06:00:29Z")
        }
    }},
    {"$unwind": "$diveValues"},
    {"$match": {
        "diveValues.depth": {
            "$gte": 1.0,
            "$lt": 4.0
        }
    }},
    {"$group": {
        "_id": { 
            "year": {"$year": "$startdatetime" }, 
            "month": {"$month": "$startdatetime" }
        },
        "avgTemp": { "$avg": "$diveValues.temp" }}
    }
])

这将仅为您提供所选深度间隔的平均值。

答案 1 :(得分:0)

您需要将 $match 管道置于 $unwind 之前 optimize 聚合操作在整个集合上执行 $unwind 操作可能会导致一些性能问题,因为它会为每个数组条目生成每个文档的副本并使用更多内存(聚合时可能的内存上限)因此,产生扁平阵列的“时间”以及处理它的“时间”需要10%总内存的管道。因此,最好将进入管道的文档数量限制为扁平化。

db.collection.aggregate([
    {
        "$match": {
            "startdatetime": {
                "$gt": new ISODate("2016-01-10T06:00:29Z"),
                "$lt": new ISODate("2016-06-10T06:00:29Z")
            }, 
            "diveValues.depth": { "$gte": 1, "$lte": 4 } 
        } 
    },
    { "$unwind": "$diveValues" },
    { "$match": { "diveValues.depth": { "$gte": 1, "$lte": 4 } } },
    { 
        "$group": {
            "_id": { 
                "year": { "$year": "$startdatetime" }, 
                "month": { "$month": "$startdatetime" }
            },
            "avgTemp": { "$avg": "$diveValues.temp" }
        }
    }
])

如果您希望结果包含所有深度和1-4深度范围的平均温度,那么您需要运行此管道,该管道将使用 $cond tenary运算符为 $avg 运算符提供基于深度范围的组内累积温度:

db.collection.aggregate([
    {
        "$match": {
            "startdatetime": {
                "$gt": new ISODate("2016-01-10T06:00:29Z"),
                "$lt": new ISODate("2016-06-10T06:00:29Z")
            }
        }
    },     
    { "$unwind": "$diveValues" },
    { 
        "$group": {
            "_id": { 
                "year": { "$year": "$startdatetime" }, 
                "month": { "$month": "$startdatetime" }
            },
            "avgTemp": { "$avg": "$diveValues.temp" },
            "avgTempDepth1-4": {
                "$avg": {
                    "$cond": [
                        { 
                            "$and": [
                                { "$gte": [ "$diveValues.depth", 1 ] }, 
                                { "$lte": [ "$diveValues.depth", 4 ] }
                            ]
                        },
                        "$diveValues.temp",
                        null                        
                    ]
                }
            }
        }
    }
])