2016-02-12 220 views
2

我想在一些數據統計中使用mongo的聚合框架。我正在使用的查詢,當在本地運行時,幾乎不需要一分鐘,但是當我在服務器上運行相同的查詢它不給予迴應,並在繼續等待太久後,我不得不取消它,任何人都可以請建議爲什麼這發生。服務器上的Monogo聚合查詢花費太長時間

var orderIds = db.delivery.find({"status":"DELIVERED"}).map(function(o) { 
    return o.order 
}); 

var userIds = db.order.aggregate([{ 
    $match : { _id : { $in : orderIds } } 
}, { 
    $group: { _id : "$customer" } 
}]).map(function(u) { return u._id }); 

var userstats = db.order.aggregate([{ 
    $sort : { customer : 1, dateCreated : 1 } 
}, { 
    $match : { status : "DELIVERED", customer : { $in : userIds } } 
}, { 
    $group: { 
     _id : "$customer", orders : { $sum : 1 }, 
     firstOrderDate : { $first : "$dateCreated" }, 
     lastOrderDate : { $last : "$dateCreated" } 
    } 
}]); 

userstats.forEach(function(x) { 
    db.user.update({ _id : x._id }, { 
     $set : { 
      totalOrders : x.orders, 
      firstOrderDate : x.firstOrderDate, 
      lastOrderDate : x.lastOrderDate 
     } 
    }) 
}) 

我不確定,但不應該在服務器上更快嗎? ,但相反,它無法提供輸出。

回答

2

爲了加速這個過程,你可以通過幾種方式來重構你的操作。 第一個將是消除等,其可與$group管道內的$max$min運營商被替換的$sort操作者不必要的流水線作業。

其次,使用bulk() API與大集合,因爲他們將分批發送操作服務器時尤其如此,這將增加更新操作perfromance(例如,說的500批次大小)與發送向服務器發送每個請求(正如您當前使用forEach()循環內的更新語句所做的那樣)。

考慮以下重構操作:

var orderIds = db.delivery.find({"status": "DELIVERED"}).map(function(d){return d.order;}), 
    counter = 0, 
    bulk = db.user.initializeUnorderedBulkOp(); 

var userstatsCursor = db.orders.aggregate([ 
    { "$match": { "_id": { "$in": orderIds } } }, 
    { 
     "$group": { 
      "_id": "$customer", 
      "orders": { "$sum": 1 }, 
      "firstOrderDate": { "$min": "$dateCreated" }, 
      "lastOrderDate":{ "$max": "$dateCreated" } } 
     } 
    } 
]); 

userstatsCursor.forEach(function (x){ 
    bulk.find({ "_id": x._id }).updateOne({ 
     "$set": { 
      "totalOrders": x.orders, 
      "firstOrderDate": x.firstOrderDate, 
      "lastOrderDate": x.lastOrderDate 
     } 
    }); 

    counter++; 
    if (counter % 500 == 0) { 
     bulk.execute(); // Execute per 500 operations and 
     // re-initialize every 500 update statements 
     bulk = db.user.initializeUnorderedBulkOp(); 
    } 
}); 

// Clean up remaining operations in queue 
if (counter % 500 != 0) { bulk.execute(); } 
+0

解決方案看起來不錯,我可以看到很多新東西,但查詢以某種方式拋出語法錯誤「Unexpected token}」 – user29578

+1

@ user29578有一個錯字,我在更新後的答案中刪除了管線陣列之前的額外右大括號。再試一次。 – chridam

+0

嗨chridam,我得到這個錯誤2016-02-14T02:32:33.615 + 0530 TypeError:對象函數(){ 返回新Bulk(this,false); }具有(殼)沒有方法 '查找' :1:436 在Object.DBQuery.forEach(SRC /蒙戈/殼/ query.js:281:9) 在(殼):1:409 – user29578

1

我建議你做$match第一操作在您的管道爲$match運營商只能使用一個索引,如果它是第一個在聚合管道:

var userstats = db.order.aggregate([{ 
    $match : { 
     status :"DELIVERED", 
     customer : { $in : userIds } 
    } 
}, { 
    $sort : { 
     customer : 1, 
     dateCreated : 1 
    } 
}, { 
    $group : { 
     _id : "$customer", 
     orders : { $sum : 1 }, 
     firstOrderDate: { $first : "$dateCreated" }, 
     lastOrderDate : { $last:"$dateCreated" } 
    } 
}]); 

你也應該statuscustomer添加一個索引如果你還沒有定義一個:

db.delivery.createIndex({status:1,customer:1}) 
+0

有這方面的幫助:) http://stackoverflow.com/questions/35403356/mongo-find-query-on-joda-datetime – user29578