NoSQL专题

在Node.js + MongoDB中实现聚合查询的案例源码

  该案例以计算成百上千个账户中得到每个账户的余额,这种密集计算使用数据库进行算法要比使用Node.JS有效得多。案例源码下载: gist

首先我们要定义MongoDB的Schema,需要两个Accout和Record, 账户Accout有账户名称,货币名称和一组记录Record。

// Record Schema

var Record = new Schema({

    amount: { type: Number, required: true },

    date: { type: Date, required: true },

    description: { type: String }

})

 

// Account Schema

var Account = new Schema({

    name: { type: String, required: true },

    currency: { type: String, required: true },

    created: { type: Date, default: Date.now },

    records: [Record]

});

 

//Define Models

var AccountModel = mongoose.model('Account', Account);

var RecordModel = mongoose.model('Record', Record);

下面是mongoose对MongoDB实现聚合,主要利用其聚合框架

var getBalance = function(accountId) {

    db.AccountModel.aggregate([

        { $match: {

            _id: accountId

        }},

        { $unwind: "$records" },

        { $group: {

            _id: "$_id",

            balance: { $sum: "$records.amount"  }

        }}

    ], function (err, result) {

        if (err) {

            console.log(err);

            return;

        }

        console.log(result);

    });

}

$match用于根据要求查询文档, $unwind 从数组中获得一个元素,然后复制这个元素的父文档,最后使用$group计算每个账户的余额总数。

MongoDB的聚合框架原理如下图:使用$match类似SQL的where语句,查询所有文档字段status是"A"的文档,$group是将相同id为"A123"的元素合并,合并后的字段名是total,值是根据{ $sum: "$amount"  }来计算,{ $sum表示计算总和。

最后,我们创建插入数据的功能:

var insertAccount = function(account, callback) {

    var Account = new AccountModel();

    Account.name = account.name;

    Account.currency = account.currency;

    Account.records = [];

 

    Account.save(function(err) {

        if (err) {

            console.log(err);

            return null;

        }

 

        return callback(Account._id);

    });

}

 

var insertRecords = function(accountId, record, numberOfRecords, callback) {

    AccountModel.findOne({_id:accountId}, function(err, account) {

        if (err) {

            console.log(err);

            return ;

        }

 

        for (var i = 0; i < numberOfRecords; i++) {

            var Record = new RecordModel();

            Record.amount = record.amount;

            Record.date = new Date();

            Record.is_expense = record.is_expense;

 

            account.records.push(Record);

        }

 

        account.save(function(err2) {

            if (err2) {

                console.log(err2);

            }

 

            return callback();

        });

    });

}

 

insertAccount({name:"First Account", currency: "Dollar"}, function(accountId) {

    if (accountId == null) {

        console.log("accountId is null");

        return ;

    }

 

    insertRecords(accountId, {amount:123.5}, 1000, function() {

        getBalance(accountId);

    });

});

NodeJS专题