

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Node.js 用 Amazon QLDB ドライバー — クックブックリファレンス
<a name="driver-cookbook-nodejs"></a>

**重要**  
サポート終了通知: 既存のお客様は、07/31/2025 のサポート終了まで Amazon QLDB を使用できます。詳細については、[「Amazon QLDB 台帳を Amazon Aurora PostgreSQL に移行する](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)」を参照してください。

このリファレンスガイドでは、Node.js 用 Amazon QLDB ドライバーの一般的なユースケースについて説明します。JavaScript と TypeScript のコード例を示して、ドライバーを使用した基本的な*作成、読み取り、更新、削除* (CRUD) オペレーションの実行方法を解説します。Amazon Ion データを処理するためのコード例も含まれています。さらに、このガイドでは、トランザクションをべき等にし、一意性の制約を実装するためのベストプラクティスを取り上げています。

**Contents**
+ [ドライバーのインポート](#cookbook-nodejs.importing)
+ [ドライバーのインスタンス化](#cookbook-nodejs.instantiating)
+ [CRUD オペレーション](#cookbook-nodejs.crud)
  + [テーブルの作成](#cookbook-nodejs.crud.creating-tables)
  + [インデックスの作成](#cookbook-nodejs.crud.creating-indexes)
  + [ドキュメントの読み取り](#cookbook-nodejs.crud.reading)
    + [クエリパラメータの使用](#cookbook-nodejs.reading-using-params)
  + [ドキュメントの挿入](#cookbook-nodejs.crud.inserting)
    + [1 つのステートメントで複数のドキュメントの挿入](#cookbook-nodejs.crud.inserting.multiple)
  + [ドキュメントの更新](#cookbook-nodejs.crud.updating)
  + [ドキュメントの削除](#cookbook-nodejs.crud.deleting)
  + [トランザクションで複数のステートメントの実行](#cookbook-nodejs.crud.multi-statement)
  + [再試行ロジック](#cookbook-nodejs.crud.retry-logic)
  + [一意性制約の実装](#cookbook-nodejs.crud.uniqueness-constraints)
+ [Amazon Ion の操作](#cookbook-nodejs.ion)
  + [Ion モジュールのインポート](#cookbook-nodejs.ion.import)
  + [Ion 型の作成](#cookbook-nodejs.ion.creating-types)
  + [Ion バイナリダンプの取得](#cookbook-nodejs.ion.getting-binary)
  + [Ion テキストダンプの取得](#cookbook-nodejs.ion.getting-text)

## ドライバーのインポート
<a name="cookbook-nodejs.importing"></a>

次のコード例では、ドライバーをインポートします。

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');
var ionjs = require('ion-js');
```

------
#### [ TypeScript ]

```
import { QldbDriver, TransactionExecutor } from "amazon-qldb-driver-nodejs";
import { dom, dumpBinary, load } from "ion-js";
```

------

**注記**  
この例では、Amazon Ion パッケージ (`ion-js`) もインポートします。このリファレンスでいくつかのデータオペレーションを実行するときに Ion データを処理するには、このパッケージが必要です。詳細については、「[Amazon Ion の操作](#cookbook-nodejs.ion)」を参照してください。

## ドライバーのインスタンス化
<a name="cookbook-nodejs.instantiating"></a>

次のコード例は、デフォルト設定を使用して指定された台帳名に接続するドライバーのインスタンスを作成します。

------
#### [ JavaScript ]

```
const qldbDriver = new qldb.QldbDriver("vehicle-registration");
```

------
#### [ TypeScript ]

```
const qldbDriver: QldbDriver = new QldbDriver("vehicle-registration");
```

------

## CRUD オペレーション
<a name="cookbook-nodejs.crud"></a>

QLDB は**作成、読み取り、更新、および削除 (CRUD) オペレーションをトランザクションの一部として実行します。

**警告**  
ベストプラクティスとして、書き込みトランザクションを厳密にべき等にしてください。

**トランザクションをべき等にする**

再試行の場合に予期しない結果を避けるために、書き込みトランザクションをべき等にすることをお勧めします。トランザクションは、複数回実行して毎回同じ結果を生成できる場合、*idempotent* です。

例えば、`Person` という名前のテーブルにドキュメントを挿入するトランザクションについて考えてみます。トランザクションは、まずドキュメントがテーブル内に既に存在するかどうかをチェックする必要があります。このチェックを行わないと、テーブルに重複するドキュメントができる可能性があります。

QLDB がサーバー側でトランザクションを正常にコミットできるが、レスポンスを待機中にクライアントはタイムアウトするとします。トランザクションがべき等でない場合、再試行時に同じドキュメントが複数回挿入される可能性があります。

**インデックスを使用してテーブル全体のスキャンを回避する**

インデックス付きフィールドまたはドキュメント ID で**等価演算子を使用する `WHERE` 述語句でステートメントを実行する (例: `WHERE indexedField = 123` または `WHERE indexedField IN (456, 789)`) こともお勧めします。このインデックス付きのルックアップがない場合、QLDB はテーブルスキャンを実行する必要があり、トランザクションタイムアウトや**オプティミスティック同時実行制御 (OCC) 競合が発生する可能性があります。

OCC の詳細については、「[Amazon QLDB 同時実行モデル](concurrency.md)」を参照してください。

**暗黙的に作成されたトランザクション**

[QldbDriver.executeLambda](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_qldbdriver_.qldbdriver.html#executelambda) メソッドは [TransactionExecutor](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_transactionexecutor_.transactionexecutor.html) のインスタンス (これを使用してステートメントを実行可能) を受け取る Lambda 関数を受け入れます。`TransactionExecutor` のインスタンスは、暗黙的に作成されたトランザクションをラップします。

Lambda 関数内でステートメントを実行するには、トランザクションエグゼキューターの [execute](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_transactionexecutor_.transactionexecutor.html#execute) メソッドを使用します。ドライバーは、Lambda 関数が戻ったときに暗黙的にトランザクションをコミットします。

**注記**  
`execute` メソッドは、Amazon Ion 型と Node.js ネイティブ型の両方をサポートします。Node.js ネイティブ型を引数として `execute` に渡すと、ドライバーが `ion-js` パッケージを使用して Ion 型に変換します (指定された Node.js データ型の変換がサポートされている場合)。サポートされているデータ型と変換ルールについては、Ion JavaScript DOM の「[README](https://github.com/amzn/ion-js/blob/master/src/dom/README.md)」を参照してください。

次のセクションでは、基本的な CRUD オペレーションの実行、カスタム再試行ロジックの指定、一意性制約の実装方法について説明します。

**Contents**
+ [テーブルの作成](#cookbook-nodejs.crud.creating-tables)
+ [インデックスの作成](#cookbook-nodejs.crud.creating-indexes)
+ [ドキュメントの読み取り](#cookbook-nodejs.crud.reading)
  + [クエリパラメータの使用](#cookbook-nodejs.reading-using-params)
+ [ドキュメントの挿入](#cookbook-nodejs.crud.inserting)
  + [1 つのステートメントで複数のドキュメントの挿入](#cookbook-nodejs.crud.inserting.multiple)
+ [ドキュメントの更新](#cookbook-nodejs.crud.updating)
+ [ドキュメントの削除](#cookbook-nodejs.crud.deleting)
+ [トランザクションで複数のステートメントの実行](#cookbook-nodejs.crud.multi-statement)
+ [再試行ロジック](#cookbook-nodejs.crud.retry-logic)
+ [一意性制約の実装](#cookbook-nodejs.crud.uniqueness-constraints)

### テーブルの作成
<a name="cookbook-nodejs.crud.creating-tables"></a>

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute("CREATE TABLE Person");
    });
})();
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE TABLE Person');
    });
}());
```

------

### インデックスの作成
<a name="cookbook-nodejs.crud.creating-indexes"></a>

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute("CREATE INDEX ON Person (GovId)");
    });
})();
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE INDEX ON Person (GovId)');
    });
}());
```

------

### ドキュメントの読み取り
<a name="cookbook-nodejs.crud.reading"></a>

------
#### [ JavaScript ]

```
(async function() {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

#### クエリパラメータの使用
<a name="cookbook-nodejs.reading-using-params"></a>

次のコード例では、ネイティブ型のクエリパラメータを使用します。

------
#### [ JavaScript ]

```
(async function() {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    // Assumes that Person table has documents as follows:
    // { "GovId": "TOYENC486FH", "FirstName": "Brent" }
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

次のコード例では、Ion 型のクエリパラメータを使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govId = ionjs.load("TOYENC486FH");

        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govId: dom.Value = load("TOYENC486FH");

        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

次のコード例では、複数のクエリパラメータを使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ? AND FirstName = ?', 'TOYENC486FH', 'Brent')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ? AND FirstName = ?', 'TOYENC486FH', 'Brent')).getResultList();
        for (let result of results) {
            console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
            console.log(result.get('FirstName')); // prints [String: 'Brent']
        }
    });
}());
```

------

次のコード例では、クエリパラメータのリストを使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govIds = ['TOYENC486FH','LOGANB486CG','LEWISR261LL'];
        /*
        Assumes that Person table has documents as follows:
        { "GovId": "TOYENC486FH", "FirstName": "Brent" }
        { "GovId": "LOGANB486CG", "FirstName": "Brent" }
        { "GovId": "LEWISR261LL", "FirstName": "Raul" }
        */
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId IN (?,?,?)', ...govIds)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId'));
            console.log(result.get('FirstName'));
            /*
            prints:
            [String: 'TOYENC486FH']
            [String: 'Brent']
            [String: 'LOGANB486CG']
            [String: 'Brent']
            [String: 'LEWISR261LL']
            [String: 'Raul']
            */
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govIds: string[] = ['TOYENC486FH','LOGANB486CG','LEWISR261LL'];
        /*
        Assumes that Person table has documents as follows:
        { "GovId": "TOYENC486FH", "FirstName": "Brent" }
        { "GovId": "LOGANB486CG", "FirstName": "Brent" }
        { "GovId": "LEWISR261LL", "FirstName": "Raul" }
        */
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId IN (?,?,?)', ...govIds)).getResultList();
        for (let result of results) {
            console.log(result.get('GovId'));
            console.log(result.get('FirstName'));
            /*
            prints:
            [String: 'TOYENC486FH']
            [String: 'Brent']
            [String: 'LOGANB486CG']
            [String: 'Brent']
            [String: 'LEWISR261LL']
            [String: 'Raul']
            */
        }
    });
}());
```

------

**注記**  
インデックス付きルックアップなしでクエリを実行すると、完全なテーブルスキャンが呼び出されます。この例では、`GovId` フィールドで[インデックス](ql-reference.create-index.md)を使用して、パフォーマンスを最適化することをお勧めします。`GovId` でインデックスをオンにしないと、クエリのレイテンシーが大きくなり、OCC 競合例外やトランザクションタイムアウトが発生する可能性があります。

### ドキュメントの挿入
<a name="cookbook-nodejs.crud.inserting"></a>

次のコード例では、ネイティブデータ型を挿入します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            await txn.execute('INSERT INTO Person ?', doc);
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc: Record<string, string> = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            await txn.execute('INSERT INTO Person ?', doc);
        }
    });
}());
```

------

次のコード例では、Ion データ型を挿入します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            // Create a sample Ion doc
            const ionDoc = ionjs.load(ionjs.dumpBinary(doc));

            await txn.execute('INSERT INTO Person ?', ionDoc);
        }
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId:TOYENC486FH exists
        // This is critical to make this transaction idempotent
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            const doc: Record<string, string> = {
                'FirstName': 'Brent',
                'GovId': 'TOYENC486FH',
            };
            // Create a sample Ion doc
            const ionDoc: dom.Value = load(dumpBinary(doc));

            await txn.execute('INSERT INTO Person ?', ionDoc);
        }
    });
}());
```

------

このトランザクションは、ドキュメントを `Person` テーブルに挿入します。挿入する前に、まずドキュメントがテーブル内に既に存在しているかどうかをチェックします。**このチェックにより、トランザクションは本質的にべき等になります。**このトランザクションを複数回実行しても、意図しない副作用は発生しません。

**注記**  
この例では、`GovId` フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。`GovId` でインデックスをオンにしないと、ステートメントのレイテンシーが大きくなり、OCC 競合例外やトランザクションタイムアウトが発生する可能性があります。

#### 1 つのステートメントで複数のドキュメントの挿入
<a name="cookbook-nodejs.crud.inserting.multiple"></a>

1 つの [INSERT](ql-reference.insert.md) ステートメントを使用して複数のドキュメントを挿入するために、次のように型 [list](driver-working-with-ion.md#driver-ion-list) のパラメータをステートメントに渡すことができます。

```
// people is a list
txn.execute("INSERT INTO People ?", people);
```

リストを渡すときには、変数プレースホルダー (`?`) を二重山括弧 (`<<...>>`) で囲まないでください。マニュアルの PartiQL ステートメントでは、二重山括弧は**バッグと呼ばれる順序付けされていないコレクションを表します。

### ドキュメントの更新
<a name="cookbook-nodejs.crud.updating"></a>

次のコード例では、ネイティブデータ型を使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', 'John', 'TOYENC486FH');
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', 'John', 'TOYENC486FH');
    });
}());
```

------

次のコード例では、Ion データ型を使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const firstName = ionjs.load("John");
        const govId = ionjs.load("TOYENC486FH");

        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', firstName, govId);
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const firstName: dom.Value = load("John");
        const govId: dom.Value = load("TOYENC486FH");

        await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', firstName, govId);
    });
}());
```

------

**注記**  
この例では、`GovId` フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。`GovId` でインデックスをオンにしないと、ステートメントのレイテンシーが大きくなり、OCC 競合例外やトランザクションタイムアウトが発生する可能性があります。

### ドキュメントの削除
<a name="cookbook-nodejs.crud.deleting"></a>

次のコード例では、ネイティブデータ型を使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('DELETE FROM Person WHERE GovId = ?', 'TOYENC486FH');
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('DELETE FROM Person WHERE GovId = ?', 'TOYENC486FH');
    });
}());
```

