说我有一个表suppliers
,该表通过另一个表tags
与suppliers_tags
关联。所以我的模型看起来像这样:
const Supplier = Sequelize.define('suppliers', {
id: { type: Sequelize.INTEGER, primaryKey: true },
name: Sequelize.STRING,
});
const SupplierTag = Sequelize.define('suppliers_tags', {
id: { type: Sequelize.INTEGER, primaryKey: true },
supplier_id: { type: Sequelize.INTEGER, references: { model: 'suppliers', key: 'id' } },
tag_id: { type: Sequelize.INTEGER, references: { model: 'suppliers', key: 'id' } },
});
const Tag = Sequelize.define('tags', {
id: { type: Sequelize.INTEGER, primaryKey: true },
name: Sequelize.STRING,
type: Sequelize.ENUM('food', 'drink'),
});
关联看起来像这样:
Supplier.belongsToMany(Tag, { as: 'tags', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
假设我在数据库中有以下数据:
供应商:
id name
1 Supplier1
2 Supplier2
3 Supplier3
标签:
id name type
1 Food1 food
2 Vegan food
3 Vegan drink
4 Food2 food
(我特意将两个标签命名为相同的标签,对于此应用程序,具有不同类型的标签可以具有相同的名称非常重要。)
suppliers_tags
id supplier_id tag_id
1 1 1
2 1 3
3 1 2
4 2 1
5 2 4
6 3 2
现在我可以执行以下查询:
Supplier.findAll({
include: [
{
model: Tag,
as: 'tags',
where: {
[Op.and]: [
{ type: 'food' },
{ name: 'Vegan' },
],
},
},
],
});
这将返回Supplier1和Supplier3,正确加入suppliers_tags
和tags
并过滤tags
表以包含type
'food'和name
中的一个“素食主义者”。
现在,如果我要搜索同时满足以下两个条件的供应商怎么办:
food
,名称为Food1
drink
,名称为Vegan
天真的(?),我尝试了以下操作:
Supplier.findAll({
include: [
{
model: Tag,
as: 'tags_food',
where: {
[Op.and]: [
{ type: 'food' },
{ name: 'Food1' },
],
},
},
{
model: Tag,
as: 'tags_drink',
where: {
[Op.and]: [
{ type: 'drink' },
{ name: 'Vegan' },
],
},
},
],
});
这会尝试两次联接tags
表,但忽略添加别名,从而导致以下错误:
SequelizeEagerLoadingError: tags is associated to suppliers multiple times. To identify the correct association, you must use the 'as' keyword to specify the alias of the association you want to include.
对,所以as
上的include
选项似乎没有达到预期的效果。如果我将关联修改为以下内容,该怎么办:
Supplier.belongsToMany(Tag, { as: 'tags_drink', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Supplier.belongsToMany(Tag, { as: 'tags_food', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
现在,如果我运行相同的findAll
查询,它将生成以下SQL:
SELECT
"suppliers".*
,"tags_food"."id" AS "tags_food.id"
,"tags_food"."name" AS "tags_food.name"
,"tags_food"."type" AS "tags_food.type"
,"tags_food->suppliers_tags"."supplier_id" AS "tags_food.suppliers_tags.supplier_id"
,"tags_food->suppliers_tags"."tag_id" AS "tags_food.suppliers_tags.tag_id"
,"tags_drink"."id" AS "tags_drink.id"
,"tags_drink"."name" AS "tags_drink.name"
,"tags_drink"."type" AS "tags_drink.type"
,"tags_drink->suppliers_tags"."supplier_id" AS "tags_drink.suppliers_tags.supplier_id"
,"tags_drink->suppliers_tags"."tag_id" AS "tags_drink.suppliers_tags.tag_id"
FROM (
SELECT
"suppliers"."id"
,"suppliers"."name"
FROM "suppliers" AS "suppliers"
WHERE (
SELECT "suppliers_tags"."supplier_id"
FROM "suppliers_tags" AS "suppliers_tags"
INNER JOIN "tags" AS "tag" ON "suppliers_tags"."tagId" = "tag"."id"
AND ("tag"."type" = 'food' AND "tag"."name" = 'Food1')
WHERE ("suppliers"."id" = "suppliers_tags"."supplier_id")
LIMIT 1
) IS NOT NULL
AND (
SELECT "suppliers_tags"."supplier_id"
FROM "suppliers_tags" AS "suppliers_tags"
INNER JOIN "tags" AS "tag" ON "suppliers_tags"."tag_id" = "tag"."id"
AND ("tag"."type" = 'drink' AND "tag"."name" = 'Vegan')
WHERE ("suppliers"."id" = "suppliers_tags"."supplier_id")
LIMIT 1
) IS NOT NULL
) AS "suppliers"
INNER JOIN (
"suppliers_tags" AS "tags_food->suppliers_tags"
INNER JOIN "tags" AS "tags_food"
ON "tags_food"."id" = "tags_food->suppliers_tags"."tagId"
) ON "suppliers"."id" = "tags_food->suppliers_tags"."supplier_id"
AND ("tags_food"."type" = 'food' AND "tags_food"."name" = 'Food1')
INNER JOIN (
"suppliers_tags" AS "tags_drink->suppliers_tags"
INNER JOIN "tags" AS "tags_drink"
ON "tags_drink"."id" = "tags_drink->suppliers_tags"."tag_id"
) ON "suppliers"."id" = "tags_drink->suppliers_tags"."supplier_id"
AND ("tags_drink"."type" = 'drink' AND "tags_drink"."name" = 'Vegan')
ORDER BY "suppliers"."id"
这是正确的并且是我需要的,除了一个错误:在tag_id
联接的情况下,外键tagId
已切换为tags_food
(?!)。这当然会失败,并显示以下错误:
SequelizeDatabaseError: column tags_food->suppliers_tags.tagId does not exist
请注意,正确外键tag_id
是由Sequelize在tags_drink
上的联接中生成的。
如果我切换关系定义,即:
Supplier.belongsToMany(Tag, { as: 'tags_food', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Supplier.belongsToMany(Tag, { as: 'tags_drink', through: 'suppliers_tags', foreignKey: 'supplier_id' });
Tag.belongsToMany(Supplier, { as: 'supplierTag', through: 'suppliers_tags', foreignKey: 'tag_id' });
然后成功为tag_id
联接生成tags_food
,为tagId
联接产生奇怪的tags_drink
。
我可以将其归结为Sequelize的another基本缺陷,还是:
在Sequelize中,是否存在一种确定的方法可以在不同条件下两次将同一关系加入?