メインコンテンツまでスキップ

Nested Write(create)

create メソッド内でリレーション先のレコードを同時に作成・関連付けしたい場合に利用します。

使用するには事前にリレーション定義が必要です。

説明例用のシート

リレーション定義のシート例を使用します。

使用できる操作

操作内容
createリレーション先のレコードを新規作成して関連付け
createManyリレーション先のレコードを複数新規作成して関連付け
connect既存のリレーション先レコードを関連付け
connectOrCreate既存のレコードがあれば関連付け、なければ新規作成して関連付け

リレーション種類ごとの対応表

操作manyToOneoneToOneoneToManymanyToMany
create単一のみ単一のみ単一/配列単一/配列
createMany--対応-
connect対応対応単一/配列単一/配列
connectOrCreate対応対応単一/配列単一/配列
注記

to-one リレーションは FK の位置によって動作が異なります。

  • manyToOne(FK 保有側): 自レコードの FK に値がセットされます
  • oneToOne(非FK側): FK を保有するリレーション先レコードの FK が書き換えられます。自レコードは変更されません

oneToOne は 1 対 1 の FK を持たない側専用の定義です(リレーション定義を参照)。

oneToOne(非FK側)の挙動

操作動作対象レコードが存在しない場合
createリレーション先レコードを FK 自動セットで作成-
connect置き換え(既接続レコードの FK を null 化してから、対象レコードの FK を親にセット)NestedWriteConnectNotFoundError
connectOrCreate存在すれば connect と同じ置き換え、なければ FK 自動セットで作成-

createMany や配列形式の指定は NestedWriteInvalidOperationError になります。

create

manyToOne での create

ユーザーを作成しながら、そのユーザーに紐づく投稿も同時に作成する例です。ただし manyToOne は逆方向(投稿側から著者を作成)として使います。

const result = gassma.Posts.create({
data: {
id: 4,
title: "新しい記事",
published: true,
author: {
create: {
id: 4,
name: "Dave",
email: "[email protected]",
},
},
},
});

上記を実行すると以下が行われます。

  1. Users シートに Dave が作成される
  2. Dave の id(= 4)が Posts の authorId に自動セットされる
  3. Posts シートに新しい記事が作成される

戻り値は以下の形式です。

{
id: 4,
title: "新しい記事",
authorId: 4,
published: true,
}

oneToOne での create

ユーザー作成と同時にプロフィールも作成します。oneToOne(非FK側)では、リレーション先レコードの FK に親の値が自動セットされます。

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
profile: {
create: { id: 3, bio: "新しく来ました" },
},
},
});

上記を実行すると以下が行われます。

  1. Users シートに Dave が作成される
  2. Profiles シートに { id: 3, userId: 4, bio: "新しく来ました" } が作成される(userId は Dave の id = 4 が自動セット)

oneToMany での create

ユーザー作成と同時に投稿も作成します。

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
posts: {
create: [
{ id: 4, title: "Dave の記事1", published: true },
{ id: 5, title: "Dave の記事2", published: false },
],
},
},
});

上記を実行すると以下が行われます。

  1. Users シートに Dave が作成される
  2. Posts シートに 2 件の記事が作成される(authorId は Dave の id = 4 が自動セット)

配列ではなく単一オブジェクトで 1 件だけ作成することもできます。

posts: {
create: { id: 4, title: "Dave の記事", published: true },
}

manyToMany での create

投稿作成と同時にタグも作成し、中間テーブルに関連付けます。

const result = gassma.Posts.create({
data: {
id: 4,
title: "新しい記事",
authorId: 1,
published: true,
tags: {
create: { id: 3, name: "TypeScript" },
},
},
});

上記を実行すると以下が行われます。

  1. Posts シートに新しい記事が作成される
  2. Tags シートに "TypeScript" タグが作成される
  3. PostTags シートに { postId: 4, tagId: 3 } が作成される

createMany

oneToMany で複数の子レコードを一括作成します。

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
posts: {
createMany: {
data: [
{ id: 4, title: "記事1", published: true },
{ id: 5, title: "記事2", published: false },
],
},
},
},
});

各レコードの authorId には Dave の id が自動セットされます。

connect

既存のレコードを関連付けます。where 条件で対象レコードを指定します。

manyToOne での connect

既存のユーザーと紐づけて投稿を作成します。

const result = gassma.Posts.create({
data: {
id: 4,
title: "新しい記事",
published: true,
author: {
connect: { name: "Alice" },
},
},
});