------

次のコード例では、Ion データ型を使用します。

------
#### [ JavaScript ]

```
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        const govId = ionjs.load("TOYENC486FH");

        await txn.execute('DELETE FROM Person WHERE GovId = ?', govId);
    });
}());
```

------
#### [ TypeScript ]

```
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        const govId: dom.Value = load("TOYENC486FH");

        await txn.execute('DELETE FROM Person WHERE GovId = ?', govId);
    });
}());
```

------

**注記**  
この例では、`GovId` フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。`GovId` でインデックスをオンにしないと、ステートメントのレイテンシーが大きくなり、OCC 競合例外やトランザクションタイムアウトが発生する可能性があります。

### トランザクションで複数のステートメントの実行
<a name="cookbook-nodejs.crud.multi-statement"></a>

------
#### [ TypeScript ]

```
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd
// set your UPDATE to filter on vin and insured, and check if you updated something or not.
async function insureCar(driver: QldbDriver, vin: string): Promise<boolean> {

    return await driver.executeLambda(async (txn: TransactionExecutor) => {
        const results: dom.Value[] = (await txn.execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)).getResultList();

        if (results.length > 0) {
            await txn.execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin);
            return true;
        }
        return false;
    });
};
```

------

### 再試行ロジック
<a name="cookbook-nodejs.crud.retry-logic"></a>

