

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# AWS AppSync JavaScript Amazon RDS 的解析器函数参考
<a name="resolver-reference-rds-js"></a>

 AWS AppSync RDS 函数和解析器允许开发人员使用 RDS 数据 API 向 Amazon Aurora 集群数据库发送SQL查询，并获取这些查询的结果。您可以使用带有模块`sql`标签的模板或使用`rds`模块 AWS AppSync的`select`、`insert`、和`remove`帮助函数来编写发送到 Data API 的SQL语句。`rds` `update` AWS AppSync 利用 RDS 数据服务的[https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html)操作对数据库运行 SQL 语句。

**主题**
+ [带 SQL 标签的模板](#sql-tagged-templates)
+ [创建语句](#creating-statements)
+ [检索数据](#retrieving-data)
+ [实用程序函数](#utility-functions)
+ [SQL 选择](#utility-functions-select)
+ [SQL 插入](#utility-functions-insert)
+ [SQL 更新](#utility-functions-update)
+ [SQL 删除](#utility-functions-delete)
+ [转换](#casting)

## 带 SQL 标签的模板
<a name="sql-tagged-templates"></a>

AWS AppSync的`sql`标记模板使您能够使用模板表达式创建可在运行时接收动态值的静态语句。 AWS AppSync 根据表达式值构建变量映射以构造发送到 Amazon Aurora 无服务器数据 API 的[https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html)查询。使用此方法，运行时传递的动态值不可能修改原始语句，这可能会导致意外执行。所有动态值都作为参数传递，不能修改原始语句，也不会由数据库执行。这使您的查询不太容易受到 SQL 注入攻击。

**注意**  
在所有情况下，在编写 SQL 语句时，都应遵循安全准则，以正确处理作为输入收到的数据。

**注意**  
带 `sql` 标签的模板仅支持传递变量值。不能使用表达式来动态指定列名称或表名称。但是，您可以使用实用程序函数来构建动态语句。

在以下示例中，我们创建了一个查询，该查询根据运行时在 GraphQL 查询中动态设置的 `col` 参数值进行筛选。只能使用标签表达式将该值添加到语句中：

```
import { sql, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
  const query = sql`
SELECT * FROM table 
WHERE column = ${ctx.args.col}`
  ;
  return createMySQLStatement(query);
}
```

借助于通过变量映射传递所有动态值，我们依靠数据库引擎来安全地处理和清理值。

## 创建语句
<a name="creating-statements"></a>

函数和解析器可以与 MySQL 和 PostgreSQL 数据库进行交互。分别使用 `createMySQLStatement` 和 `createPgStatement` 来构建语句。例如，`createMySQLStatement` 可以创建 MySQL 查询。这些函数最多可接受两条语句，在请求应立即检索结果时很有用。使用 MySQL，您可以执行：

```
import { sql, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { id, text } = ctx.args;
    const s1 = sql`insert into Post(id, text) values(${id}, ${text})`;
    const s2 = sql`select * from Post where id = ${id}`;
    return createMySQLStatement(s1, s2);
}
```

**注意**  
`createPgStatement` 和 `createMySQLStatement` 不转义或引用使用带 `sql` 标签的模板构建的语句。

## 检索数据
<a name="retrieving-data"></a>

您执行的 SQL 语句的结果可在响应处理程序的 `context.result` 对象中提供。结果是一个 JSON 字符串，其中包含来自 `ExecuteStatement` 操作的[响应元素](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_ExecuteStatement.html#API_ExecuteStatement_ResponseElements)。解析后，结果具有以下形状：

```
type SQLStatementResults = {
    sqlStatementResults: {
        records: any[];
        columnMetadata: any[];
        numberOfRecordsUpdated: number;
        generatedFields?: any[]
    }[]
}
```

您可以使用 `toJsonObject` 实用程序将结果转换为表示返回行的 JSON 对象列表。例如：

```
import { toJsonObject } from '@aws-appsync/utils/rds';

export function response(ctx) {
    const { error, result } = ctx;
    if (error) {
        return util.appendError(
            error.message,
            error.type,
            result
        )
    }
    return toJsonObject(result)[1][0]
}
```

请注意，`toJsonObject` 返回语句结果数组。如果您提供了一条语句，则数组长度为 `1`。如果您提供了两条语句，则数组长度为 `2`。数组中的每个结果都包含 `0` 行或多行。如果结果值无效或不符合预期，则 `toJsonObject` 返回 `null`。

## 实用程序函数
<a name="utility-functions"></a>

您可以使用 AWS AppSync RDS 模块的实用工具帮助程序与您的数据库进行交互。

### SQL 选择
<a name="utility-functions-select"></a>

`select` 实用程序会创建一条 `SELECT` 语句来查询您的关系数据库。

**基本用法**

在其基本形式中，您可以指定要查询的表：

```
import { select, createPgStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {

    // Generates statement: 
    // "SELECT * FROM "persons"
    return createPgStatement(select({table: 'persons'}));
}
```

请注意，您也可以在表标识符中指定架构：

```
import { select, createPgStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {

    // Generates statement:
    // SELECT * FROM "private"."persons"
    return createPgStatement(select({table: 'private.persons'}));
}
```

**指定列**

您可以使用 `columns` 属性指定列。如果未将其设置为某个值，则它默认为 `*`：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name']
    }));
}
```

您也可以指定列的表：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "persons"."name"
    // FROM "persons"
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'persons.name']
    }));
}
```

**限制和偏移**

您可以将 `limit` 和 `offset` 应用于查询：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "name"
    // FROM "persons"
    // LIMIT :limit
    // OFFSET :offset
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        limit: 10,
        offset: 40
    }));
}
```

**排序依据**

您可以使用 `orderBy` 属性对结果进行排序。提供指定列和可选 `dir` 属性的对象数组：

```
export function request(ctx) {

    // Generates statement: 
    // SELECT "id", "name" FROM "persons"
    // ORDER BY "name", "id" DESC
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        orderBy: [{column: 'name'}, {column: 'id', dir: 'DESC'}]
    }));
}
```

**筛选条件**

您可以使用特殊条件对象来构建筛选条件：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: {name: {eq: 'Stephane'}}
    }));
}
```