上記を実行すると以下が行われます。

  1. Users シートから name: "Alice" のレコードを検索
  2. 見つかった Alice の id(= 1)が Posts の authorId に自動セットされる
  3. Posts シートに新しい記事が作成される
注意

条件に一致するレコードが見つからない場合、NestedWriteConnectNotFoundError がスローされます。

oneToOne での connect

ユーザー作成と同時に、既存のプロフィールを紐づけます。

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
profile: {
connect: { id: 1 },
},
},
});

上記を実行すると、Profiles シートの id: 1userId が Dave の id(= 4)に更新されます。

oneToOne の connect は置き換えとして動作します。親にすでに接続されているリレーション先レコードがある場合、そのレコードの FK を null にしてから、対象レコードの FK を親にセットします。

注意

条件に一致するレコードが見つからない場合、NestedWriteConnectNotFoundError がスローされます。

oneToMany での connect

ユーザー作成と同時に、既存の投稿を紐づけます。

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
posts: {
connect: [
{ title: "下書き記事" },
],
},
},
});

上記を実行すると、Posts シートの「下書き記事」の authorId が Dave の id(= 4)に更新されます。

manyToMany での connect

既存のタグと投稿を関連付けます。

const result = gassma.Posts.create({
data: {
id: 4,
title: "新しい記事",
authorId: 1,
published: true,
tags: {
connect: [
{ name: "GAS" },
{ name: "JavaScript" },
],
},
},
});

上記を実行すると以下が行われます。

  1. Posts シートに新しい記事が作成される
  2. PostTags シートに { postId: 4, tagId: 1 }{ postId: 4, tagId: 2 } が作成される

Tags シートのレコード自体は変更されません。

connectOrCreate

既存のレコードがあれば関連付け、なければ新規作成して関連付けます。

const result = gassma.Posts.create({
data: {
id: 4,
title: "新しい記事",
published: true,
author: {
connectOrCreate: {
where: { name: "Alice" },
create: {
id: 4,
name: "Alice",
email: "[email protected]",
},
},
},
},
});

上記の場合、Users シートに Alice が存在するため connect と同じ動作になります。存在しない場合は create のデータで新規作成されます。

oneToOne(非FK側)でも同様に、見つかった場合は connect と同じ置き換え動作、見つからない場合はリレーション先レコードが FK 自動セットで作成されます。

oneToMany / manyToMany では配列で複数指定できます。

tags: {
connectOrCreate: [
{
where: { name: "GAS" },
create: { id: 3, name: "GAS" },
},
{
where: { name: "新しいタグ" },
create: { id: 4, name: "新しいタグ" },
},
],
}

深いネスト

Nested write は再帰的に処理されるため、深い階層のリレーションも一度に作成できます。

例えば、ユーザー → 投稿 → タグ を一度に作成する場合:

const gassma = new Gassma.GassmaClient({
relations: {
Users: {
posts: {
type: "oneToMany",
to: "Posts",
field: "id",
reference: "authorId",
},
},
Posts: {
tags: {
type: "manyToMany",
to: "Tags",
field: "id",
reference: "id",
through: {
sheet: "PostTags",
field: "postId",
reference: "tagId",
},
},
},
},
});

const result = gassma.Users.create({
data: {
id: 4,
name: "Dave",
email: "[email protected]",
posts: {
create: {
id: 4,
title: "Dave の記事",
published: true,
tags: {
create: { id: 3, name: "TypeScript" },
},
},
},
},
});

上記を実行すると以下の順序で処理が行われます。

  1. Users シートに Dave が作成される
  2. Posts シートに記事が作成される(authorId: 4 が自動セット)
  3. Tags シートに "TypeScript" が作成される
  4. PostTags シートに関連行が作成される

注意事項

  • Nested write は create メソッドのみで利用できます。createMany / updateMany 等では利用できません。
  • FK は自動セットされますが、PK(id 等)は明示的に指定する必要があります。オートインクリメント機能はありません。
  • connect で指定した where 条件に一致するレコードが見つからない場合、NestedWriteConnectNotFoundError がスローされます。
  • Nested write は複数のシートに書き込みますが、途中でエラーになった場合はどのシートにも 1 行も書かれません。詳細は書き込みの原子性と同時実行を参照してください。

バリデーション

エラー原因
NestedWriteWithoutRelationsErrorリレーション定義なしで nested write 構文を使用
NestedWriteConnectNotFoundErrorconnect / connectOrCreate の where でレコードが見つからない
NestedWriteInvalidOperationErrorリレーション種別に対応しない操作を指定(例: oneToOne に createMany や配列形式を指定)