ドライバーの `executeLambda` メソッドには、再試行可能な例外 (タイムアウトや OCC 競合など) が発生した場合にトランザクションを再試行する組み込みの再試行メカニズムがあります。再試行の最大数とバックオフ戦略を設定できます。

デフォルトの再試行制限は `4` であり、デフォルトのバックオフ戦略は `10` ミリ秒のベースを使用する [defaultBackoffFunction](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/modules/_src_retry_defaultretryconfig_.html#defaultretryconfig) です。再試行設定は、[RetryConfig](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_retry_retryconfig_.retryconfig.html) のインスタンスを使用して、ドライバーインスタンスごとおよびトランザクションごとに設定できます。

次のコード例では、ドライバーのインスタンスのカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');

// Configuring retry limit to 2
const retryConfig = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
    return 1000 * retryAttempt;
};

const retryConfigCustomBackoff = new qldb.RetryConfig(2, customBackoff);
const qldbDriverCustomBackoff = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfigCustomBackoff);
```

------
#### [ TypeScript ]

```
import { BackoffFunction, QldbDriver, RetryConfig } from "amazon-qldb-driver-nodejs"

// Configuring retry limit to 2
const retryConfig: RetryConfig = new RetryConfig(2);
const qldbDriver: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfig);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff: BackoffFunction = (retryAttempt: number, error: Error, transactionId: string) => {
    return 1000 * retryAttempt;
};

