如何用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 } 请问该如何实现呢? 谢谢!


8 回复

要使用 Mongoose 在 Node.js 中实现分组计算功能,可以利用 MongoDB 的聚合框架(Aggregation Framework)。以下是如何实现你描述的需求的步骤和示例代码。

步骤

  1. 定义模型:首先定义 MyModel 模型。
  2. 构建聚合管道:使用聚合管道来实现分组计算。
  3. 执行查询:通过模型调用 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);
});

解释

  1. 定义模型:首先,我们定义了一个Mongoose模型MyModel,它包含了depiduseridusername字段。
  2. 构建聚合管道
    • $match:过滤出符合depid条件的数据。
    • $group:按depid分组,并计算每个分组的数量。
  3. 调用aggregate:执行聚合操作并获取结果。
  4. 格式化结果:将结果从数组形式转换为对象形式,便于直接访问每个分组的计数。

这段代码会输出符合指定depid值的数据分组计数,格式如你所期望的那样。

回到顶部