跳到主要內容

建模和查詢多對多關係

問題

在關聯式資料庫中建模和查詢多對多關係可能具有挑戰性。本文展示了如何使用 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' } },
},
})

顯式關係

在您需要在關係表中儲存額外欄位,或者您正在內省已設定多對多關係的現有資料庫的情況下,大多需要建立顯式關係。這與上面使用的 schema 相同,但具有顯式關係表

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 查詢它們。