在 Sequelize 中,如何按项目的多对多关联值过滤项目,并且仍然显示所有关联值而不仅仅是过滤后的值?
考虑这些模式(我使用sequelize-typescript,但它几乎是相同的)。我在 Item 和 Tag 之间有一个多态的 n-m 关联。 n-m关联用EntityTag模型/表来表示;该关联有一个额外的“值”来表示标签值。
标签只是一个键(词),但是这个键(词)可能在与实体(例如:项目)链接时具有关联值。这正是 Amazon Web Services 对其资源所做的事情。
@Table
export class Item extends Model<Interaction> {
@Column
description: string;
@BelongsToMany(() => Tag, {
through: {
model: () => EntityTag,
scope: { entityType: 2 }, // For the polymorphic association
unique: false
},
foreignKey: 'entityId'
})
tags: Tag[];
}
@Table
export class Tag extends Model<Tag> {
@PrimaryKey
@Column
id: string;
}
@Table
export class EntityTag extends Model<EntityTag> {
@PrimaryKey
@Column
entityId: number;
@PrimaryKey
@Column
entityType: number;
@PrimaryKey
@ForeignKey(() => Tag)
@Column
tagId: string;
@Column
value: string;
}
现在,理想情况下,我希望能够执行这样的查询:“给我与具有以下值的以下标签关联的所有项目:('TagA' = 'Foo', 'TagB' = 'Bar') ”.
我知道它有点太复杂了,所以现在我只考虑一个更简单的查询,例如:“给我与具有以下特定值的以下单个标签关联的所有项目:('TagA' = 'Foo ')",其中 'TagA' 是 tagId,'Foo' 是值。
考虑以下数据库
标签
+------+
| id |
+------+
| TagA |
| TagB |
+------+
项目
+------+-------------+
| id | description |
+------+-------------+
| 1000 | NULL |
+------+-------------+
实体标签
+------------+----------+-------+-------+
| entityType | entityId | tagId | value |
+------------+----------+-------+-------+
| 2 | 1000 | TagA | Foo |
+------------+----------+-------+-------+
| 2 | 1000 | TagB | Bar |
+------------+----------+-------+-------+
我希望Sequelize自动生成的SQL查询是这样的(我自己手动编写):
SELECT * FROM
(SELECT Item.*
FROM Item
JOIN EntityTag
ON EntityTag.entityId = Item.id AND EntityTag.entityType = 2
WHERE EntityTag.tagId = "TagA" AND EntityTag.value = "Foo")
AS I
JOIN EntityTag
ON EntityTag.entityType = 2 AND EntityTag.entityId = I.id;
我的查询结果如下:
+------+-------------+------------+----------+-------+-------+
| id | description | entityType | entityId | tagId | value |
+------+-------------+------------+----------+-------+-------+
| 1000 | NULL | 2 | 1000 | TagA | Foo |
+------+-------------+------------+----------+-------+-------+
| 1000 | NULL | 2 | 1000 | TagB | Bar |
+------+-------------+------------+----------+-------+-------+
不幸的是,当我尝试使用 Sequelize 获得相同的结果时,我发现了一些困难。我试过这个:
let items = await Item.findAll({
include: [{
model: Tag,
through: {
where: {
"tagId": "TagA",
"value": "Foo"
}
}
}]
});
生成以下查询:
SELECT
`Item`.*,
`tags`.`id` AS `tags.id`,
`tags->EntityTag`.`entityId` AS `tags.EntityTag.entityId`,
`tags->EntityTag`.`entityType` AS `tags.EntityTag.entityType`,
`tags->EntityTag`.`tagId` AS `tags.EntityTag.tagId`,
`tags->EntityTag`.`value` AS `tags.EntityTag.value`
FROM
(SELECT
`Item`.`id`,
`Item`.`description`
FROM
`Item` AS `Item`
) AS `Item`
LEFT OUTER JOIN
(`EntityTag` AS `tags->EntityTag`
INNER JOIN `Tag` AS `tags` ON `tags`.`id` = `tags->EntityTag`.`tagId`
AND ((`tags->EntityTag`.`tagId` = 'TagA'
AND `tags->EntityTag`.`value` = 'Foo')
AND `tags->EntityTag`.`entityType` = 2)) ON `Item`.`id` = `tags->EntityTag`.`entityId`;
但是我通过此查询得到的是仅填充了匹配标签的项目。它缺少与同一项目关联的所有其他标签。
[
{
"id": 1000,
"description": null,
"tags": [
{
"id": "TagA",
"EntityTag": {
"entityId": 1000,
"entityType": 2,
"tagId": "TagA",
"value": "Foo"
}
}
]
}
]
这是原始查询结果。
+------+-------------+------------+----------+-------+-------+
| id | description | entityType | entityId | tagId | value |
+------+-------------+------------+----------+-------+-------+
| 1000 | NULL | 2 | 1000 | TagA | Foo |
+------+-------------+------------+----------+-------+-------+
如您所见,它缺少值为“Bar”的“TagB”。我对 Sequelize 自动生成的所有子查询感到非常困惑。我该如何使用 Sequelize 解决这个问题?
提前致谢。
我找到了解决方案,尽管我不确定这是最好的解决方案。我已将其放在我自己几乎重复的问题的答案中:https://stackoverflow.com/a/77654527/11847996
答案基本上是创建两个您想要的关联(至少给其中一个关联一个别名,这样它们就不会冲突),然后将它们都包含在查询中,并且仅按其中之一进行过滤。