const retryConfigCustomBackoff: RetryConfig = new RetryConfig(2, customBackoff);
const qldbDriverCustomBackoff: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfigCustomBackoff);
```

------

次のコード例では、特定の Lambda 実行のカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。`executeLambda` のこの設定は、ドライバーインスタンスに設定された再試行ロジックを上書きします。

------
#### [ JavaScript ]

```
var qldb = require('amazon-qldb-driver-nodejs');

// Configuring retry limit to 2
const retryConfig1 = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig1);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
    return 1000 * retryAttempt;
};

const retryConfig2 = new qldb.RetryConfig(2, customBackoff);

// The config `retryConfig1` will be overridden by `retryConfig2`
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        await txn.execute('CREATE TABLE Person');
    }, retryConfig2);
}());
```

------
#### [ TypeScript ]

```
import { BackoffFunction, QldbDriver, RetryConfig, TransactionExecutor } from "amazon-qldb-driver-nodejs"

// Configuring retry limit to 2
const retryConfig1: RetryConfig = new RetryConfig(2);
const qldbDriver: QldbDriver = new QldbDriver("test-ledger", undefined, undefined, retryConfig1);

// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff: BackoffFunction = (retryAttempt: number, error: Error, transactionId: string) => {
    return 1000 * retryAttempt;
};

