You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sequelize如何在嵌套Include中为Chapter模型应用Distinct?

嘿,这个问题我之前也碰到过!在Sequelize的嵌套关联里做特定字段的去重确实有点 tricky,不过针对PostgreSQL的环境,有几个靠谱的方案可以解决,我给你一步步拆解:

方案一:用PostgreSQL专属的DISTINCT ON(最简洁推荐)

PostgreSQL支持DISTINCT ON语法,可以指定按某个字段去重,同时保留你想要的那条数据(比如最新创建的Chapter)。Sequelize从v5开始就支持这个选项,直接在Chapter的include配置里加就行:

const books = await Book.findAll({
  include: [
    {
      model: Part,
      include: [
        {
          model: Chapter,
          attributes: ['id', 'topic', 'content', 'createdAt'], // 按需指定返回字段
          distinctOn: ['topic'], // 按topic字段去重
          // 注意:DISTINCT ON要求排序的第一个字段必须是去重字段,后面可以加你想要的排序规则
          order: [['topic', 'ASC'], ['createdAt', 'DESC']]
        }
      ]
    }
  ]
});

这个方案的好处是代码简洁,性能也不错,直接利用PostgreSQL的原生语法,能确保每个topic只返回一条(这里是每个topic下最新创建的Chapter)。

方案二:使用group分组(适合需要聚合字段的场景)

如果你的需求不仅是去重,还要对其他字段做聚合(比如取每个topic下最新的id、最长的content等),可以用group选项配合聚合函数:

const books = await Book.findAll({
  include: [
    {
      model: Part,
      include: [
        {
          model: Chapter,
          attributes: [
            [sequelize.fn('MAX', sequelize.col('id')), 'id'], // 取每个topic下最大的id(最新的)
            'topic',
            [sequelize.fn('MAX', sequelize.col('content')), 'content'], // 取对应最新的content
            [sequelize.fn('MAX', sequelize.col('createdAt')), 'createdAt']
          ],
          group: ['topic'], // 按topic分组
          order: [['topic', 'ASC'], [sequelize.fn('MAX', sequelize.col('createdAt')), 'DESC']]
        }
      ]
    }
  ]
});

不过这个方案要注意:聚合函数的选择要符合你的业务需求,比如如果同一个topic下的content不一样,用MAX可能不是你想要的,这时候就不如方案一灵活。

方案三:子查询过滤(最灵活,适合复杂逻辑)

如果你的去重逻辑更复杂(比如要同时按topic和partId去重,或者需要更复杂的筛选),可以用子查询先找出每个topic下符合条件的Chapter,再关联到主查询里:

const books = await Book.findAll({
  include: [
    {
      model: Part,
      include: [
        {
          model: Chapter,
          where: sequelize.literal(`
            "Chapter"."createdAt" = (
              SELECT MAX("createdAt") 
              FROM "Chapters" 
              WHERE "Chapters"."topic" = "Chapter"."topic" 
              AND "Chapters"."partId" = "Chapter"."partId"
            )
          `),
          order: [['createdAt', 'DESC']]
        }
      ]
    }
  ]
});

这里的子查询会找出同一个Part下,每个topic最新创建的Chapter,然后主查询只返回这些符合条件的Chapter,完美实现"每个Part下的Chapter按topic唯一"的需求。

注意事项

  1. 如果你用方案一的distinctOn,要确保你的Sequelize版本在v5及以上,并且数据库是PostgreSQL(MySQL不支持这个语法)。
  2. 使用distinctOn时,排序的第一个字段必须是你指定的去重字段,否则PostgreSQL会抛出语法错误。
  3. 子查询方案要注意关联外键(比如partId),避免不同Part下的Chapter被错误去重。

内容的提问来源于stack exchange,提问作者Diego

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:25:22