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

SequelizeJS中复杂多对多(多对多对多)关联的FindAll查询问题

嘿,关于Sequelize里这种带额外字段的多对多关联查询,我刚好有不少经验,给你一步步拆解一下!核心思路是把中间表显式定义成独立模型,而不是让Sequelize自动生成,这样就能轻松访问中间表的额外字段(比如这里的price)了。

第一步:定义所有模型

首先得把三个模型都定义好,包括中间表CompanyProduct:

Company模型

const { DataTypes } = require('sequelize');

const Company = sequelize.define('Company', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    comment: '公司名称'
  }
});

Product模型

const Product = sequelize.define('Product', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  name: {
    type: DataTypes.STRING,
    allowNull: false,
    comment: '产品名称'
  }
});

中间表CompanyProduct模型

这个是关键,要包含price字段,以及关联两边的外键:

const CompanyProduct = sequelize.define('CompanyProduct', {
  price: {
    type: DataTypes.DECIMAL(10, 2),
    allowNull: false,
    comment: '该公司对应产品的售价'
  },
  // 外键可以显式定义,也可以让关联自动生成,显式定义更清晰
  companyId: {
    type: DataTypes.INTEGER,
    references: {
      model: Company,
      key: 'id'
    }
  },
  productId: {
    type: DataTypes.INTEGER,
    references: {
      model: Product,
      key: 'id'
    }
  }
});
第二步:设置关联关系

接下来要建立模型之间的关联,因为中间表有额外字段,要用belongsToMany时指定through为我们定义的中间表模型,同时明确外键和别名:

// 公司 ↔ 产品 的多对多关联
Company.belongsToMany(Product, {
  through: CompanyProduct, // 指定中间表模型
  foreignKey: 'companyId', // 中间表中指向Company的外键
  otherKey: 'productId', // 中间表中指向Product的外键
  as: 'products' // 别名,查询时用来引用关联的产品
});

Product.belongsToMany(Company, {
  through: CompanyProduct,
  foreignKey: 'productId',
  otherKey: 'companyId',
  as: 'companies' // 别名,查询时用来引用关联的公司
});

// 额外:给中间表和两边模型建立一对多关联(可选,但某些场景下查询更灵活)
Company.hasMany(CompanyProduct, { foreignKey: 'companyId', as: 'productPricings' });
CompanyProduct.belongsTo(Company, { foreignKey: 'companyId' });

Product.hasMany(CompanyProduct, { foreignKey: 'productId', as: 'companyPricings' });
CompanyProduct.belongsTo(Product, { foreignKey: 'productId' });
第三步:执行FindAll查询

现在就可以写各种查询了,下面是几个常用场景:

场景1:查询所有公司,附带它们的产品和对应售价

const allCompaniesWithProducts = await Company.findAll({
  include: [
    {
      model: Product,
      as: 'products', // 对应关联时设置的别名
      through: {
        attributes: ['price'], // 指定要返回的中间表字段
        as: 'pricing' // 给中间表数据起个友好的别名,方便后续取值
      }
    }
  ]
});

// 返回结果示例:
// allCompaniesWithProducts[0] = {
//   id: 1,
//   name: 'XX公司',
//   products: [
//     {
//       id: 1,
//       name: '汽车',
//       pricing: { price: 250000.00 }
//     },
//     {
//       id: 2,
//       name: '自行车',
//       pricing: { price: 1500.00 }
//     }
//   ]
// }

场景2:查询所有产品,附带生产它们的公司和对应售价

const allProductsWithCompanies = await Product.findAll({
  include: [
    {
      model: Company,
      as: 'companies',
      through: {
        attributes: ['price'],
        as: 'pricing'
      }
    }
  ]
});

场景3:带过滤条件的查询(比如只查售价大于1000的产品及其公司)

const { Op } = require('sequelize');

const filteredProducts = await Product.findAll({
  include: [
    {
      model: Company,
      as: 'companies',
      through: {
        attributes: ['price'],
        as: 'pricing',
        where: { price: { [Op.gt]: 1000 } } // 过滤售价大于1000的记录
      },
      required: true // 加上这个相当于INNER JOIN,只返回有符合条件公司的产品
    }
  ],
  order: [['name', 'ASC']], // 按产品名称升序排序
  limit: 10, // 分页:每页10条
  offset: 0 // 从第0条开始(第一页)
});

场景4:直接查询中间表的完整关联数据

如果需要更灵活的组合,也可以直接查询中间表,同时关联两边的模型:

const companyProductDetails = await CompanyProduct.findAll({
  include: [
    { model: Company, as: 'Company' },
    { model: Product, as: 'Product' }
  ],
  where: { price: { [Op.between]: [500, 500000] } }, // 过滤价格区间
  order: [['price', 'DESC']] // 按售价降序排序
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:19:36