聚合并更新MongoDB

时间:2016-02-05 16:59:43

标签: mongodb mongodb-query aggregation-framework

我有两个系列:

  • 客户(6 000 000份文件)
  • 订单(5 000 000份文件)

每天一次,我想计算过去一年,过去一个月和过去一周的订单数量,以及客户的订单数量。

我试过了:

db.orders.aggregate(
    {$match: 
        { date_order: { $gt: v_date1year } }
    },
    {$group : {
        _id : "$id_client", 
        count : {$sum : 1}
    }} ,
    {
        "$out": "tmp_indicators"
    }
)

db.tmp_indicators.find({}).forEach(function (my_client) { 
    db.clients.update (
        {"id_client": my_client._id},
        {"$set": 
            { "nb_orders_1year" : my_client.count }
        }
    )
})

我必须这样做3次,过去一年聚合1次,过去一个月1次,过去一周1次。 处理非常缓慢,您是否知道如何以更好的方式执行它?

1 个答案:

答案 0 :(得分:6)

为了提高性能,尤其是在处理大型集合时,请利用 Bulk() API进行批量更新,因为您将批量发送操作到服务器(例如,假设批量大小为1000),这样可以提供更好的性能,因为您不会向服务器发送每个请求(因为您目前正在使用 forEach() 循环中的更新语句)但每1000个请求中只有一个,从而使您的更新比当前更高效,更快捷。

以下示例演示了此方法,第一个示例使用MongoDB版本>= 2.6 and < 3.2中提供的 Bulk() API。它通过使用聚合结果中的值更改clients字段来更新nb_orders_1year集合中的所有文档。

由于 aggregate() 方法返回cursor您可以使用聚合输出集合的 forEach() 迭代它并访问每个文档的方法,从而批量设置批量更新操作,然后使用API​​有效地通过服务器发送:

var bulk = db.clients.initializeUnorderedBulkOp(),
    pipeline = [
        {
            "$match": { "date_order": { "$gt": v_date1year } }
        },
        {
            "$group": {
                "_id": "$id_client", 
                "count": { "$sum" : 1 }
            }
        },
        { "$out": "tmp_indicators" }        
    ],
    counter = 0;

db.orders.aggregate(pipeline);  
db.tmp_indicators.find().forEach(function (doc) {       
    bulk.find({ "_id": doc._id }).updateOne({ 
        "$set": { "nb_orders_1year": doc.count }
    });

    counter++;
    if (counter % 1000 == 0) {
        bulk.execute(); // Execute per 1000 operations and re-initialize every 1000 update statements
        bulk = db.clients.initializeUnorderedBulkOp();
    }
});
// Clean up remaining operations in queue
if (counter % 1000 != 0) { bulk.execute(); }

下一个示例适用于deprecated the Bulk API以后的新MongoDB版本3.2,并使用 bulkWrite() 提供了一套更新的api。

它使用与上面相同的光标,但不是迭代结果,而是使用 map() 方法创建包含批量操作的数组:

 var pipeline = [
        {
            "$match": { "date_order": { "$gt": v_date1year } }
        },
        {
            "$group": {
                "_id": "$id_client", 
                "count": { "$sum" : 1 }
            }
        },
        { "$out": "tmp_indicators" }        
    ];
db.orders.aggregate(pipeline);
var bulkOps = db.tmp_indicators.find().map(function (doc) { 
        return { 
            "updateOne": { 
                "filter": { "_id": doc._id } ,              
                "update": { "$set": { "nb_orders_1year": doc.count } } 
            }         
        };
    });

db.clients.bulkWrite(bulkOps, { "ordered": true });