2015-05-09 65 views
1

我有一個存款借記和貸記交易的交易分類賬。我需要確定account : 121的貸方餘額。我找到了一個辦法。只是,我不瞭解我所做的一半,以及爲什麼這是有效的。MapReduce來確定分類帳中的貸方餘額

var dummyschema = mongoose.Schema({ 
    account: Number, 
    refAccount: Number, 
    credit: Boolean, 
    amount: Number, 

}); 

var dummyTx = mongoose.model('dummyTx', dummyschema); 

var c = {}; 
c.map = function() {emit("credit", this.amount);}; 
c.reduce = function(key, values) { return Array.sum(values);}; 
c.query = { account : 121, credit: true }; 
c.out = {inline:1}; 

var d = {}; 
d.map = function() {emit("debit", this.amount);}; 
d.reduce = function(key, values) { return Array.sum(values);}; 
d.query = { account : 121, credit: false }; 
d.out = {inline:1}; 

dummyTx.mapReduce(c, function (error, credit) { 
    dummyTx.mapReduce(d, function (err, debit) { 
    console.log(credit['0'].value - debit['0'].value); 
    }); 
}); 

請問您是否有更好的方法來做到這一點。我閱讀了MapReduce文檔條目,但它大部分都飛過了我的腦海。如果你只是給我代碼,我會試着去理解它,儘管解釋會更有幫助。

回答

2

一種方法是使用aggregation framework。達到所需結果的聚合管道是一個您有$match運算符的運算符,該運算符可按給定的查詢條件過濾集合中的文檔,在您的情況下將是帳戶編號爲121的文檔。

進一步向下流水線是魔法發生的地方。 $project操作員通過添加額外字段來重新整形文檔,餘額將用於計算該帳戶的總額。餘額字段將使用$cond$multiply運算符根據以下條件獲取其值:如果信用值爲假,則它將從數量乘以-1得到它的值,否則它將是默認的正數量值。

$project管道的步驟是$group操作階段,然後計算出總合計餘額時,文件由帳戶號碼字段分組之後,這使得利用$sum運營商加起來所有的平衡值。

因此,在你結束了以下聚合管道末尾:

db.dummyTx.aggregate([ 
    { 
     "$match": { "account": 121 } 
    }, 
    { 
     "$project": { 
      "balance": { 
       "$cond": [ 
        { 
         "$eq": [ "$credit", false ] 
        }, 
        { 
         "$multiply": [ -1, "$amount" ] 
        }, 
        "$amount" 
       ] 
      }, 
      "account": 1 
     } 
    }, 
    { 
     "$group": { 
      "_id": "$account", 
      "total": { 
       "$sum": "$balance" 
      } 
     } 
    } 
]) 

讓我們通過添加一些測試文檔的dummyTx收集證明這一點:

db.dummyTx.insert([ 
    { account: 121, credit: true, amount: 5 }, 
    { account: 121, credit: true, amount: 2 }, 
    { account: 121, credit: false, amount: 10 }, 
    { account: 121, credit: false, amount: 2 } 
]) 

上述聚合管道將給結果如下:

/* 1 */ 
{ 
    "result" : [ 
     { 
      "_id" : 121, 
      "total" : -5 
     } 
    ], 
    "ok" : 1 
} 

要在Mongoose中實現這個,你可以使用aggregation pipeline builder如下:

Model.aggregate() 
     .match({"account": 121}) 
     .project({ "balance": { "$cond": [ 
        {"$eq": [ "$credit", false ]}, 
        {"$multiply": [ -1, "$amount" ]}, 
        "$amount" 
       ]}, 
      "account": 1}) 
     .group({"_id": "$account","total": {"$sum": "$balance"}}) 
     .exec(callback); 

- UPDATE -

如果你還是喜歡的map-reduce選項,你可以嘗試使用相同以下Map-Reduce操作概念如上;您的地圖功能會發出一個鍵值對,其中包含一個修改後的密鑰balance,其基礎是如果信用值爲真,那麼balance字段將具有正數amount,否則將是負值。

然後reduce函數通過將values數組減少到其元素之和來收集和濃縮聚合數據。然後MongoDB將結果存儲在集合outputTx中。因此,你的地圖,減少操作是這樣的:

var d = {}, 
    map = function() { 
     var balance = this.credit ? this.amount : -1 * this.amount; 
     emit("balance", balance); 
    }, 
    reduce = function(key, values) { return Array.sum(values);}; 
d.query = { account : 121 }; 
d.out = "outputTx"; 

db.dummyTx.mapReduce(map, reduce, d); 

查詢輸出收集db.outputTx.find({})會給結果:

/* 1 */ 
{ 
    "_id" : "balance", 
    "value" : -5 
} 
+1

大,心有靈犀的同時一樣...! ;) –

+0

@SylvainLeroux你可以再說一遍! – chridam

+1

非常感謝。聚合框架確實看起來更具可讀性,並且在我閱讀文檔時,我明白它更加靈活多變!我想我會花一些時間閱讀這個:D – amingilani

1

aggregation framework是達到預期的結果更具可讀性(而且可能更有效)的方式:

我讓你適應,要貓鼬,但是從蒙戈外殼:

db.dummyTx.aggregate([ 
    {$match: {account:121}}, 
    {$project: { credit: {$cond: ["$credit", 
            "$amount", 
            {$subtract: [0, "$amount"]} ]} }}, 
    {$group: { _id: "$account", balance: {$sum: "$credit"}}} 
]) 
  • $match階段只保留帳號#121;
  • $project階段將合成一個字段credit其值爲$amount在信用的情況下,-$amount在借方的情況下;
  • 最後,$group階段將總結(正面或負面)信用來計算總餘額。你可以採取