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