sql >> データベース >  >> NoSQL >> MongoDB

Mongo/Mongoose-日付で集計

    良いアプローチは、各グループの集計を計算することを目的として、集計パイプラインをいくつかのステップに分割することです。つまり、年次、月次、および週次の集計です。

    私は上記のパイプラインを生成するために微妙な試みをしましたが、それがあなたが求めているものであるかどうかはわかりませんが、解決策、より良いが最適なものへのいくつかのリードを与えることができます。おそらく他の誰かがより良い答えを与えることができます。

    次の未テストを検討してください パイプライン:

    db.statements.aggregate([
        {
            "$group": {
                "_id": {
                    "name": "$name",
                    "year": { "$year": "$date" },
                    "month": { "$month": "$date" },
                    "week": { "$week": "$date" }
                },
                "total": { "$sum": "$amount" }
            }
        },
        {
            "$group": {
                "_id": {
                    "name": "$_id.name",
                    "year": "$_id.year"
                },
                "YearlySpends": { "$push": "$total" },
                "totalYearlyAmount": { "$sum": "$total" },
                "data": { "$push": "$$ROOT" }
            }
        },
        { "$unwind": "$data" },
        {
            "$group": {
                "_id": {
                    "name": "$_id.name",
                    "month": "$data._id.month"
                },
                "YearlySpends": { "$first": "$YearlySpends" },
                "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
                "MonthlySpends": { "$push": "$data.total" },
                "totalMonthlyAmount": { "$sum": "$data.total" },
                "data": { "$push": "$data" }
            }
        },
        { "$unwind": "$data" },
        {
            "$group": {
                "_id": {
                    "name": "$_id.name",
                    "week": "$data._id.week"
                },
                "YearlySpends": { "$first": "$YearlySpends" },
                "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
                "MonthlySpends": { "$first": "$MonthlySpends" },
                "totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
                "WeeklySpends": { "$push": "$data.total" },
                "totalWeeklyAmount": { "$sum": "$data.total" },
                "data": { "$push": "$data" }
            }
        },
        { "$unwind": "$data" },
        {
            "$group": {
                "_id": "$data._id",
                "YearlySpends": { "$first": "$YearlySpends" },
                "totalYearlyAmount": { "$first": "$totalYearlyAmount" },
                "MonthlySpends": { "$first": "$MonthlySpends" },
                "totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
                "WeeklySpends": { "$first": "$WeeklySpends" },
                "totalWeeklyAmount": { "$first": "$totalWeeklyAmount" }
            }
        }
    ])
    

    サンプル出力

    /* 1 */
    {
        "_id" : {
            "name" : "Tesco",
            "year" : 2017,
            "month" : 3,
            "week" : 11
        },
        "YearlySpends" : [ 
            -3.3
        ],
        "totalYearlyAmount" : -3.3,
        "MonthlySpends" : [ 
            -3.3
        ],
        "totalMonthlyAmount" : -3.3,
        "WeeklySpends" : [ 
            -3.3
        ],
        "totalWeeklyAmount" : -3.3
    }
    
    /* 2 */
    {
        "_id" : {
            "name" : "RINGGO",
            "year" : 2017,
            "month" : 4,
            "week" : 17
        },
        "YearlySpends" : [ 
            -3.3, 
            -26.3, 
            -33.3
        ],
        "totalYearlyAmount" : -62.9,
        "MonthlySpends" : [ 
            -33.3
        ],
        "totalMonthlyAmount" : -33.3,
        "WeeklySpends" : [ 
            -33.3
        ],
        "totalWeeklyAmount" : -33.3
    }
    
    /* 3 */
    {
        "_id" : {
            "name" : "RINGGO",
            "year" : 2017,
            "month" : 3,
            "week" : 12
        },
        "YearlySpends" : [ 
            -3.3, 
            -26.3, 
            -33.3
        ],
        "totalYearlyAmount" : -62.9,
        "MonthlySpends" : [ 
            -3.3, 
            -26.3
        ],
        "totalMonthlyAmount" : -29.6,
        "WeeklySpends" : [ 
            -3.3
        ],
        "totalWeeklyAmount" : -3.3
    }
    
    /* 4 */
    {
        "_id" : {
            "name" : "RINGGO",
            "year" : 2017,
            "month" : 3,
            "week" : 11
        },
        "YearlySpends" : [ 
            -3.3, 
            -26.3, 
            -33.3
        ],
        "totalYearlyAmount" : -62.9,
        "MonthlySpends" : [ 
            -3.3, 
            -26.3
        ],
        "totalMonthlyAmount" : -29.6,
        "WeeklySpends" : [ 
            -26.3
        ],
        "totalWeeklyAmount" : -26.3
    }
    
    /* 5 */
    {
        "_id" : {
            "name" : "Sky",
            "year" : 2017,
            "month" : 3,
            "week" : 9
        },
        "YearlySpends" : [ 
            -63.3
        ],
        "totalYearlyAmount" : -63.3,
        "MonthlySpends" : [ 
            -63.3
        ],
        "totalMonthlyAmount" : -63.3,
        "WeeklySpends" : [ 
            -63.3
        ],
        "totalWeeklyAmount" : -63.3
    }
    
    /* 6 */
    {
        "_id" : {
            "name" : "Amazon",
            "year" : 2017,
            "month" : 3,
            "week" : 12
        },
        "YearlySpends" : [ 
            -61.3
        ],
        "totalYearlyAmount" : -61.3,
        "MonthlySpends" : [ 
            -61.3
        ],
        "totalMonthlyAmount" : -61.3,
        "WeeklySpends" : [ 
            -61.3
        ],
        "totalWeeklyAmount" : -61.3
    }
    

    更新

    集計操作にフィルターを含める場合は、 $ match 最初のパイプラインステージとしてクエリを実行します。ただし、最初の $ match ステップ次に、前のステップは、フィルター処理された結果を集約するため、わずかに変更されます。これは、最初にすべてのドキュメントを集約してから、結果にフィルターを適用することとは大きく異なります。

    filter-first-then-aggregateを使用する場合 ルート、 $ match ベンダーごとにドキュメントをフィルタリングする最初のステップとして、次に先行する $ redact 日付フィールドの月の部分でドキュメントをさらにフィルタリングするパイプラインステップ。残りは $ group ステージ:

    Statements.aggregate([
        { "$match": { "name": req.params.vendor } },
        {
            "$redact": {
                "$cond": [
                    { "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
                    "$$KEEP",
                    "$$PRUNE"
                ]
            }
        },
        .....
        /*
            add the remaining pipeline steps after
        */
    ], function(err, data){
        if (err) throw err;
        console.log(data);
    })
    

    group-first-then-filterを使用する場合 ルートの場合、フィルターはグループ化された結果を与える最後のパイプラインの後にありますが、ストリームのその部分のドキュメントが元のスキーマとは異なるため、異なるフィールドに適用されます。

    コレクション内のすべてのドキュメントを使用して集計操作を開始し、その後フィルタリングするため、このルートはパフォーマンスが低下します。

    Statements.aggregate([
        .....
        /*
            place the initial pipeline steps from 
            the original query above here
        */
        .....
        { 
            "$match": { 
                "_id.name": req.params.vendor,
                "_id.month": parseInt(req.params.month)
            } 
        }
    ], function(err, data){
        if (err) throw err;
        console.log(data);
    })
    

    複数の日付フィルターパラメーターの場合、 $ redact 演算子は

    {
        "$redact": {
            "$cond": [
                {
                    "$and": [
                         { "$eq": [{ "$year": "$date" },  parseInt(req.params.year)  ]},
                         { "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
                         { "$eq": [{ "$week": "$date" },  parseInt(req.params.week)  ]}
                    ]
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
    



    1. ipをバインドしているにもかかわらず、ローカルネットワークでMongoDBサーバーにアクセスできない

    2. MongoidGroupByまたはMongoDbgroupby in rails

    3. mongodbの複数のドキュメントは、一意のキーによって挿入または更新されます

    4. UNIXタイムスタンプに基づく日ごとのMongodb集計