自己関連
リレーションフィールドは自身のモデルを参照することもでき、この場合のリレーションは自己関連と呼ばれます。自己関連は、1対1、1対多、多対多のいずれのカーディナリティでも可能です。
自己関連は常に@relation
属性を必要とすることに注意してください。
一対一の自己関連
以下の例は、一対一の自己関連をモデル化しています
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
successorId String? @unique @db.ObjectId
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
このリレーションは以下を表します
- 「ユーザーは1人または0人の先行者を持つことができる」(例:サラはブログ所有者としてメアリーの先行者である)
- 「ユーザーは1人または0人の後継者を持つことができる」(例:メアリーはブログ所有者としてサラの後継者である)
注: 一対一の自己関連は、両側で必須にすることはできません。どちらか一方または両方がオプションである必要があります。そうでない場合、最初の
User
レコードを作成することが不可能になります。
一対一の自己関連を作成するには
- リレーションの両側で、同じ名前を共有する
@relation
属性を定義する必要があります。この場合、BlogOwnerHistoryです。 - 1つのリレーションフィールドは完全にアノテーションされている必要があります。この例では、
successor
フィールドはfield
引数とreferences
引数の両方を定義しています。 - 1つのリレーションフィールドは外部キーによって裏付けられている必要があります。
successor
フィールドは、id
フィールドの値を参照するsuccessorId
外部キーによって裏付けられています。successorId
スカラーリレーションフィールドは、一対一のリレーションを保証するために@unique
属性も必要とします。
注: 一対一の自己関連は、関係の両側が等しい場合でも両側が必要です。たとえば、「親友」のリレーションをモデル化するには、
bestfriend1
とbestfriend2
の2つのリレーションフィールドを作成する必要があります。
リレーションのどちらの側も外部キーによって裏付けられることができます。以下の前の例では、successor
はsuccessorId
によって裏付けられています
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
successorId String? @unique @db.ObjectId
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
あるいは、predecessor
がpredecessorId
によって裏付けられるように書き換えることもできます
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
successor User? @relation("BlogOwnerHistory")
predecessorId Int? @unique
predecessor User? @relation("BlogOwnerHistory", fields: [predecessorId], references: [id])
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
successor User? @relation("BlogOwnerHistory")
predecessorId String? @unique @db.ObjectId
predecessor User? @relation("BlogOwnerHistory", fields: [predecessorId], references: [id])
}
どちらの側が外部キーによって裏付けられていても、Prisma Clientはpredecessor
フィールドとsuccessor
フィールドの両方を表面化します
const x = await prisma.user.create({
data: {
name: "Bob McBob",
successor: {
connect: {
id: 2,
},
},
predecessor: {
connect: {
id: 4,
},
},
},
});
データベースにおける一対一の自己関連
リレーショナルデータベース
リレーショナルデータベースのみにおいて、一対一の自己関連は以下のSQLで表されます
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"successorId" INTEGER
);
ALTER TABLE "User" ADD CONSTRAINT fk_successor_user FOREIGN KEY ("successorId") REFERENCES "User" (id);
ALTER TABLE "User" ADD CONSTRAINT successor_unique UNIQUE ("successorId");
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しており、これはドキュメントがリレーショナルデータベースと同様の方法でIDによって互いを参照することを意味します。
以下のMongoDBドキュメントは、2人のユーザー間の一対一の自己関連を表しています
{ "_id": { "$oid": "60d97df70080618f000e3ca9" }, "name": "Elsa the Elder" }
{
"_id": { "$oid": "60d97df70080618f000e3caa" },
"name": "Elsa",
"successorId": { "$oid": "60d97df70080618f000e3ca9" }
}
一対多の自己関連
一対多の自己関連は以下のようになります
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
teacherId String? @db.ObjectId
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}
このリレーションは以下を表します
- 「ユーザーは0人または1人の教師を持つ」
- 「ユーザーは0人以上の生徒を持つことができる」
teacher
フィールドを必須にすることで、各ユーザーに教師がいることを必須にすることもできます。
データベースにおける一対多の自己関連
リレーショナルデータベース
リレーショナルデータベースにおいて、一対多の自己関連は以下のSQLで表されます
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"teacherId" INTEGER
);
ALTER TABLE "User" ADD CONSTRAINT fk_teacherid_user FOREIGN KEY ("teacherId") REFERENCES "User" (id);
teacherId
にUNIQUE
制約がないことに注意してください。複数の生徒が同じ教師を持つことができます。
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しており、これはドキュメントがリレーショナルデータベースと同様の方法でIDによって互いを参照することを意味します。
以下のMongoDBドキュメントは、3人のユーザー間の一対多の自己関連を表しています - 1人の教師と、同じteacherId
を持つ2人の生徒。
{
"_id": { "$oid": "60d9b9e600fe3d470079d6f9" },
"name": "Ms. Roberts"
}
{
"_id": { "$oid": "60d9b9e600fe3d470079d6fa" },
"name": "Student 8",
"teacherId": { "$oid": "60d9b9e600fe3d470079d6f9" }
}
{
"_id": { "$oid": "60d9b9e600fe3d470079d6fb" },
"name": "Student 9",
"teacherId": { "$oid": "60d9b9e600fe3d470079d6f9" }
}
多対多の自己関連
多対多の自己関連は以下のようになります
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
followedBy User[] @relation("UserFollows", fields: [followedByIDs], references: [id])
followedByIDs String[] @db.ObjectId
following User[] @relation("UserFollows", fields: [followingIDs], references: [id])
followingIDs String[] @db.ObjectId
}
このリレーションは以下を表します
- 「ユーザーは0人以上のユーザーにフォローされることができる」
- 「ユーザーは0人以上のユーザーをフォローできる」
リレーショナルデータベースの場合、この多対多のリレーションは暗黙的であることに注意してください。これは、Prisma ORMが基盤となるデータベースでそれに対するリレーションテーブルを維持することを意味します。
リレーションに他のフィールドを含める必要がある場合は、明示的な多対多の自己関連を作成することもできます。以前に示された自己関連の明示的なバージョンは次のとおりです
model User {
id Int @id @default(autoincrement())
name String?
followedBy Follows[] @relation("followedBy")
following Follows[] @relation("following")
}
model Follows {
followedBy User @relation("followedBy", fields: [followedById], references: [id])
followedById Int
following User @relation("following", fields: [followingId], references: [id])
followingId Int
@@id([followingId, followedById])
}
データベースにおける多対多の自己関連
リレーショナルデータベース
リレーショナルデータベースにおいて、多対多の自己関連(暗黙的)は以下のSQLで表されます
CREATE TABLE "User" (
id integer DEFAULT nextval('"User_id_seq"'::regclass) PRIMARY KEY,
name text
);
CREATE TABLE "_UserFollows" (
"A" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE,
"B" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE
);
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しており、これはドキュメントがリレーショナルデータベースと同様の方法でIDによって互いを参照することを意味します。
以下のMongoDBドキュメントは、5人のユーザー間の多対多の自己関連を表しています - "Bob"
をフォローする2人のユーザーと、"Bob"
がフォローする2人のユーザー。
{
"_id": { "$oid": "60d9866f00a3e930009a6cdd" },
"name": "Bob",
"followedByIDs": [
{ "$oid": "60d9866f00a3e930009a6cde" },
{ "$oid": "60d9867000a3e930009a6cdf" }
],
"followingIDs": [
{ "$oid": "60d9867000a3e930009a6ce0" },
{ "$oid": "60d9867000a3e930009a6ce1" }
]
}
{
"_id": { "$oid": "60d9866f00a3e930009a6cde" },
"name": "Follower1",
"followingIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }]
}
{
"_id": { "$oid": "60d9867000a3e930009a6cdf" },
"name": "Follower2",
"followingIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }]
}
{
"_id": { "$oid": "60d9867000a3e930009a6ce0" },
"name": "CoolPerson1",
"followedByIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }]
}
{
"_id": { "$oid": "60d9867000a3e930009a6ce1" },
"name": "CoolPerson2",
"followedByIDs": [{ "$oid": "60d9866f00a3e930009a6cdd" }]
}
同じモデルに複数の自己関連を定義する
同じモデルに複数の自己関連を一度に定義することもできます。前のセクションのすべてのリレーションを例として、User
モデルを次のように定義できます。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
teacherId String? @db.ObjectId
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
followedBy User[] @relation("UserFollows", fields: [followedByIDs])
followedByIDs String[] @db.ObjectId
following User[] @relation("UserFollows", fields: [followingIDs])
followingIDs String[] @db.ObjectId
}