web-dev-qa-db-ja.com

MongoDB集約フレームワーク$ groupは値の配列を返すことができますか?

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}}}

これは素晴らしい、そして非常に高速ですが、日時/メトリックごとに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]
               ]
    }
]

これは、チャートコードが期待する形式であるため、フロントエンドを大幅に簡素化します。

14
Chris Matta

Aggregation Frameworkを使用して2つのフィールドを値の配列に結合することは可能ですが、(少なくとも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 }
    }}
)
16
Stennie

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を使用したアプローチと同様に、2つの値で構成されるマップ操作への「入力」として配列を指定し、基本的にこれらの値を$cond操作を介して必要なフィールド値に置き換えます。

これにより、以前のリリースで必要だったドキュメントの変換に必要なすべてのパイプラインジャグリングが実際に削除され、実際の集計は手元のジョブに任せられます。これは基本的に「port_name」値ごとに累積され、配列への変換は問題ではなくなります。範囲。

2
Blakes Seven

$ Pushと$ addToSetを使用せずに集約フレームワークで配列を構築することは、欠けているように思われます。私は以前にこれを機能させようとしましたが、失敗しました。あなたがただすることができればそれは素晴らしいでしょう:

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

の中に $group、しかしそれはうまくいきません。

また、このような「リテラル」オブジェクトの構築は機能しません。

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

彼らが最終的にこの種のデータをマッサージするためのより良い方法を考え出すことを願っています。

1
Eve Freeman