从MongoDB迁移至PostgreSQL:Mongoose聚合转Sequelize实现
Sequelize替代Mongoose分组聚合的实现方案
原Mongoose代码的核心逻辑是:按name字段分组,保留每个分组中最先出现的完整文档,最终仅返回该文档的name和version字段。以下是对应的Sequelize实现方案,适配PostgreSQL特性:
方案一:利用PostgreSQL DISTINCT ON(简洁高效)
PostgreSQL的DISTINCT ON语法可以直接实现"按字段分组取第一条"的需求,和原逻辑完全匹配:
async listTestModels(): Promise<TestModel[]> { return TestModel.findAll({ attributes: ['name', 'version'], distinct: true, // 排序规则决定分组内取哪条记录,和MongoDB的$first逻辑对应 // 若原逻辑是按插入顺序取第一条,可替换为['name', 'ASC', 'id', 'ASC'] order: [['name', 'ASC']], // 可选:返回纯JS对象而非Sequelize实例 raw: true }); }
方案二:窗口函数(灵活适配复杂场景)
如果需要更精确控制分组内的筛选规则(比如按特定字段排序取第一条),可以用ROW_NUMBER()窗口函数实现:
async listTestModels(): Promise<TestModel[]> { const { Op } = require('sequelize'); // 子查询:给每个name分组的记录按指定规则编号 const subQuery = TestModel.findAll({ attributes: [ 'name', 'version', [TestModel.sequelize.literal('ROW_NUMBER() OVER (PARTITION BY name ORDER BY createdAt ASC)'), 'row_num'] ], raw: true }); // 主查询:仅取每个分组中编号为1的记录 return TestModel.findAll({ attributes: ['name', 'version'], where: { [Op.and]: TestModel.sequelize.literal(`(name, version) IN (SELECT name, version FROM (${subQuery.query}) AS sub WHERE row_num = 1)`) }, raw: true }); }
注意事项
- 确保Sequelize的
TestModel模型已正确映射PostgreSQL表结构,name和version字段对应无误。 - 若原Mongoose的
$first依赖插入顺序,需在排序规则中加入自增主键(如id)或创建时间字段(如createdAt),保证取数逻辑一致。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

