如何用mongoose在Nodejs中实现分组计算的功能?
如何用mongoose在Nodejs中实现分组计算的功能?
我现用nodejs+mongodb+mongoose. 我有个数据结构MyModel
{ depid:String, userid:String, username:String } 测试数据: {depid:‘a’,userid:‘1’,username:‘张1’} ,{depid:‘b’,userid:‘2’,username:‘张2’} ,{depid:‘a’,userid:‘3’,username:‘张3’} ,{depid:‘c’,userid:‘4’,username:‘张4’} ,{depid:‘c’,userid:‘5’,username:‘张5’} ,{depid:‘d’,userid:‘6’,username:‘张6’} 我想传入一个depid=‘a,c,b’ 值,根据depid获取分组数量,想要的结果: { depid:‘a’,count:2 ,depid:‘c’:count:2 ,depid:‘b’,count:1 } 请问该如何实现呢? 谢谢!
要使用 Mongoose 在 Node.js 中实现分组计算功能,可以利用 MongoDB 的聚合框架(Aggregation Framework)。以下是如何实现你描述的需求的步骤和示例代码。
步骤
- 定义模型:首先定义
MyModel
模型。 - 构建聚合管道:使用聚合管道来实现分组计算。
- 执行查询:通过模型调用
aggregate
方法执行查询。
示例代码
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// 定义 MyModel 模型
const MyModelSchema = new Schema({
depid: String,
userid: String,
username: String
});
const MyModel = mongoose.model('MyModel', MyModelSchema);
// 连接到 MongoDB
mongoose.connect('mongodb://localhost/test', { useNewUrlParser: true, useUnifiedTopology: true });
// 要查询的部门ID列表
const depids = ['a', 'c', 'b'];
// 构建聚合管道
const pipeline = [
{
$match: { // 过滤条件
depid: { $in: depids } // 只选择指定的 depid
}
},
{
$group: { // 分组计算
_id: "$depid", // 分组依据
count: { $sum: 1 } // 计算每个分组的数量
}
},
{
$project: { // 重新格式化输出
_id: 0, // 不显示 _id 字段
depid: "$_id", // 将 _id 重命名为 depid
count: 1 // 显示 count 字段
}
}
];
// 执行聚合查询
MyModel.aggregate(pipeline)
.then(result => {
console.log(result);
// 输出结果类似于:
// [{ depid: 'a', count: 2 }, { depid: 'c', count: 2 }, { depid: 'b', count: 1 }]
})
.catch(err => {
console.error(err);
});
解释
- $match 阶段:用于过滤出符合条件的数据。这里我们只选择了
depid
属于指定列表中的文档。 - $group 阶段:用于按
depid
字段进行分组,并计算每组的数量。 - $project 阶段:用于重新格式化输出结果,去掉默认的
_id
字段,并将_id
重命名为depid
。
这样,你就能够根据给定的 depid
列表获取每个分组的数量了。
aggregate
大不了 mapreduce,但不要指望速度
具体怎么做,能讲讲吗。
用aggregate有一个问题,用$match无法匹配ObjectId
OutdoorApply.aggregate( [ {$match : {mobile:'18988889991'}} //这样可以,因为mobile字段是字符串 {$match : {outdoorid:'5463fda5b0687cad05c3c7c0'}} //这样就无法匹配 ,因为outdoorid是ObjectId // {$group:{ _id: {'outdoorid':'$outdoorid'}, count: { $sum: 1 }}} ], function (err, res) { if (err){ console.log('outdoorDao.getOutdoorApplyCount Error'+err); return callback(err,null); } return callback(err,res); } );
谢谢了,是的,是要转换的。
OutdoorApply.aggregate( [ {$match : {outdoorid: {$in:ids.map(function(id){return new ObjectId(id);})}}} ,{$group:{_id :"$outdoorid" ,count:{$sum:1}}} // ,{$group:{ _id: {'outdoorid':'$outdoorid'}, count: { $sum: 1 }}} ], function (err, res) { if (err){ console.log('outdoorDao.getOutdoorApplyCount Error'+err); return callback(err,null); } return callback(err,res); } );
总听到有人说aggredate效率不高,但不知有没有依据呢。 如果小数据,用这种方式也可以:
var q={'outdoorid':{$in:ids}}; var outdoors=OutdoorApply.find(q,'outdoorid',{},function(err,docs){ if(err){ console.log('outdoorDao.getOutdoorApplyCount Error'+err); return callback(err,null); } var uhash={}; docs.forEach(function(d){ if(!uhash[d.outdoorid]){ uhash[d.outdoorid]=0; } uhash[d.outdoorid]++; }); return callback(err,uhash); });
你可以使用Mongoose的aggregate
方法来实现这个功能。aggregate
方法允许你使用MongoDB的聚合管道来处理数据。以下是如何使用Mongoose来实现你的需求:
const mongoose = require('mongoose');
const MyModel = mongoose.model('MyModel', new mongoose.Schema({
depid: String,
userid: String,
username: String
}));
async function getGroupCounts(depids) {
const pipeline = [
{ $match: { depid: { $in: depids } } },
{ $group: { _id: "$depid", count: { $sum: 1 } } }
];
const result = await MyModel.aggregate(pipeline);
return result.reduce((acc, item) => {
acc[item._id] = item.count;
return acc;
}, {});
}
// 调用函数
getGroupCounts(['a', 'c', 'b']).then(result => {
console.log(result);
});
解释
- 定义模型:首先,我们定义了一个Mongoose模型
MyModel
,它包含了depid
、userid
和username
字段。 - 构建聚合管道:
$match
:过滤出符合depid
条件的数据。$group
:按depid
分组,并计算每个分组的数量。
- 调用
aggregate
:执行聚合操作并获取结果。 - 格式化结果:将结果从数组形式转换为对象形式,便于直接访问每个分组的计数。
这段代码会输出符合指定depid
值的数据分组计数,格式如你所期望的那样。