跳到主要内容

多对多关系建模和查询

问题

在关系数据库中建模和查询多对多关系可能具有挑战性。本文展示了如何使用 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 查询它们。