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

自己関連

リレーションフィールドは自身のモデルを参照することもでき、この場合のリレーションは自己関連と呼ばれます。自己関連は、1対1、1対多、多対多のいずれのカーディナリティでも可能です。

自己関連は常に@relation属性を必要とすることに注意してください。

一対一の自己関連

以下の例は、一対一の自己関連をモデル化しています

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人の後継者を持つことができる」(例:メアリーはブログ所有者としてサラの後継者である)

: 一対一の自己関連は、両側で必須にすることはできません。どちらか一方または両方がオプションである必要があります。そうでない場合、最初のUserレコードを作成することが不可能になります。

一対一の自己関連を作成するには

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

: 一対一の自己関連は、関係の両側が等しい場合でも両側が必要です。たとえば、「親友」のリレーションをモデル化するには、bestfriend1bestfriend2の2つのリレーションフィールドを作成する必要があります。

リレーションのどちらの側も外部キーによって裏付けられることができます。以下の前の例では、successorsuccessorIdによって裏付けられています

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

あるいは、predecessorpredecessorIdによって裏付けられるように書き換えることもできます

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,
},
},
},
});

データベースにおける一対一の自己関連

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

リレーショナルデータベースのみにおいて、一対一の自己関連は以下の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" }
}

一対多の自己関連

一対多の自己関連は以下のようになります

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フィールドを必須にすることで、各ユーザーに教師がいることを必須にすることもできます。

データベースにおける一対多の自己関連

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

リレーショナルデータベースにおいて、一対多の自己関連は以下の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);

teacherIdUNIQUE制約がないことに注意してください。複数の生徒が同じ教師を持つことができます。

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

多対多の自己関連

多対多の自己関連は以下のようになります

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人のユーザーと、"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モデルを次のように定義できます。

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")
}
© . All rights reserved.