一个查询中有两个总和

时间:2014-09-22 09:04:17

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我有一个mongodb(Mongoose实体)代表我的应用程序中的财务条目:

var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.Types.ObjectId;

var transactionSchema = mongoose.Schema({

    description     : String,
    amount          : { type:Number, min:0.01, required:true }, 
    dateEntry       : { type:Date, default:Date.now }, 
    dateAdded       : { type:Date, default:Date.now }, 
    positive        : { type:Boolean, default:false }, 

    category        : { type: ObjectId, ref: 'Category' },
    user            : { type: ObjectId, ref: 'User' }

});

module.exports = mongoose.model('Transaction', transactionSchema);

我想平衡我的交易:我的第一种方法是做出一些正面条目,然后是另一个负数条目的总和,最后从正面条目中减去负数:

async.parallel({
                totalPositive: function(callback) {
                    var matcher = baseSearch;
                    matcher.positive = true;
                    Transaction.aggregate(
                        { $match: matcher },
                        { $group: {_id:null,sum:{$sum: '$amount'}} },
                        function(err,result) {
                            callback(err,result);
                        }
                    );
                },
                totalNegative: function(callback) {
                    var matcher = baseSearch;
                    matcher.positive = false;
                    Transaction.aggregate(
                        { $match: matcher },
                        { $group: {_id:null,sum:{$sum: '$amount'}} },
                        function(err,result) {
                            callback(err,result);
                        }
                    );
                }
            }, function(err, results) {
                if (err) {
                    return res.json({error:err.message});
                }
                results.balance = results.totalPositive[0].sum - results.totalNegative[0].sum;
                res.json(results);
            });

我想知道是否可以通过只返回一个查询来保存一个查询。

有什么想法吗?或者这是最好的解决方案? 谢谢

1 个答案:

答案 0 :(得分:1)

您可以使用条件分组,然后只进行最终项目来进行数学运算:

Transaction.aggregate(
    [
        { "$group": {
            "_id": null,
            "totalPositive": {
                "$cond": [ "$positive", "$amount", 0 ]
            },
            "totalNegative": {
                "$cond": [ "$positive", 0, "$amount" ]
            }
        }},
        { "$project": {
            "totalBalance": { "$subtract": [ "$totalPositive", "$totalNegative" ] }
        }}
    ],
    function(err,result) {

    }
)

所以只评估是否$sum"金额"取决于什么"积极"有true/false的任何一个。然后在以下管道中使用$subtract运算符。

更好的是,只需设置"标志"值"总和"在一个小组阶段:

Transaction.aggregate(
    [
        { "$group": {
            "_id": null,
            "totalBalance": {
                "$sum": {
                    "$cond": [
                         "$positive",
                         "$amount",
                         { "$subtract": [ 0, "$amount" ] }
                    ]
                }
        }}
    ],
    function(err,result) {

    }
);

因此,当您只需一次完成此操作时,就不需要并行查询。

"肉"每个案例主要在$cond运算符中,作为评估的三元运算符。链接那里,但如果你不熟悉,那么这意味着一种提供"内联" if/then/else评估。所以有一个"测试"作为第一个参数,其中true在返回结果中使用第二个参数,否则返回false第三个参数。

SQL to aggregation mapping的文档中还提供了其他示例,这些示例为那些习惯于SQL的人提供了几个常见的示例,或者至少习惯于#34;声明的"问题解决的语法。

建议对aggregation framework进行一般性阅读,因为与基本的.find()方法调用相比,SELECT thisfield where anotherfield = "this"与高级SQL的每个其他排列之间的差异基本上都是SQL术语。