PrismaでのInner Joinのやりかた
いま個人的に Blitzjs が推しでして、そのなかで使用している ORM の Prisma についての情報が少なかったのでまとめました。
What's Prisma?
- Blitz で使用されている Node の ORM
-
scheme.prisma
という独自のファイルにモデル定義を行い、この定義情報を元に migrate を実行する
Prisma - Database tools for modern application development
Q. scheme.prisma のさわり心地はどうですか?
A. prisma プラグインを IDE に入れてからは快適です。
(ファイルとスニペット識別がされます。されないと書きにくかった)
table 間の relationship も割と直感的にかけます。
model Theme {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
lesson Lesson[]
}
model Lesson {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String?
description String?
theme Theme? @relation(fields: [themeId], references: [id])
themeId Int?
words Int @default(0)
read_time Int?
}
Schema 定義(1:N)
例えば Theme
とLesson
は1:N
の関係性なので、
Theme.lesson
はLesson[]
として 1:N を表現します。
Lesson
側はTheme
モデルを扱うプロパティ(ここではtheme
)を設定します。
これによりlesson.theme
はTheme
のモデル情報を参照できます。
実際の RDS に保存されるカラムは@relation
のfields
に定義できます。ここではthemeId
となります。
model Theme {
id Int @default(autoincrement()) @id
lesson Lesson[]
}
model Lesson {
id Int @default(autoincrement()) @id
theme Theme? @relation(fields: [themeId], references: [id])
themeId Int?
}
このように、
- メインモデルに次を設定(
model Lesson
)- サブモデルを扱うプロパティ情報(
theme
) - 外部キーを扱うプロパティ(
themeId
)
- サブモデルを扱うプロパティ情報(
- サブモデルに次を定義(
model Theme
)- メインモデルを扱うプロパティ情報(
lesson
)
- メインモデルを扱うプロパティ情報(
の作業を行えば定義完了です。
Select Inner Join
さて、Schema を使用して inner join しましょう。
せっかくなので blitz console で試してみます。
単純にwhere
だけ指定してfindMany
するとselect *
されますが、@relation
を設定したモデルは取得しません。
⚡️ > db.lesson.findMany({ where: { id: 4 } }).then(console.log)
Promise { <pending> }
⚡️ > [
{
id: 4,
createdAt: 2020-09-17T15:28:26.000Z,
updatedAt: 1970-01-01T00:00:00.000Z,
title: '最高のレッスン!',
description: 'どうだ!',
themeId: 1,
words: 61,
read_time: 30
}
]
join して取得するにはinclude
を使用します。
join したテーブルカラムのselect *
をしたいだけなら、include: { theme: true }
を指定すればよいです。
⚡️ > db.lesson.findMany({ where: { id: 4 }, include: { theme: true } }).then(console.log)
Promise { <pending> }
⚡️ > [
{
id: 4,
createdAt: 2020-09-17T15:28:26.000Z,
updatedAt: 1970-01-01T00:00:00.000Z,
title: '最高のレッスン!',
description: 'どうだ!',
themeId: 1,
words: 61,
read_time: 30,
theme: {
id: 1,
createdAt: 2020-09-16T08:16:33.566Z,
updatedAt: 1970-01-01T00:00:00.000Z,
name: 'GENERAL'
}
}
]
とはいえテーブル情報をすべて返却してしまうと、とあるサービスで騒ぎになったようなセキュリティインシデントも発生しますので、できるだけカラム指定しましょう。
カラム指定にはselect
で指定します。
カラムごとに true を指定する形式で select できます。select: { name: true }
(割と SQL の感覚と近いですね。)
⚡️ > db.lesson.findMany({ where: { id: 4 }, include: { theme: {select:{name:true}}} }).then(console.log)
Promise { <pending> }
⚡️ > [
{
id: 4,
createdAt: 2020-09-17T15:28:26.000Z,
updatedAt: 1970-01-01T00:00:00.000Z,
title: '最高のレッスン!',
description: 'どうだ!',
themeId: 1,
words: 61,
read_time: 30,
theme: { name: 'GENERAL' }
}
]
ちなみに、メインモデルに対する select とサブモデルへの include を同時に宣言するとエラーになります。
⚡️ > db.lesson.findMany({ select: { title: true, themeId: true } ,where: { id: 4 }, include: { theme: {select:{name:true}}} }).then(console.log)
Uncaught PrismaClientValidationError:
Invalid `prisma.lesson.findMany()` invocation in
repl:1:11
{
select: {
~~~~~~
title: true,
themeId: true
},
where: {
id: 4
},
include: {
~~~~~~~
theme: {
select: {
name: true
}
}
}
}
Please either use `include` or `select`, but not both at the same time.
これは常用する形式だと思うのですが、どうやって解決するんでしょうね?
ここのソリューションが見つかりましたらまた記事を投稿させていただきます。
以上、Prisma で inner join の方法でした!
Discussion
おおなるほど!ありがとうございます!