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の方法でした!