Sequelize:按关联数量/多个关联过滤查询

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

我有两个模型在续集:模型A和模型B。 这些模型处于多对多关系。

我想获取与 modelB 的(多个)特定实例关联的 modelA 的所有实体。

例如,我想获取 modelA 与 id (1, 3, 4) 的 modelB 有关系的所有模型。我想获得至少与所有指定 modelB 有关系的每个模型 a。

如何在sequelize中实现这一点?

这是我迄今为止尝试过的:

型号1:

@Table
export class Workout extends Model<Partial<Workout>> {
    @UniqueIndex
    @Column
    name!: string;

    @Column
    length!: number;

    @UniqueIndex
    @ForeignKey(() => User)
    @Column
    userId?: number;

    @BelongsTo(() => User, "userId")
    user?: User;

    @BelongsToMany(() => Tag, () => WorkoutTag)
    tags?: Tag[];
}

型号2:

@Table({ timestamps: false })
export class Tag extends Model<Partial<Tag>> {
    @Unique
    @Column
    name!: string;

    @BelongsToMany(() => Workout, () => WorkoutTag)
    workouts?: Workout[];
}

还有连接表

@Table({ tableName: "workout_tag", timestamps: false })
export class WorkoutTag extends Model<WorkoutTag> {
    @ForeignKey(() => Workout)
    @Column
    workoutId!: number;

    @ForeignKey(() => Tag)
    @Column
    tagId!: number;

    @BelongsTo(() => Workout)
    workout!: Workout;

    @BelongsTo(() => Tag)
    tag!: Tag;
}

我尝试过的查询:

        const include: Includeable[] = [{ model: User, as: "user" }];
        const additionalOptions: FindOptions<Partial<Workout>> = {};

        if ((filter?.tags?.length ?? 0) > 0) {
            console.log("Applying filter", filter?.tags);
            include.push({
                model: Tag,
                through: {
                    where: {
                        tagId: { [Op.in]: filter?.tags },
                    },
                },
            });
            additionalOptions.group = ["`Workout`.`id`"];
            additionalOptions.having = sequelize.literal(
                `COUNT(DISTINCT "\`tags->WorkoutTag\`.\`tagId\`") = ${filter!.tags!.length}`
            );
        }

        console.log("Loading workouts");

        const workouts = await Workout.findAll({
            include,
            limit: WORKOUTS_PER_PAGE,
            offset: (page - 1) * WORKOUTS_PER_PAGE,
            where: {
                name: { [Op.like]: `%${search}%` },
                length: { [Op.between]: [filter?.length?.min ?? 0, filter?.length?.max ?? 180] },
            },
            ...additionalOptions,
        });

我正在尝试获取与

filter.tags
数组中指定的所有标签相关的所有锻炼。

filter.tags
是标签实体的id数组

我使用的数据库是MySQL

这些是我使用的表格:

CREATE TABLE `workouts` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(255) DEFAULT NULL,
   `length` int DEFAULT NULL,
   `userId` int DEFAULT NULL,
   `createdAt` datetime DEFAULT NULL,
   `updatedAt` datetime DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `unique-index` (`name`,`userId`),
   KEY `userId` (`userId`),
   CONSTRAINT `workouts_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
 ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci



CREATE TABLE `users` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(255) DEFAULT NULL,
   `createdAt` datetime DEFAULT NULL,
   `updatedAt` datetime DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`),
   UNIQUE KEY `email` (`email`)
 ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci



CREATE TABLE `tags` (
   `id` int NOT NULL AUTO_INCREMENT,
   `name` varchar(255) DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `name` (`name`)
 ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci


CREATE TABLE `workout_tag` (
   `workoutId` int NOT NULL,
   `tagId` int NOT NULL,
   PRIMARY KEY (`workoutId`,`tagId`),
   UNIQUE KEY `workout_tag_tagId_workoutId_unique` (`workoutId`,`tagId`),
   KEY `tagId` (`tagId`),
   CONSTRAINT `workout_tag_ibfk_1` FOREIGN KEY (`workoutId`) REFERENCES `workouts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT `workout_tag_ibfk_2` FOREIGN KEY (`tagId`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
sql mysql node.js sequelize.js sequelize-typescript
1个回答
0
投票

你需要进行“关系划分”(推荐阅读)。

查找具有与其关联的标签 1、3、4(可能还有更多)的所有锻炼的 SQL 解决方案是:

SELECT *
FROM workouts
WHERE NOT EXISTS (
    SELECT 1
    FROM (VALUES ROW(1), ROW(3), ROW(4)) AS required_tags(id)
    WHERE NOT EXISTS (
        SELECT 1
        FROM workout_tag
        WHERE workout_tag.workoutId = workouts.id AND workout_tag.tagId = required_tags.id
    )
);

或者如果您发现条件聚合更容易理解:

SELECT *
FROM workouts
WHERE id IN (
    SELECT workoutId
    FROM workout_tag
    GROUP BY workoutId
    HAVING COUNT(CASE WHEN tagId IN (1, 3, 4) THEN 1 END) = 3
    -- the "= 3" part must match the number of items in the IN clause
)

数据库<>小提琴

第二个查询更容易转换为Sequelize,您可以在having子句中使用原始查询。

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