

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# Node.js 的 Amazon QLDB 驅動程式 – 技術指南參考
<a name="driver-cookbook-nodejs"></a>

**重要**  
支援終止通知：現有客戶將可以使用 Amazon QLDB，直到 07/31/2025 的支援結束為止。如需詳細資訊，請參閱[將 Amazon QLDB Ledger 遷移至 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)
    + [在一個陳述式中插入多個文件](#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) 操作。

**警告**  
最佳實務是，讓您的寫入交易嚴格具有同冪性。

**使交易具有等冪性**

我們建議您將寫入交易設為等冪，以避免重試時發生任何非預期的副作用。如果交易可以多次執行，並且每次產生相同的結果，則表示交易是*等*冪的。

例如，請考慮將文件插入名為 的資料表的交易`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) 方法接受 lambda 函數，該函數會接收 [TransactionExecutor](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/classes/_src_transactionexecutor_.transactionexecutor.html) 執行個體，您可以使用它來執行陳述式。執行個體會`TransactionExecutor`包裝隱含建立的交易。

您可以使用交易執行器的執行方法，在 lambda 函數中[執行](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)
  + [在一個陳述式中插入多個文件](#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 衝突例外狀況或交易逾時。

#### 在一個陳述式中插入多個文件
<a name="cookbook-nodejs.crud.inserting.multiple"></a>

若要使用單一[INSERT](ql-reference.insert.md)陳述式插入多個文件，您可以將類型[清單](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`，預設退避策略為 [defaultBackoffFunction](https://amazon-qldb-docs.s3.amazonaws.com/drivers/nodejs/2.2.0/modules/_src_retry_defaultretryconfig_.html#defaultretryconfig)，以`10`毫秒為基礎。您可以使用 RetryConfig 的執行個體，設定每個驅動程式執行個體和每個交易的[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)。