const retryConfig2: RetryConfig = new RetryConfig(2, customBackoff);

// The config `retryConfig1` will be overridden by `retryConfig2`
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        await txn.execute('CREATE TABLE Person');
    }, retryConfig2);
}());
```

------

### 一意性制約の実装
<a name="cookbook-nodejs.crud.uniqueness-constraints"></a>

QLDB はユニークインデックスをサポートしていませんが、この動作をアプリケーションに実装できます。

`Person` テーブル内の `GovId` フィールドに対して一意性制約を実装するとします。これを行うには、以下を実行するトランザクションを記述します。

1. テーブルに指定された `GovId` を持つ既存のドキュメントがないことをアサートします。

1. アサーションに合格した場合は、ドキュメントを挿入します。

競合するトランザクションが同時にアサーションに合格すると、一方のトランザクションだけが正常にコミットされます。もう一方のトランザクションは OCC 競合例外で失敗します。

次のコード例は、この一意性制約ロジックを実装する方法を示しています。

------
#### [ JavaScript ]

```
const govId = 'TOYENC486FH';
const document = {
    'FirstName': 'Brent',
    'GovId': 'TOYENC486FH',
};
(async function() {
    await qldbDriver.executeLambda(async (txn) => {
        // Check if doc with GovId = govId exists
        const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            await txn.execute('INSERT INTO Person ?', document);
        }
    });
})();
```

------
#### [ TypeScript ]

```
const govId: string = 'TOYENC486FH';
const document: Record<string, string> = {
    'FirstName': 'Brent',
    'GovId': 'TOYENC486FH',
};
(async function(): Promise<void> {
    await qldbDriver.executeLambda(async (txn: TransactionExecutor) => {
        // Check if doc with GovId = govId exists
        const results: dom.Value[] = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
        // Insert the document after ensuring it doesn't already exist
        if (results.length == 0) {
            await txn.execute('INSERT INTO Person ?', document);
        }
    });
})();
```

------

**注記**  
この例では、`GovId` フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。`GovId` でインデックスをオンにしないと、ステートメントのレイテンシーが大きくなり、OCC 競合例外やトランザクションタイムアウトが発生する可能性があります。

## Amazon Ion の操作
<a name="cookbook-nodejs.ion"></a>

以下のセクションでは、Amazon Ion モジュールを使用して Ion データを処理する方法について説明します。

**Contents**
+ [Ion モジュールのインポート](#cookbook-nodejs.ion.import)
+ [Ion 型の作成](#cookbook-nodejs.ion.creating-types)
+ [Ion バイナリダンプの取得](#cookbook-nodejs.ion.getting-binary)
+ [Ion テキストダンプの取得](#cookbook-nodejs.ion.getting-text)

### Ion モジュールのインポート
<a name="cookbook-nodejs.ion.import"></a>

------
#### [ JavaScript ]

```
var ionjs = require('ion-js');
```

------
#### [ TypeScript ]

```
import { dom, dumpBinary, dumpText, load } from "ion-js";
```

------

### Ion 型の作成
<a name="cookbook-nodejs.ion.creating-types"></a>

次のコード例では、Ion テキストから Ion オブジェクトを作成します。

------
#### [ JavaScript ]

```
const ionText  = '{GovId: "TOYENC486FH", FirstName: "Brent"}';
const ionObj = ionjs.load(ionText);