您也可以组合筛选条件：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME and "id" > :ID
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: {name: {eq: 'Stephane'}, id: {gt: 10}}
    }));
}
```

您也可以创建 `OR` 语句：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE "name" = :NAME OR "id" > :ID
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: { or: [
            { name: { eq: 'Stephane'} },
            { id: { gt: 10 } }
        ]}
    }));
}
```

您也可以使用 `not` 来否定条件：

```
export function request(ctx) {

    // Generates statement:
    // SELECT "id", "name"
    // FROM "persons"
    // WHERE NOT ("name" = :NAME AND "id" > :ID)
    return createPgStatement(select({
        table: 'persons',
        columns: ['id', 'name'],
        where: { not: [
            { name: { eq: 'Stephane'} },
            { id: { gt: 10 } }
        ]}
    }));
}
```

您也可以使用以下运算符来比较值：


| 
| 
| 运算符 | 说明 | 可能的值类型 | 
| --- |--- |--- |
| eq | Equal | 数字、字符串、布尔值 | 
| ne | Not equal | 数字、字符串、布尔值 | 
| le | Less than or equal | 数字，字符串 | 
| lt | Less than | 数字，字符串 | 
| ge | Greater than or equal | 数字，字符串 | 
| gt | Greater than | 数字，字符串 | 
| contains | like | 字符串 | 
| 不包含 | 不像 | 字符串 | 
| 开始于 | 以前缀开头 | 字符串 | 
| 介于 | 在两个值之间 | 数字，字符串 | 
| 属性存在 | 该属性不为空 | 数字、字符串、布尔值 | 
| size | 检查元素的长度 | 字符串 | 

### SQL 插入
<a name="utility-functions-insert"></a>

`insert` 实用程序提供了一种通过 `INSERT` 操作在数据库中插入单行项目的简单方法。

**单个项目插入**

要插入项目，请指定表，然后传入您的值对象。对象键映射到您的表列。列名称会自动转义，并使用变量映射将值发送到数据库：

```
import { insert, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({ table: 'persons', values });
    
    // Generates statement:
    // INSERT INTO `persons`(`name`)
    // VALUES(:NAME)
    return createMySQLStatement(insertStatement)
}
```

**MySQL 用例**

您可以组合 `insert` 后跟 `select` 来检索您插入的行：

```
import { insert, select, createMySQLStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({  table: 'persons', values });
    const selectStatement = select({
        table: 'persons',
        columns: '*',
        where: { id: { eq: values.id } },
        limit: 1,
    });
    
    // Generates statement:
    // INSERT INTO `persons`(`name`)
    // VALUES(:NAME)
    // and
    // SELECT *
    // FROM `persons`
    // WHERE `id` = :ID
    return createMySQLStatement(insertStatement, selectStatement)
}
```

**Postgres 用例**

