node.js - MapReduce 确定分类账中的信用余额

标签 node.js mongodb mongoose

我有一个交易分类账,其中存储借方和贷方交易。我需要确定帐户: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 。实现所需结果的聚合管道是一个 $match 的管道。运算符,按给定的查询条件过滤集合中的文档,在您的情况下,这将是帐号为 121 的文档。

管道的更深处就是奇迹发生的地方。 $project运算符(operator)通过添加额外字段“余额”来 reshape 文档,该字段将用于计算该帐户的总计。余额字段将使用 $cond$multiply运算符获取其值,条件是如果信用值为 false,则从金额乘以 -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);

-- 更新 --

如果您仍然喜欢 Map-Reduce 选项,您可以尝试以下 Map-Reduce 使用与上面相同概念的操作;您的 map 函数将发出一个键值对,其中包含修改后的键 balance,其条件是如果信用值为 true,则 balance 字段将具有正金额,否则将为负。

然后,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
}

关于node.js - MapReduce 确定分类账中的信用余额,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30144145/

相关文章:

node.js - 从 Kubernetes NGINX Ingress Controller 公开服务总是返回 502 Bad Gateway

javascript - 根据某些条件,使用 mongodb 聚合从数组中获取对象以及同一级别的其他字段

mongodb - 如何在不依赖MongoDB的情况下启动spring-boot应用程序?

ruby-on-rails - 如何在使用 db.collection.insert() 时即时切换 MongoDB 数据库?

javascript - 基于条件的文档中各个字段的聚合计数

node.js - 无法从 Firebase 集合中删除文件

javascript - NodeJs 堆 js 模块 : Heap is not a constructor

node.js - 自定义环境变量未从 NodeJS 读取

node.js - mongodb 服务在说 serverStatus 非常慢后崩溃

node.js - Mongoose 唯一电子邮件地址验证