【问题标题】:Can the MongoDB aggregation framework $group return an array of values?MongoDB 聚合框架 $group 可以返回一个值数组吗?
【发布时间】:2017-12-13 12:07:56
【问题描述】:

MongoDB 中用于输出格式化的聚合函数有多灵活?

数据格式:

{
        "_id" : ObjectId("506ddd1900a47d802702a904"),
        "port_name" : "CL1-A",
        "metric" : "772.0",
        "port_number" : "0",
        "datetime" : ISODate("2012-10-03T14:03:00Z"),
        "array_serial" : "12345"
}

现在我正在使用这个聚合函数来返回一个 DateTime 数组、一个指标数组和一个计数:

{$match : { 'array_serial' : array, 
                            'port_name' : { $in : ports},
                            'datetime' : { $gte : from, $lte : to}
                        }
                },
               {$project : { port_name : 1, metric : 1, datetime: 1}},
               {$group : { _id : "$port_name", 
                            datetime : { $push : "$datetime"},
                            metric : { $push : "$metric"},
                            count : { $sum : 1}}}

这很好,而且非常快,但是有没有办法格式化输出,以便每个日期时间/指标有一个数组?像这样:

[
    {
      "_id" : "portname",
      "data" : [
                ["2012-10-01T00:00:00.000Z", 1421.01],
                ["2012-10-01T00:01:00.000Z", 1361.01],
                ["2012-10-01T00:02:00.000Z", 1221.01]
               ]
    }
]

这将大大简化前端,因为这是图表代码所期望的格式。

【问题讨论】:

  • 与此同时,我正在获取输出并循环遍历对象并使用下划线的zip 函数将它们组合起来,这似乎不会增加太多开销。

标签: mongodb aggregation-framework


【解决方案1】:

使用聚合框架将两个字段组合成一个值数组是可能的,但绝对不是那么简单(至少在 MongoDB 2.2.0 中)。

这是一个例子:

db.metrics.aggregate(

    // Find matching documents first (can take advantage of index)
    { $match : {
        'array_serial' : array, 
        'port_name' : { $in : ports},
        'datetime' : { $gte : from, $lte : to}
    }},

    // Project desired fields and add an extra $index for # of array elements
    { $project: {
        port_name: 1,
        datetime: 1,
        metric: 1,
        index: { $const:[0,1] }
    }},

    // Split into document stream based on $index
    { $unwind: '$index' },

    // Re-group data using conditional to create array [$datetime, $metric]
    { $group: {
        _id: { id: '$_id', port_name: '$port_name' },
        data: {
            $push: { $cond:[ {$eq:['$index', 0]}, '$datetime', '$metric'] }
        },
    }},

    // Sort results
    { $sort: { _id:1 } },

    // Final group by port_name with data array and count
    { $group: {
        _id: '$_id.port_name',
        data: { $push: '$data' },
        count: { $sum: 1 }
    }}
)

【讨论】:

  • 啊!我不知道可以多次调用 $group 。我试试看,谢谢!
  • '$const' 到底是做什么的?似乎没有记录。
  • 原来$const 是 mongos 和 mongod 之间序列化的内部实现细节,并不打算(ab)被最终用户查询使用(参见jira.mongodb.org/browse/SERVER-6769)。特别是这可能无法通过mongos 正常工作。当时我没有意识到它不是一个记录在案的表达式,而且我已经看到它(ab)用于向文档添加常量,就像我在这里所做的那样。在 MongoDB 2.4.0 发布后,我会尝试重新审视这个答案,因为可能有另一种(并记录在案的)方法。
  • 请投票并观看 MongoDB 功能请求SERVER-8141,它建议添加一个$array 聚合表达式:)。
【解决方案2】:

MongoDB 2.6 通过引入 $map 使这变得更容易,它允许更简单的数组转置形式:

db.metrics.aggregate([
   { "$match": {
       "array_serial": array, 
       "port_name": { "$in": ports},
       "datetime": { "$gte": from, "$lte": to }
    }},
    { "$group": {
        "_id": "$port_name",
        "data": {
            "$push": {
                "$map": {
                    "input": [0,1],
                    "as": "index",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$index", 0 ] },
                            "$datetime",
                            "$metric"
                        ]
                    }
                }
            }
        },
        "count": { "$sum": 1 }
    }}
])

$unwind 的方法非常相似,您将一个数组作为“输入”提供给由两个值组成的映射操作,然后通过$cond 操作将这些值替换为您想要的字段值。

这实际上消除了转换文档所需的所有管道杂耍,就像以前版本中所要求的那样,只是将实际聚合留给手头的工作,这基本上是每个“port_name”值的累积,而对数组的转换是没有的更长的问题区域。

【讨论】:

    【解决方案3】:

    在没有 $push 和 $addToSet 的聚合框架中构建数组似乎是缺乏的。我以前试过让它工作,但失败了。如果你能这样做,那就太棒了:

    data : {$push: [$datetime, $metric]}
    

    $group,但这不起作用。

    另外,像这样构建“文字”对象是行不通的:

    data : {$push: {literal:[$datetime, $metric]}}
    or even data : {$push: {literal:$datetime}}
    

    我希望他们最终能想出一些更好的方法来处理这类数据。

    【讨论】:

    • 这些是我尝试过的确切方法,我只是假设它会起作用。我猜不是:(
    猜你喜欢
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 2021-09-15
    • 2016-03-23
    • 1970-01-01
    • 2021-07-27
    相关资源
    最近更新 更多