借助 Postgres，您可以使用 [https://www.postgresql.org/docs/current/dml-returning.html](https://www.postgresql.org/docs/current/dml-returning.html) 从插入的行中获取数据。它接受 `*` 或列名称数组：

```
import { insert, createPgStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: values } = ctx.args;
    const insertStatement = insert({
        table: 'persons',
        values,
        returning: '*'
    });

    // Generates statement:
    // INSERT INTO "persons"("name")
    // VALUES(:NAME)
    // RETURNING *
    return createPgStatement(insertStatement)
}
```

### SQL 更新
<a name="utility-functions-update"></a>

`update` 实用程序允许您更新现有行。您可以使用条件对象将更改应用于满足条件的所有行中的指定列。例如，假设我们有一个允许我们进行这种突变的架构。我们要将 `Person` 的 `name` 更新为 `id` 值 `3`，但仅限我们自 `2000` 年开始就已知道它们 (`known_since`)：

```
mutation Update {
    updatePerson(
        input: {id: 3, name: "Jon"},
        condition: {known_since: {ge: "2000"}}
    ) {
    id
    name
  }
}
```

更新解析器如下所示：

```
import { update, createPgStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: { id, ...values }, condition } = ctx.args;
    const where = {
        ...condition,
        id: { eq: id },
    };
    const updateStatement = update({
        table: 'persons',
        values,
        where,
        returning: ['id', 'name'],
    });

    // Generates statement:
    // UPDATE "persons"
    // SET "name" = :NAME, "birthday" = :BDAY, "country" = :COUNTRY
    // WHERE "id" = :ID
    // RETURNING "id", "name"
    return createPgStatement(updateStatement)
}
```

我们可以在条件中添加一项检查，以确保只更新主键 `id` 等于 `3` 的行。同样，对于 Postgres `inserts`，您可以使用 `returning` 返回修改后的数据。

### SQL 删除
<a name="utility-functions-delete"></a>

`remove` 实用程序允许您删除现有行。您可以在满足条件的所有行上使用条件对象。请注意，`delete`这是中的保留关键字 JavaScript。 `remove`应该改用：

```
import { remove, createPgStatement } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const { input: { id }, condition } = ctx.args;
    const where = { ...condition, id: { eq: id } };
    const deleteStatement = remove({
        table: 'persons',
        where,
        returning: ['id', 'name'],
    });

    // Generates statement:
    // DELETE "persons"
    // WHERE "id" = :ID
    // RETURNING "id", "name"
    return createPgStatement(updateStatement)
}
```

## 转换
<a name="casting"></a>

在某些情况下，您可能希望在语句中使用更具体的正确对象类型。您可以使用提供的类型提示来指定参数的类型。 AWS AppSync 支持与数据 API [相同的类型提示](https://docs.aws.amazon.com//rdsdataservice/latest/APIReference/API_SqlParameter.html#rdsdtataservice-Type-SqlParameter-typeHint)。您可以使用 AWS AppSync `rds`模块中的`typeHint`函数来转换参数。

以下示例允许您将数组作为强制转换为 JSON 对象的值发送。我们使用 `->` 运算符来检索 JSON 数组中 `index` 为 `2` 的元素：

```
import { sql, createPgStatement, toJsonObject, typeHint } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const arr = ctx.args.list_of_ids
    const statement = sql`select ${typeHint.JSON(arr)}->2 as value`
    return createPgStatement(statement)
}

export function response(ctx) {
    return toJsonObject(ctx.result)[0][0].value
}
```

在处理和比较 `DATE`、`TIME` 和 `TIMESTAMP` 时，强制转换也很有用：

```
import { select, createPgStatement, typeHint } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const when = ctx.args.when
    const statement = select({
        table: 'persons',
        where: { createdAt : { gt: typeHint.DATETIME(when) } }
    })
    return createPgStatement(statement)
}
```

下面是另一个示例，显示如何发送当前日期和时间：

```
import { sql, createPgStatement, typeHint } from '@aws-appsync/utils/rds';

export function request(ctx) {
    const now = util.time.nowFormatted('YYYY-MM-dd HH:mm:ss')
    return createPgStatement(sql`select ${typeHint.TIMESTAMP(now)}`)
}
```

**可用的类型提示**
+ `typeHint.DATE` – 相应的参数作为 `DATE` 类型的对象发送到数据库。接受的格式为 `YYYY-MM-DD`。
+ `typeHint.DECIMAL` – 相应的参数作为 `DECIMAL` 类型的对象发送到数据库。
+ `typeHint.JSON` – 相应的参数作为 `JSON` 类型的对象发送到数据库。
+ `typeHint.TIME` – 相应的字符串参数值作为 `TIME` 类型的对象发送到数据库。接受的格式为 `HH:MM:SS[.FFF]`。
+ `typeHint.TIMESTAMP` – 相应的字符串参数值作为 `TIMESTAMP` 类型的对象发送到数据库。接受的格式为 `YYYY-MM-DD HH:MM:SS[.FFF]`。
+ `typeHint.UUID` – 相应的字符串参数值作为 `UUID` 类型的对象发送到数据库。