console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------
#### [ TypeScript ]

```
const ionText: string = '{GovId: "TOYENC486FH", FirstName: "Brent"}';
const ionObj: dom.Value = load(ionText);

console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------

次のコード例では、Node.js ディクショナリから Ion オブジェクトを作成します。

------
#### [ JavaScript ]

```
const aDict = {
    'GovId': 'TOYENC486FH',
    'FirstName': 'Brent'
};
const ionObj = ionjs.load(ionjs.dumpBinary(aDict));
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------
#### [ TypeScript ]

```
const aDict: Record<string, string> = {
    'GovId': 'TOYENC486FH',
    'FirstName': 'Brent'
};
const ionObj: dom.Value = load(dumpBinary(aDict));
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
```

------

### Ion バイナリダンプの取得
<a name="cookbook-nodejs.ion.getting-binary"></a>

------
#### [ JavaScript ]

```
// ionObj is an Ion struct
console.log(ionjs.dumpBinary(ionObj).toString()); // prints 224,1,0,234,238,151,129,131,222,147,135,190,144,133,71,111,118,73,100,137,70,105,114,115,116,78,97,109,101,222,148,138,139,84,79,89,69,78,67,52,56,54,70,72,139,133,66,114,101,110,116
```

------
#### [ TypeScript ]

```
// ionObj is an Ion struct
console.log(dumpBinary(ionObj).toString()); // prints 224,1,0,234,238,151,129,131,222,147,135,190,144,133,71,111,118,73,100,137,70,105,114,115,116,78,97,109,101,222,148,138,139,84,79,89,69,78,67,52,56,54,70,72,139,133,66,114,101,110,116
```

------

### Ion テキストダンプの取得
<a name="cookbook-nodejs.ion.getting-text"></a>

------
#### [ JavaScript ]

```
// ionObj is an Ion struct
console.log(ionjs.dumpText(ionObj)); // prints {GovId:"TOYENC486FH",FirstName:"Brent"}
```

------
#### [ TypeScript ]

```
// ionObj is an Ion struct
console.log(dumpText(ionObj)); // prints {GovId:"TOYENC486FH",FirstName:"Brent"}
```

------

Ion の詳細については、GitHub にある [Amazon Ion のドキュメント](http://amzn.github.io/ion-docs/)を参照してください。QLDB で Ion を操作するコード例については、「[Amazon QLDB で Amazon Ion のデータ型を操作する](driver-working-with-ion.md)」を参照してください。