web-dev-qa-db-ja.com

Mongodbは複数の日付範囲の$ groupを集計します

私の集計では、ストリーム内の各ドキュメントに日付が含まれています。

日付範囲の値を合計する必要があります。

つまり。

{
    value: 3,
    date: [SoME TIME STAMP]
},
{
    value: 4,
    date: [SoME TIME STAMP]
},
{
    value: 1,
    date: [SoME TIME STAMP]
},
{
    value: -6,
    date: [SoME TIME STAMP]
}

これらのドキュメントを日付範囲に基づいてグループ化できるようにしたいと考えています。 IE:1〜7日前、8〜15日前。 15〜30日前。

db.Collection.aggregate([
{$match: {some matching}},
{$group: {What should i do here??}}
])

もちろん、日付の3つの異なる$ matchで3つの異なる集計を行うことができます。

すべての$ groupを実行し、「value」フィールドを1回の実行で合計することは可能ですか?

11
Zhen Liu

現在の日付が範囲内のどこにあるかに基づいて、グループ化キーを条件付きで決定する必要があります。これは基本的に、ネストされた条件と $cond の論理バリアントを使用して $lt によって実現されます。

// work out dates somehow
var today = new Date(),
    oneDay = ( 1000 * 60 * 60 * 24 ),
    thirtyDays = new Date( today.valueOf() - ( 30 * oneDay ) ),
    fifteenDays = new Date( today.valueOf() - ( 15 * oneDay ) ),
    sevenDays = new Date( today.valueOf() - ( 7 * oneDay ) );

db.collection.aggregate([
    { "$match": {
        "date": { "$gte": thirtyDays }
    }},
    { "$group": {
        "_id": {
            "$cond": [
                { "$lt": [ "$date", fifteenDays ] },
                "16-30",
                { "$cond": [
                    { "$lt": [ "$date", sevenDays ] },
                    "08-15",
                    "01-07"
                ]}
            ]
        },
        "count": { "$sum": 1 },
        "totalValue": { "$sum": "$value" }
    }}
])

$condは三項演算子であるため、最初の条件が評価されて条件がtrueであるかどうかが確認されます。trueの場合は2番目の引数が返され、それ以外の場合はfalseの場合3番目の引数が返されます。したがって、偽の場合に別の$condをネストすることで、日付がどこにあるか、最も古い範囲を意味する「15日未満」、または「7日未満」を意味する論理テストを取得できます。ミドルレンジ、またはもちろんそれは最新のレンジです。

ここでは10未満の数字の前に0を付けているだけなので、必要に応じて並べ替えることができます。$groupの「キー」の出力自体は順序付けされていないためです。

しかし、これが単一のクエリでこれを行う方法です。日付がどこに当たるかに基づいてグループ化キーをどのようにするかを計算し、各キーについて蓄積します。

27
Blakes Seven

最初のステップは、範囲を表す日付オブジェクトを作成することです。 8〜15日前にダンジの集約操作を実行するとします。これは、2つの日付オブジェクトが必要であることを意味します。たとえば、開始と終了です。 startは1日前の日付を保持し、endは8日前の日付を保持します。これらの日付オブジェクトの作成は、nが前の日数である日付からnを引くことにより、それらを前の日数に設定するのと同じくらい簡単です。

_var start = new Date();
start.setDate(start.getDate() - 8);

var end = new Date();
end.setDate(end.getDate() - 15);
_

または、.getTime()メソッドを使用してタイムスタンプミリ秒から減算すると、標準のJavaScriptタイムスタンプ(_Jan 1/1970_からのミリ秒)が返され、通常の数学演算を使用して、Dateオブジェクトに直接フィードバックできます。

_var today = new Date();
var start = new Date(today.getTime() - 8*24*60*60*1000);
var end = new Date(today.getTime() - 15*24*60*60*1000);
_

これで日付オブジェクトができたので、_$match_基準として使用し、_$lte_および_$gte_比較演算子:

_var pipeline = [
    {
        "$match": {
            "date": { "$gte": start, "$lte": end }
        }
    }
]
_

この段階で集計を実行すると、日付が8〜15日前の範囲にあるすべてのドキュメントが得られます。

_db.aggregate(pipeline);
_

これはfind()クエリと同等です。

_db.collection.find({
    "date": { "$gte": start, "$lte": end }
});
_

ここで、次のパイプラインステージで、nullのグループ__id_を指定する集計演算を作成し、を使用してコレクション内のすべてのドキュメントの合計値とカウントを計算する必要があります _$sum_アキュムレータ演算子:

_var pipeline = [
    {
        "$match": {
            "date": { "$gte": start, "$lte": end }
        }
    },
    {
        "$group": {
            "_id": null,
            "totalValues": { "$sum": "$value" },
            "count": { "$sum": 1 }
        }
    }
]

db.collection.aggregate(pipeline);
_

さらに進んで、2つのパラメーター、日付範囲の開始値と終了を受け取る上記の集計操作から実際の合計を返す汎用関数を作成することもできます。

_var getTotalValues = function(start, end){
    var today = new Date();
    var startDate = new Date(today.getTime() - start*24*60*60*1000);
    var endDate = new Date(today.getTime() - end*24*60*60*1000);    

    var pipeline = [
            {
                "$match": {
                    "timestamp": { "$gte": startDate, "$lte": endDate }
                }
            },
            {
                "$group": {
                    "_id": null,
                    "totalValues": { "$sum": "$value" },            
                    "count": { "$sum": 1 }
                }
            }
        ],
        resultArray = db.collection.aggregate(pipeline).toArray();

    return resultArray[0].totalValues;
}

var total = getTotalValues(1, 8);
printjson(total); // prints the total
_
4
chridam