Node.js向けORM Objection.jsを使ってみた(3) - 基本的なクエリの発行方法について

## 概要

データベースに対してクエリを発行する際は、QueryBuilderオブジェクトを使用します。

Modelのqueryメソッド(staticメソッド)を実行すると、QueryBuilderを取得できます。

メソッドチェーンによってクエリを構築していき、thenメソッドを実行すると、構築したクエリが実行されます。

```javascript
const queryBuilder = Project.query();

queryBuilder.then(projects => console.log(projects));

// awaitでも実行可能
const projects = await queryBuilder.where('name', '=', 'hogepiyo');
```

Modelの定義については[こちら](https://qiita.com/uki00a/items/14cfb36a5222447a8919)を参照。

## Fetch Queries

### テーブルの全レコードを取得する

```javascript
const projects = await Project.query();
// select "projects".* from "projects"

console.log(projects.every(project => project instanceof Project)); // true
```

### 指定したIDを保持するレコードを取得する

whereメソッドを使用して、返却されるレコードを制限できます。

```javascript
const project = await Project.query().where({ id: 1 }).first();
// select "projects".* from "projects" where "id" = 1

console.log(project instanceof Project); // true

// 上記と同様のSQLを実行する
await Project.query().where('id', '=', 1).first();
await Project.query().where('id', 1).first()

// findByIdメソッドを使用することで、より簡潔に記述できる
await Project.query().findById(1);
```

### 複雑なクエリ

```javascript
const tasks = await Task
  .query()
  .select('name', 'description')
  .where('deadline', '>=', new Date())
  .orWhere(function() {
    this
      .where('assignedTo', '=', 1)
      .andWhere('projectId', '=', 2);
  })
  .orderBy('deadline', 'asc');

// select "name", "description" from "tasks" where "deadline" >= '2018-08-19 01:43:33.853' or ("assignedTo" = 1 and "projectId" = 2) order by "deadline" asc
```

## Insert Queries

### レコードを作成する

```js
const user = await User.query().insert({ name: 'fuga', email: 'fuga@example.com' });
// insert into "users" ("email", "name") values ('fuga@example.com', 'fuga') returning "id"

console.log(user); // User { name: 'fuga', email: 'fuga@example.com', id: 3 }

const users = await User.query().insert([
  { name: 'hoge', email: 'hoge@example.com' },
  { name: 'piyo', email: 'piyo@example.com' }
]);
// insert into "users" ("email", "name") values ('hoge@example.com', 'hoge'), ('piyo@example.com', 'piyo') returning "id"

console.log(users);
// [ User { name: 'hoge', email: 'hoge@example.com', id: 5 },
//   User { name: 'piyo', email: 'piyo@example.com', id: 6 } ]
```

### 作成されたレコードの全カラムを取得する

insertメソッドを実行すると、引数で指定したプロパティとプライマリキーのみが設定された状態でModelのインスタンスが返却されます。

カラムにデフォルト値を設定しているなどの都合で、すべてのカラムが必要な場合は、insertAndFetchメソッドを使用します。

```js
const task = await Task.query().insert({ assignedTo: 1, projectId: 1, name: 'sample' });

console.log(task); // Task { assignedTo: 1, projectId: 1, name: 'sample', id: 1 }

const taskWithAllProps = await Task.query().insertAndFetch({ assignedTo: 1, projectId: 1, name: 'sample' });
// PostgreSQLであれば、
// `await Task.query().insert({ assignedTo: 1, projectId: 1, name: 'sample' }).returning('*')`
// でも可

console.log(taskWithAllProps);
// Task {
//   assignedTo: 1,
//   projectId: 1,
//   name: 'sample',
//   id: 2,
//   description: null,
//   deadline: null,
//   createdAt: 2018-08-18T17:18:12.358Z,
//   updatedAt: 2018-08-18T17:18:12.358Z }
```

## Update Queries

### 特定のレコードを更新する

patchまたはupdateメソッドを使用することで、レコードの更新を行うことができます。

戻り値は、更新されたレコードの行数です。

patchとupdateは、ModelのjsonSchemaプロパティでrequiredと指定されたプロパティの扱いが異なります。

updateメソッドは、required指定されたプロパティが引数に指定されなかった場合、ValidationErrorを発生させます。

そのため、レコードの一部のカラムのみを更新する際はpatch、全カラムを更新する際はupdateを使うと良いと思います。

```js
const num = await User.query().where({ id: 1 }).patch({ name: 'hoge' });
// update "users" set "name" = 'hoge' where "id" = '1'

console.log(num); // 1

// ModelのjsonSchemaプロパティでrequiredと指定したプロパティを省略すると、
// ValidationErrorが発生する
await User.query().where({ id: 1 }).update({ name: 'hoge' }));
// ValidationError: email: is a required property
```

### 更新後、最新レコードを取得する

patchAndFetchByIdまたはupdateAndFetchByIdメソッドを使用すると、

更新されたレコードの件数ではなく、更新後のレコードそのものを取得することができます。

```js
const user = await User.query().patchAndFetchById(1, { name: 'piyo' });
// update "users" set "name" = 'piyo' where "users"."id" = '1'

console.log(user); // User { name: 'piyo', id: 1, email: 'taro@example.com' }

// PostgreSQLの場合は、`await User.query().patch({ name: 'piyo' }).where({ id: 1 }).returning('*')`でも可
```

## Delete Queries

### 特定のレコードを削除する

deleteメソッドを使用します。

戻り値は、削除されたレコードの行数です。

```js
const num = await User.query().delete().where({ id: 3 });
// delete from "users" where "id" = '3'

console.log(num); // 1
```

## 参考

https://vincit.github.io/objection.js/#table-queries