MapReduce 确定账本中的贷方余额
MapReduce to determine credit balance in a ledger
我有一个交易分类帐,其中存储借方和贷方交易。我需要确定 account : 121
的信用余额。我找到了一种方法。只是,我不明白我所做的一半,以及为什么这样做。
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 文档条目,但它大多飞过我的脑海。如果你只是给我代码,我会试着理解它。虽然解释会更有帮助。
aggregation framework 是实现预期结果的更易读(并且可能更有效)的方法:
我让你适应 mongoose,但是从 mongo shell:
db.dummyTx.aggregate([
{$match: {account:121}},
{$project: { credit: {$cond: ["$credit",
"$amount",
{$subtract: [0, "$amount"]} ]} }},
{$group: { _id: "$account", balance: {$sum: "$credit"}}}
])
您可以采用的一种方法是使用 aggregation framework. The aggregation pipeline that achieves the desired result is one where you have a $match
运算符按给定的查询条件过滤集合中的文档,在您的情况下是帐号为 121 的文档。
管道的更下方是奇迹发生的地方。 $project
operator reshapes the documents by adding an extra field, balance which would be used to calculate the total for that account. The balance field would use the $cond
and $multiply
运算符根据以下条件获取其值:如果信用值为 false,则从金额乘以 -1 获取其值,否则它将是默认的正金额值。
在 $project
pipeline step is the $group
operator stage which then calculates the total aggregated balance when the documents are grouped by the account number field, this makes use of the $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);
-- 更新 --
如果您仍然喜欢 Map-Reduce 选项,您可以尝试以下 Map-Reduce 操作,它使用与上述相同的概念;您的映射函数将根据以下条件发出一个键值对,其中包含修改后的键 balance
,如果信用值为真,则 balance
字段将具有正值 amount
,否则这将是负面的。
reduce 函数然后通过将值数组缩减为其元素的总和来收集和压缩聚合数据。 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
}
我有一个交易分类帐,其中存储借方和贷方交易。我需要确定 account : 121
的信用余额。我找到了一种方法。只是,我不明白我所做的一半,以及为什么这样做。
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 文档条目,但它大多飞过我的脑海。如果你只是给我代码,我会试着理解它。虽然解释会更有帮助。
aggregation framework 是实现预期结果的更易读(并且可能更有效)的方法:
我让你适应 mongoose,但是从 mongo shell:
db.dummyTx.aggregate([
{$match: {account:121}},
{$project: { credit: {$cond: ["$credit",
"$amount",
{$subtract: [0, "$amount"]} ]} }},
{$group: { _id: "$account", balance: {$sum: "$credit"}}}
])
您可以采用的一种方法是使用 aggregation framework. The aggregation pipeline that achieves the desired result is one where you have a $match
运算符按给定的查询条件过滤集合中的文档,在您的情况下是帐号为 121 的文档。
管道的更下方是奇迹发生的地方。 $project
operator reshapes the documents by adding an extra field, balance which would be used to calculate the total for that account. The balance field would use the $cond
and $multiply
运算符根据以下条件获取其值:如果信用值为 false,则从金额乘以 -1 获取其值,否则它将是默认的正金额值。
在 $project
pipeline step is the $group
operator stage which then calculates the total aggregated balance when the documents are grouped by the account number field, this makes use of the $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);
-- 更新 --
如果您仍然喜欢 Map-Reduce 选项,您可以尝试以下 Map-Reduce 操作,它使用与上述相同的概念;您的映射函数将根据以下条件发出一个键值对,其中包含修改后的键 balance
,如果信用值为真,则 balance
字段将具有正值 amount
,否则这将是负面的。
reduce 函数然后通过将值数组缩减为其元素的总和来收集和压缩聚合数据。 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
}