LoginSignup
4
2

More than 5 years have passed since last update.

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

Posted at

Graph upserts

オブジェクトグラフを元に、データベース上のレコードの登録・更新・削除を行う機能。

使用例

以下のようなデータがデータベースに登録されているとします。

[ Project {
    id: 53,
    name: 'project1',
    ownerId: null,
    members:
     [ User {
         id: 48,
         name: 'hoge',
         email: 'hoge@example.com',
         tasks:
          [ Task {
              id: 15,
              name: 'hoge\'s task1',
              description: null,
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z },
            Task {
              id: 16,
              name: 'hoge\'s task2',
              description: null,
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] },
       User { id: 49, name: 'piyo', email: 'piyo@example.com', tasks: [] } ] },
  Project {
    id: 54,
    name: 'project2',
    ownerId: null,
    members:
     [ User {
         id: 48,
         name: 'hoge',
         email: 'hoge@example.com',
         tasks:
          [ Task {
              id: 15,
              name: 'hoge\'s task1',
              description: null,
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z },
            Task {
              id: 16,
              name: 'hoge\'s task2',
              description: null,
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] },
       User {
         id: 50,
         name: 'fuga',
         email: 'fuga@example.com',
         tasks:
          [ Task {
              id: 17,
              name: 'fuga\'s task1',
              description: null,
              assignedTo: 50,
              projectId: 54,
              deadline: 2018-09-30T15:00:00.000Z,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] } ] } ]
const { transaction } = require('objection');
const { inspect } = require('util');

await transaction(Project.knex(), async (trx) => {
  const projects = await Project.query(trx)
    .upsertGraph([
      {
        id: 53,
        members: [
          {
            id: 48,
            name: 'hoge',
            tasks: [
              // idが15のtaskを更新
              {
                id: 15,
                name: 'hoge\'s task1',
                description: 'hogeのタスク',
                assignedTo: 48
              }
              // idが16のtaskは指定されていないため、データベース上から関連が削除されます。
            ]
          },
          {
            // idを持っていないため、データベースに新しいレコードが作成される。
            '#id': 'hogepiyo',
            name: 'hogepiyo',
            email: 'hogepiyo@example.com',
            tasks: [
              {
                // idを持っていないため、データベースに新しいレコードが作成される。
                name: "#ref{hogepiyo.name}'s task",
                projectId: 53
              }
            ]
          }
        ]
      },
      {
        // idを持っていないため、データベースに新しいレコードが作成される。
        name: 'project3',
        members: [
          { id: 48 },
          { '#ref': 'hogepiyo' }
        ]
      }
    ], {
      relate: ['members', 'members.tasks'],
      unrelate: ['members'] 
    });

  console.log(inspect(projects, { depth: 5 }));

  // [ Project {
  //     id: 53,
  //     members:
  //      [ User {
  //          id: 48,
  //          name: 'hoge',
  //          tasks:
  //           [ Task {
  //               id: 15,
  //               name: 'hoge\'s task1',
  //               description: 'hogeのタスク',
  //               assignedTo: 48 } ] },
  //        User {
  //          name: 'hogepiyo',
  //          email: 'hogepiyo@example.com',
  //          tasks:
  //           [ Task {
  //               name: 'hogepiyo\'s task',
  //               projectId: 53,
  //               assignedTo: 54,
  //               id: 20 } ],
  //          id: 54 } ] },
  //   Project {
  //     name: 'project3',
  //     members:
  //      [ User { id: 48 },
  //        User { name: 'hogepiyo', email: 'hogepiyo@example.com', id: 54 } ],
  //     id: 58 } ]
});

上記を実行すると、データベース上のデータは、以下のような状態になります。

[ Project {
    id: 53,
    name: 'project1',
    ownerId: null,
    members:
     [ User {
         id: 48,
         name: 'hoge',
         email: 'hoge@example.com',
         tasks:
          [ Task {
              id: 15,
              name: 'hoge\'s task1',
              description: 'hogeのタスク',
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] },
       User {
         id: 54,
         name: 'hogepiyo',
         email: 'hogepiyo@example.com',
         tasks:
          [ Task {
              id: 20,
              name: 'hogepiyo\'s task',
              description: null,
              assignedTo: 54,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:12:08.443Z,
              updatedAt: 2018-08-19T13:12:08.443Z } ] } ] },
  Project {
    id: 54,
    name: 'project2',
    ownerId: null,
    members:
     [ User {
         id: 48,
         name: 'hoge',
         email: 'hoge@example.com',
         tasks:
          [ Task {
              id: 15,
              name: 'hoge\'s task1',
              description: 'hogeのタスク',
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] },
       User {
         id: 50,
         name: 'fuga',
         email: 'fuga@example.com',
         tasks:
          [ Task {
              id: 17,
              name: 'fuga\'s task1',
              description: null,
              assignedTo: 50,
              projectId: 54,
              deadline: 2018-09-30T15:00:00.000Z,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] } ] },
  Project {
    id: 58,
    name: 'project3',
    ownerId: null,
    members:
     [ User {
         id: 48,
         name: 'hoge',
         email: 'hoge@example.com',
         tasks:
          [ Task {
              id: 15,
              name: 'hoge\'s task1',
              description: 'hogeのタスク',
              assignedTo: 48,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:03:57.761Z,
              updatedAt: 2018-08-19T13:03:57.761Z } ] },
       User {
         id: 54,
         name: 'hogepiyo',
         email: 'hogepiyo@example.com',
         tasks:
          [ Task {
              id: 20,
              name: 'hogepiyo\'s task',
              description: null,
              assignedTo: 54,
              projectId: 53,
              deadline: null,
              createdAt: 2018-08-19T13:12:08.443Z,
              updatedAt: 2018-08-19T13:12:08.443Z } ] } ] } ]

upsertGraphメソッドはデフォルトで、以下のように動作します。

  • グラフ中のidを持たないオブジェクトは、データベースの対応するテーブルに新しいレコードとして登録される。
  • グラフ中のidを持つオブジェクトは、データベースの対応するレコードが更新される。
  • データベース上には関連が存在するが、グラフ中に現れなかったオブジェクトは、データベースからレコードが削除される。

内部的にinsertGraphメソッドを利用しているため、#id#refの機能を利用することができます。

上記の例では、第2引数でrelateおよびunrelateオプションを指定することで、デフォルトの挙動を変更しています。

{
  // membersおよびtasksについては、データベース上に存在するレコードに対する関連を作成できるようにする
  relate: ['members', 'members.tasks'],

  // membersについては、グラフ中に登場しないオブジェクトがあった際は、データベース上のレコードは削除せず、関連の削除のみを行う(usersテーブルからはレコードを削除せず、joinテーブルのみからレコードを削除する)
  unrelate: ['members'] 
}

insertGraph同様、デフォルトではatomicではないので、transactionと併用する必要があります。

参考

4
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
4
2