在 Sequelize 中,如何按项目的多对多关联值过滤项目,并显示其所有关联值?

问题描述 投票:0回答:1

在 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 解决这个问题?

提前致谢。

sequelize.js
1个回答
0
投票

我找到了解决方案,尽管我不确定这是最好的解决方案。我已将其放在我自己几乎重复的问题的答案中:https://stackoverflow.com/a/77654527/11847996

答案基本上是创建两个您想要的关联(至少给其中一个关联一个别名,这样它们就不会冲突),然后将它们都包含在查询中,并且仅按其中之一进行过滤。

© www.soinside.com 2019 - 2024. All rights reserved.