我有两个模型在续集:模型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
你需要进行“关系划分”(推荐阅读)。
查找具有与其关联的标签 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子句中使用原始查询。