メインコンテンツへスキップ

自己参照リレーション

リレーションフィールドはそれ自身のモデルを参照することもできます。この場合、そのリレーションは自己参照リレーションと呼ばれます。自己参照リレーションは、1対1、1対多、多対多のすべてのカーディナリティを持つことができます。

自己参照リレーションは常に`@relation`属性を必要とすることに注意してください。

1対1自己参照リレーション

次の例は、1対1自己参照リレーションをモデル化しています。

model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}

このリレーションは以下を表します。

  • 「ユーザーは1人または0人の前任者を持つことができる」(例:サラはブログオーナーとしてのメアリーの前任者です)
  • 「ユーザーは1人または0人の後任者を持つことができる」(例:メアリーはブログオーナーとしてのサラの後任者です)

注意:1対1自己参照リレーションは、両側で必須にすることはできません。片側または両側がオプションである必要があります。そうしないと、最初の`User`レコードを作成することが不可能になります。

1対1自己参照リレーションを作成するには

  • リレーションの両側は、同じ名前を共有する`@relation`属性を定義する必要があります。この場合、BlogOwnerHistoryです。
  • 1つのリレーションフィールドは、完全にアノテーションされている必要があります。この例では、`successor`フィールドは`field`と`references`引数の両方を定義しています。
  • 1つのリレーションフィールドは、外部キーによってバックアップされる必要があります。 `successor`フィールドは、`id`フィールドの値を参照する`successorId`外部キーによってバックアップされています。 `successorId`スカラリレーションフィールドは、1対1リレーションを保証するために`@unique`属性も必要とします。

注意:1対1自己参照リレーションは、両側がリレーションシップで等しい場合でも、両側が必要です。たとえば、「親友」リレーションをモデル化するには、`bestfriend1`と`bestfriend2`の2つのリレーションフィールドを作成する必要があります。

リレーションのどちら側も外部キーによってバックアップできます。前の例(以下に再掲)では、`successor`は`successorId`によってバックアップされています。

model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}

あるいは、これを`predecessor`が`predecessorId`によってバックアップされるように書き換えることもできます。

model User {
id Int @id @default(autoincrement())
name String?
successor User? @relation("BlogOwnerHistory")
predecessorId Int? @unique
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,
},
},
},
});

データベースにおける1対1自己参照リレーション

リレーショナルデータベース

リレーショナルデータベースのみでは、1対1自己参照リレーションは次の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人のユーザー間の1対1自己参照リレーションを表しています。

{ "_id": { "$oid": "60d97df70080618f000e3ca9" }, "name": "Elsa the Elder" }
{
"_id": { "$oid": "60d97df70080618f000e3caa" },
"name": "Elsa",
"successorId": { "$oid": "60d97df70080618f000e3ca9" }
}

1対多自己参照リレーション

1対多自己参照リレーションは次のようになります。

model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}

このリレーションは以下を表します。

  • 「ユーザーは0人または1人の教師を持つ」
  • 「ユーザーは0人以上の生徒を持つことができる」

`teacher`フィールドを必須にすることで、各ユーザーが教師を持つようにすることもできます。

データベースにおける1対多自己参照リレーション

リレーショナルデータベース

リレーショナルデータベースでは、1対多自己参照リレーションは次の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対多自己参照リレーションを表しています - 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" }
}

多対多自己参照リレーション

多対多自己参照リレーションは次のようになります。

model User {
id Int @id @default(autoincrement())
name String?
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}

このリレーションは以下を表します。

  • 「ユーザーは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人のユーザーと、彼をフォローする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`モデルを定義できます。

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")
}