amazon-qldb-driver-nodejsからQLDBを使う②（書き込み、検索編）

前回の記事はこちらです
https://qiita.com/shushutochako/items/bbb3ec5f721c57949ea7

## 概要

今回はテーブルへのデータの追加、更新と検索を試してみたいと思います。
AWSの認証などについては前回の記事を参考にして下さい。

また、本記事は公式チュートリアルの内容を参考にしています。
https://docs.aws.amazon.com/qldb/latest/developerguide/getting-started.nodejs.tutorial.html

## moduleのバージョン

今回使用した「amazon-qldb-driver-nodejs」自体や他に必要なモジュールのバージョンは以下のとおりです。
前回の記事の時点ではpreviewでしたが、2020/4のはじめにPre-release版がリリースされたようですね！
正式なリリースも近いのではないでしょうか。

- amazon-qldb-driver-nodejs
  - v1.0.0-rc.1
- aws-sdk
  - 2.663.0
- ion-js
  - 4.0.0
- typescript
  - 3.7.5
- jsbi
  - 3.1.1    

## 前提

事前にAWSのコンソールから台帳に任意の名前でテーブルを作成しておいてください。
クエリエディタで以下を実行するとテーブルを作成できます。

```
CREATE TABLE {Your Table Name}
```

QLDBはドキュメント指向のデータモデルを採用しているため、事前に列定義をする必要はありません。

## データの追加

それではテーブルへのデータの追加を試していきます。
**※「{Your Table Name}」となっている部分は作成したテーブルに合わせて修正して下さい**


```typescript
await qldbDriver.executeLambda(async (txn) => {
  const results = await txn.execute('INSERT INTO {Your Table Name} ?', [
    {
      id: 1,
      name: 'TestRecord',
      createdAt: new Date()
    },
    {
      id: 2,
      name: 'TestRecord2',
      createdAt: new Date()
    },
    {
      id: 3,
      name: 'TestRecord3',
      category: 'A',
      createdAt: new Date()
    }
  ]);

  results.getResultList().forEach(element => {
    // 各データ採番されたdocumentIdを表示
    console.log(element.get('documentId').stringValue());
  });
});
```

生成したPooledQldbDriverのインスタンスからexecuteLambdaを呼び出します。
以前のバージョンではsessionを取得してexecuteをする必要がありましたが、driverから直接実行出来るようになりました。

excuteLambdaを実行すると関数にTransactionExecutorが渡されます。
TransactionExecutorのexcute関数にプレースホルダを含むPartiQL文とパラメータを渡して呼び出します。
excute関数の第二引数は可変長になっており、プレースホルダに渡す値を順に指定します。
excuteLambdaを使用すると暗黙的にトランザクションの開始とコミットが行われるようです。
追加した各データにはdocumentIdが自動で採番されます。


## データの変更

先ほど追加したデータの内容を変更します。以下の例ではidをキーにnameを更新しています。
普段SQLに慣れ親しんだ方であれば、特に困るところもなさそうです。

```typescript

await qldbDriver.executeLambda(async (txn) => {
  await execute(txn, 'UPDATE TestTable AS t SET t.name = ? WHERE t.id = ?', 'NewTestRecord1', 1);
});
```

## ロールバック

もし追加内容をロールバックしたい場合はTransactionExecutorのabortを呼び出します。
これによりロールバックされ「LambdaAbortedError」がthrowされます。

```typescript
const results = await txn.execute('INSERT INTO {Your Table Name} ?', [
　・
　・
　・
]);

await txn.abort();

```

## データの取得

続いて、データを取得してみます。
こちらも基本的にはinsertやupdateと変わりません。

```typescript
await qldbDriver.executeLambda(async (txn) => {
  const results = await txn.execute('SELECT * FROM TestTable WHERE id = ?',10);
  results.getResultList().forEach(element => {
    if (element.get('name').getType().name === 'string') {
      // 各データのnameを取得
      console.log(element.get('name').stringValue());
    }
  });    
});
```

  
QLDBではAmazon Ionの形式でデータを取り扱います。
以下のコードではdocument内のnameプロパティについて型を確認したのち実際の値を取得しています。

```typescript
if (element.get('name').getType().name === 'string') {
  // 各データのnameを取得
  console.log(element.get('name').stringValue());
}
```

Amazon IonはJsonのスーパーセットで、厳格な数値の定義など色々と拡張されているようです。
https://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/ion.html#ion.json-compatible


## コード全文

今回実装したコードの全文は以下のとおりです。

```typescript

import { PooledQldbDriver } from "amazon-qldb-driver-nodejs";

const AWS = require("aws-sdk");
AWS.config.loadFromPath("./credentials.json");

(async () => {
  const testServiceConfigOptions = {
    region: "{Your Region}"
  };
  const qldbDriver: PooledQldbDriver = new PooledQldbDriver("{Your Ledger}", testServiceConfigOptions);

  // insert
  await qldbDriver.executeLambda(async (txn) => {
    const results = await txn.execute('INSERT INTO {Your Table Name} ?', [
      {
        id: 1,
        name: 'TestRecord',
        createdAt: new Date()
      },
      {
        id: 2,
        name: 'TestRecord2',
        createdAt: new Date()
      },
      {
        id: 3,
        name: 'TestRecord3',
        category: 'A',
        createdAt: new Date()
      }
    ]);

    results.getResultList().forEach(element => {
      // 各データ採番されたdocumentIdを表示
      console.log(element.get('documentId').stringValue());
    });
  });

  // update
  await qldbDriver.executeLambda(async (txn) => {
    await txn.execute('UPDATE {Your Table Name} AS t SET t.name = ? WHERE t.id = ?', 'NewTestRecord3', 10);
  });

  // select
  await qldbDriver.executeLambda(async (txn) => {
    const results = await txn.execute('SELECT * FROM {Your Table Name} WHERE id = ?', 10);
    results.getResultList().forEach(element => {
      if (element.get('name').getType().name === 'string') {
        // 各データのnameを取得
        console.log(element.get('name').stringValue());
      }
    });
  });

})().catch(err => {
  console.error(err);
});
```

## さいごに

PartiQLベースで操作できることで、SQLに慣れている人であれば直感的に使用できそうですね！
今回はシンプルな「追加」「更新」「検索」などを試してみました。
しかし、「ORDER BY」句が現状は使用できなかったり、完全にSQLの感覚で考えていると困る点がいくつかあるように感じました。
QLDB Streamingなるもののも出てきたようで、検索の柔軟性に関しては他のサービスに任せるなど役割分担を考えていくことが必要そうだと感じました。
https://dev.classmethod.jp/articles/update_qldb_streaming/

ZEROBILLBANKでは一緒に働く仲間を募集中です。
[ZEROBILLBANK JAPAN Inc.](https://zerobillbank.com/%e3%81%8a%e5%95%8f%e3%81%84%e5%90%88%e3%82%8f%e3%81%9b/)