sails.js中sails-mongo如何实现除求和求平均外的MongoDB聚合查询
基于sails-mongo实现全量MongoDB聚合查询
sails-mongo从1.0版本开始就开放了底层MongoDB原生驱动的访问入口,完全可以实现和Mongoose的Model.aggregate()等效的全部聚合能力,不需要额外引入Mongoose绕路,也能灵活编写除求和、求平均之外的所有聚合逻辑,实际实现方式很直接:
直接获取底层集合实例
sails的Waterline ORM会自动管理Mongo连接池,不需要自己重新创建数据库连接。只需要在模型的静态方法中,通过datastore的manager属性拿到原生Mongo客户端,再通过对应集合名获取可直接操作的Collection对象,即可调用原生的.aggregate()方法,支持所有MongoDB聚合管道阶段的操作。
举个实际的聚合场景示例,实现按地区统计近30天的销售数据(包含求最大/最小订单额、收集订单ID等非sum/avg的聚合逻辑):// api/models/Sale.js module.exports = { attributes: { region: { type: 'string' }, amount: { type: 'number' }, orderTime: { type: 'string', columnType: 'date' }, userId: { type: 'number' } }, async getRegionStats() { // 获取底层Mongo连接管理器 const mongoClient = this.getDatastore().manager; // 获取当前模型对应的sales集合 const saleCollection = mongoClient.db(this.getDatastore().config.database).collection('sales'); // 编写全原生Mongo聚合管道 const pipeline = [ // 匹配近30天的有效订单 { $match: { orderTime: { $gte: new Date(Date.now() - 2592000000) } } }, // 按地区分组,支持所有聚合操作表达式 { $group: { _id: '$region', // 统计分组内的订单总数 orderCount: { $sum: 1 }, // 求组内最大单笔订单金额(非sum/avg操作) maxSingleAmount: { $max: '$amount' }, // 求组内最小单笔订单金额(非sum/avg操作) minSingleAmount: { $min: '$amount' }, // 将组内的所有订单ID收集为数组(非sum/avg操作) orderIdList: { $push: '$_id' }, // 收集组内去重后的订单金额值(非sum/avg操作) uniqueAmountList: { $addToSet: '$amount' } } }, // 按订单量倒序排序 { $sort: { orderCount: -1 } } ]; // 执行聚合并把游标转成普通数组返回 return await saleCollection.aggregate(pipeline).toArray(); } };常见踩坑调整
- 字段映射注意:sails-mongo默认会把模型的
id字段映射为Mongo原生的_id字段,写聚合管道时要按照Mongo实际存储的字段名编写,不要直接套用Waterline的字段映射逻辑写管道,否则会出现取不到值的问题。 - 游标处理:原生聚合方法返回的是Mongo游标对象,必须调用
.toArray()转换为普通JS数组后再传递给上层逻辑,否则会出现响应序列化失败的问题。 - 低版本兼容:如果使用sails 0.12搭配的旧版sails-mongo,需要在datastore配置里开启
raw结果开关,才能正常获取底层Collection实例,适配旧版本的聚合逻辑调用。
- 字段映射注意:sails-mongo默认会把模型的
全场景聚合适配
所有不依赖sum、avg的聚合操作都可以直接在管道中编写,比如联表查询($lookup)、数组截取($slice)、字段重命名($project)、文档拆解($unwind)、条件分支计算($cond)等,实现逻辑和直接在Mongo Shell里写聚合管道完全一致,不需要做额外的封装转换。
举个带联表查询的聚合示例:// 在User模型中聚合,关联查询每个活跃用户的最近3笔订单 const pipeline = [ { $match: { status: 'active' } }, { $lookup: { from: 'sales', localField: '_id', foreignField: 'userId', as: 'userOrders' } }, { $project: { userName: 1, recentThreeOrders: { $slice: ['$userOrders', -3] } } } ];
这种实现方式比引入Mongoose更轻量,不会出现双ORM连接池冲突的问题,聚合执行性能和Mongoose提供的
Model.aggregate()完全一致。
内容的提问来源于stack exchange,提问作者Vinita
相关产品推荐
相关产品推荐

