MapReduce来确定分类帐中的贷方余额

我有一个交易分类帐,借记和贷记交易被存储。 我需要确定account : 121贷方余额account : 121 。 我find了一个方法来做到这一点。 只是,我不明白我所做的一半,为什么这是有效的。

 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文档条目,但是大部分都是我的头脑。 如果你只是给我的代码,我会尽力去理解它,虽然一个解释将是非常有用的。

你可以采取的一种方法是使用聚合框架 。 达到所需结果的聚合pipe道是一个你有一个$match运算符,在给定的查询条件下过滤集合中的文档,在你的情况下,这将是帐号为121的文档。

更进一步的pipe道是魔术发生的地方。 $project操作员通过添加一个额外的字段来重新整形文档,余额将用于计算该帐户的总额。 余额字段将使用$cond$multiply操作符来获取其值,条件是如果信用值为假,则从数量乘以-1得到它的值,否则它将是默认的正数量值。

$projectpipe道步骤之后是$group操作员阶段,该阶段随后通过账户号码字段对文档进行分组时计算总的汇总余额,这使用$sum操作符来累加所有的余额值。

因此,最后你会得到下面的聚合pipe道:

 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集合添加一些testing文档来演示这一点:

 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 } ]) 

上面的聚合stream水线会给出如下结果:

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

为了在Mongoose中实现这个function,可以使用聚合pipe道生成器 ,如下所示:

 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); 

更新

如果您仍然喜欢Map-Reduce选项,则可以尝试使用与上面相同的概念的以下Map-Reduce操作; 你的地图function会发出一个键值对,其中包括一个修改后的关键balance基础上,如果信用值是真实的,那么balance字段将有一个积极的amount ,否则将是一个负面的标准。

然后reduce函数通过将values数组减去其元素之和来收集和聚合聚集的数据。 然后,MongoDB将结果存储在一个集合outputTx 。 因此你的Map-Reduce操作看起来像这样:

 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 } 

聚合框架是一个更可读的(可能更有效)的方式来实现预期的结果:

我让你适应mongoose,但从mongoshell:

 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借方情况下的-$amount ;
  • 最后, $group阶段将总结(正面或负面)信用来计算总余额。