首页 > 解决方案 > 在 Sequelize 中,如何按项目的多对多关联值过滤项目,并显示它们的所有关联值?

问题描述

在 Sequelize 中,如何按项目的多对多关联值过滤项目,并且仍然显示它们的所有关联值,而不仅仅是过滤后的关联值?

考虑这些模式(我使用 sequelize-typescript 但几乎相同)。我在 Item 和 Tag 之间有一个多态的 nm 关联。nm 关联用 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

解决方案


推荐阅读