LoginSignup
3
2

More than 5 years have passed since last update.

Node.js向けORM Objection.jsを使ってみた(5) - Graph inserts

Posted at

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のプロパティを設定する際に、別のモデルのプロパティの値を参照できます。

参考

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2