Graph inserts
任意のオブジェクトグラフをデータベースへ挿入する機能。
使用法
QueryBuilderのinsertGraphメソッドを使用します。
引数として渡したオブジェクトから、ModelのrelationMappingsの定義に従って、関連するテーブル群に対してレコードを作成してくれます。
注意点としては、insertGraphメソッドはatomicではないため、transactionと併用する必要があります。
const { transaction } = require('objection');
await transaction(Project.knex(), async (trx) => {
const project = await Project
.query(trx)
.insertGraph({
name: 'sample',
members: [
{ name: 'sample_user1', email: 'sample1@example.com' },
{ name: 'sample_user2', email: 'sample2@example.com' }
]
});
console.log(project);
// Project {
// name: 'sample',
// members:
// [ User { name: 'sample_user1', email: 'sample1@example.com', id: 7 },
// User { name: 'sample_user2', email: 'sample2@example.com', id: 8 } ],
// id: 7 }
});
PostgreSQLでは、以下のようなSQLが実行されます。
insert into "projects" ("name") values ('sample') returning "id";
insert into "users" ("email", "name") values ('sample1@example.com', 'sample_user1'), ('sample2@example.com', 'sample_user2') returning "id";
insert into "projectMembers" ("projectId", "userId") values (7, 7), (7, 8) returning "userId";
"#id"
、"#ref"
の使用例
const { inspect } = require('util');
const { transaction } = require('objection');
await transaction(Project.knex(), async (trx) => {
const projects = await Project
.query(trx)
.insertGraph([
{
name: 'sample_project1',
members: [
{
'#id': 'sampleUser1',
name: 'sample_user1',
email: 'sample1@example.com'
},
{
name: 'sample_user2',
email: 'sample2@example.com'
}
]
},
{
name: 'sample_project2',
members: [
{ '#ref': 'sampleUser1' }
]
}
]);
console.log(inspect(projects, { depth: 3 }));
// [ Project {
// name: 'sample_project1',
// members:
// [ User { name: 'sample_user1', email: 'sample1@example.com', id: 15 },
// User { name: 'sample_user2', email: 'sample2@example.com', id: 16 } ],
// id: 14 },
// Project {
// name: 'sample_project2',
// members:
// [ User { name: 'sample_user1', email: 'sample1@example.com', id: 15 } ],
// id: 15 } ]
console.log(projects[0].members[0].id === projects[1].members[0].id); // true
});
以下のようなSQLが実行されます。
insert into "projects" ("name") values ('sample_project1'), ('sample_project2') returning "id";
insert into "users" ("email", "name") values ('sample1@example.com', 'sample_user1'), ('sample2@example.com', 'sample_user2') returning "id";
insert into "projectMembers" ("projectId", "userId") values (16, 17), (16, 18), (17, 17) returning "userId";
- 複数のプロジェクトを同時に作成したい。
- その際に、プロジェクトに所属するユーザも一緒に作成したい。
- あるユーザを複数のプロジェクトに所属させたい。
というようなケースを想定します。
その際は、上記のように"#id"
と"#ref"
を使用することで実現できます。
#id
プロパティで、グラフ内のあるモデルに対する参照を作成し、
#ref
プロパティで、グラフ内の別の場所からそのモデルを参照することができます。
relateオプション
const { inspect } = require('util');
const { transaction } = require('objection');
await transaction(Project.knex(), async (trx) => {
const user = await User
.query()
.where({ email: 'taro@example.com' })
.first();
const project = await Project
.query(trx)
.insertGraph({
name: 'relate_sample',
members: [
{ id: user.id }
]
}, {
relate: true
});
console.log(project);
// Project { name: 'relate_sample', members: [ User { id: 1 } ], id: 24 }
});
以下のようなSQLが実行されます。
insert into "projects" ("name") values ('relate_sample') returning "id";
insert into "projectMembers" ("projectId", "userId") values (24, 1) returning "userId";
- プロジェクトを作成する
- データベース上にすでに存在するユーザをそのプロジェクトに所属させる
というようなケースを想定しています。
このようなケースは、insertGraphメソッドの第2引数に、relateプロパティをtrueに設定したオブジェクトを渡すことで実現できます。
relateプロパティをtrueに指定することで、すでにデータベース上に存在するレコードを、新規に作成されるレコードと関連づけることができます。
より複雑な例
const { inspect } = require('util');
const { transaction } = require('objection');
await transaction(Project.knex(), async (trx) => {
const user = await User
.query()
.where({ email: 'taro@example.com' })
.first();
const projects = await Project
.query(trx)
.insertGraph([
{
'#id': 'project1',
name: 'sample1',
members: [
{
'#id': 'user1',
name: 'user1',
email: 'user1@example.com',
tasks: [
{
name: 'task1',
projectId: '#ref{project1.id}'
}
]
},
{ id: user.id }
]
},
{
'#id': 'project2',
name: 'sample2',
members: [
{
'#ref': 'user1',
tasks: [
{
name: "#ref{user1.name}'s task",
projectId: '#ref{project2.id}'
}
]
}
]
}
], { relate: true });
console.log(inspect(projects, { depth: 5 }));
// [ Project {
// name: 'sample1',
// members:
// [ User {
// name: 'user1',
// email: 'user1@example.com',
// tasks:
// [ Task { name: 'task1', projectId: 33, assignedTo: 23, id: 7 } ],
// id: 23 },
// User { id: 1 } ],
// id: 33 },
// Project {
// name: 'sample2',
// members:
// [ User {
// tasks:
// [ Task { name: 'user1\'s task', projectId: 34, assignedTo: 23, id: 8 } ],
// name: 'user1',
// email: 'user1@example.com',
// id: 23 } ],
// id: 34 } ]
});
以下のようなSQLが実行されます。
insert into "projects" ("name") values ('sample1'), ('sample2') returning "id";
insert into "users" ("email", "name") values ('user1@example.com', 'user1') returning "id";
insert into "tasks" ("assignedTo", "name", "projectId") values (23, 'task1', 33), (23, 'user1\'s task', 34) returning "id";
insert into "projectMembers" ("projectId", "userId") values (33, 23), (33, 1), (34, 23) returning "userId";
上記のように、3階層以上のModelにまたがる処理も実行可能。
#ref{<id>.<property>}
の形式の値を使用することで、あるModelのプロパティを設定する際に、別のモデルのプロパティの値を参照できます。
参考