2012-10-08 103 views
13

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

数据格式:

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

现在,我使用这个聚合函数返回的日期时间,指标数组的数组,并计数:

{$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] 
       ] 
    } 
] 

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

+0

在平均时间我得到的输出,并通过对象循环和使用下划线的'zip'功能把它们结合在一起,这似乎并没有增加多少开销。 –

回答

16

将两个字段与Aggregation Framework结合成一个值的数组是可能的,但绝对不像它可能的那样直截了当(至少在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 } 
    }} 
) 
+0

啊!我不知道$ group可以被多次调用,我会试试看,谢谢! –

+0

感谢您来拯救Stennie :) –

+0

'$ const “确切地做?它似乎没有记录。 – maxdec

1

在没有$ push和$ addToSet的聚合框架中构建数组似乎是缺乏的。我试图让这个在之前工作,并失败。这将是真棒,如果你可能只是这样做:

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

$group,但不起作用。

而且,像这样的建筑“文字”对象不起作用:

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

我希望他们最终拿出按摩这类数据的一些更好的方法。

+0

这些是我尝试过的确切方法,我只是假设它会起作用。我猜不是:( –

2

MongoDB的2.6通过引入$map,这允许阵列换位的simplier形式制造本方便很多:

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”值进行累加的,并且对数组的转换是不再是一个问题领域。

0

您可以使用$zip运营商生产值的数组中的3.4

$zip$arrayElemAt创造datetimemetrics一个阵列。

喜欢的东西

db.collection.aggregate([ { 
    "$match": { 
     "array_serial": array, 
     "port_name": { 
     "$in": ports 
     }, 
     "datetime": { 
     "$gte": from, 
     "$lte": to 
     } 
    } 
    }, 
    { 
    "$group": { 
     "_id": "$port_name", 
     "data": { 
     "$push": { 
      "$arrayElemAt": [ 
      { 
       "$zip": { 
       "inputs": [ 
        [ 
        "$datetime" 
        ], 
        [ 
        "$metric" 
        ] 
       ] 
       } 
      }, 
      0 
      ] 
     } 
     }, 
     "count": { 
     "$sum": 1 
     } 
    } 
    } 
])