跳至主要内容

多对多关系的建模和查询

问题

在关系数据库中对多对多关系进行建模和查询可能具有挑战性。本文将展示使用 Prisma ORM 处理此问题的两个示例。第一个示例使用 隐式,而第二个示例使用 显式 多对多关系。

解决方案

隐式关系

这是一种多对多关系类型,其中 Prisma ORM 在内部处理 关系表。隐式多对多关系的基本示例如下

model Post {
id Int @id @default(autoincrement())
title String
tags Tag[]
}

model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}

要创建帖子及其标签,可以使用 Prisma Client 编写以下内容

await prisma.post.create({
data: {
title: 'Types of relations',
tags: { create: [{ name: 'dev' }, { name: 'prisma' }] },
},
})

在上面的示例中,我们可以直接查询帖子及其标签,如下所示

await prisma.post.findMany({
include: { tags: true },
})

获得的响应将是

[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"name": "dev"
},
{
"id": 2,
"name": "prisma"
}
]
}
]

另一个用例是,如果您想添加新标签以及将现有标签连接到帖子。例如,用户为帖子创建了新标签,并且还选择了要添加的现有标签。在这种情况下,我们可以通过以下方式执行此操作

await prisma.post.update({
where: { id: 1 },
data: {
title: 'Prisma is awesome!',
tags: { set: [{ id: 1 }, { id: 2 }], create: { name: 'typescript' } },
},
})

显式关系

当您需要在关系表中存储额外的字段,或者您正在 内省 已经设置了多对多关系的现有数据库时,通常需要创建显式关系。这是与上面相同的模式,但使用显式关系表

model Post {
id Int @id @default(autoincrement())
title String
tags PostTags[]
}

model PostTags {
id Int @id @default(autoincrement())
post Post? @relation(fields: [postId], references: [id])
tag Tag? @relation(fields: [tagId], references: [id])
postId Int?
tagId Int?

@@index([postId, tagId])
}

model Tag {
id Int @id @default(autoincrement())
name String @unique
posts PostTags[]
}

将标签添加到帖子将是创建关系表(PostTags)以及标签表(Tag)中的内容

await prisma.post.create({
data: {
title: 'Types of relations',
tags: {
create: [
{ tag: { create: { name: 'dev' } } },
{ tag: { create: { name: 'prisma' } } },
],
},
},
})

另外,查询帖子及其标签需要额外的 include,如下所示

await prisma.post.findMany({
include: { tags: { include: { tag: true } } },
})

这将提供以下输出

[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"postId": 1,
"tagId": 1,
"tag": {
"id": 1,
"name": "prisma"
}
},
{
"id": 2,
"postId": 1,
"tagId": 2,
"tag": {
"id": 2,
"name": "dev"
}
}
]
}
]

有时,在 UI 中显示关系表的数据并不理想。在这种情况下,最好在服务器端获取数据后对其进行映射,并将该响应发送到前端。

const result = posts.map((post) => {
return { ...post, tags: post.tags.map((tag) => tag.tag) }
})

这将提供与使用隐式关系获得的输出类似的输出。

[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"name": "prisma"
},
{
"id": 2,
"name": "dev"
}
]
}
]

本文介绍了如何实现隐式和显式多对多关系,以及如何使用 Prisma Client 查询它们。