

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

# 使用 DynamoDB 解析器创建简单的文章应用程序
<a name="tutorial-dynamodb-resolvers"></a>

**注意**  
我们现在主要支持 APPSYNC\$1JS 运行时系统及其文档。请考虑使用 APPSYNC\$1JS 运行时系统和[此处](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html)的指南。

本教程展示了如何将自己的亚马逊 DynamoDB 表带到 GraphQL API 并将其连接到 AWS AppSync GraphQL API。

您可以让代表您 AWS AppSync 配置 DynamoDB 资源。如果您愿意，也可以创建数据来源和解析器，将现有的表连接到 GraphQL 架构。在这两种情况下，您都可以通过 GraphQL 语句读写您的 DynamoDB 数据库，并订阅实时数据。

要将 GraphQL 语句转换为 DynamoDB 操作，并将响应转换回 GraphQL，需要完成一些特定的配置步骤。本教程通过一些现实世界的场景和数据访问模式介绍了配置过程。

## 设置您的 DynamoDB 表
<a name="setting-up-your-ddb-tables"></a>

要开始本教程，首先需要按照以下步骤配置 AWS 资源。

1. 在 CLI 中使用以下 AWS CloudFormation 模板配置 AWS 资源：

   ```
   aws cloudformation create-stack \
       --stack-name AWSAppSyncTutorialForAmazonDynamoDB \
       --template-url https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml \
       --capabilities CAPABILITY_NAMED_IAM
   ```

   或者，您可以在自己的 AWS 账户中在美国西部 2（俄勒冈）地区启动以下 CloudFormation 堆栈。

   [https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/dynamodb/AmazonDynamoDBCFTemplate.yaml)

   这会创建以下内容：
   + 名为 `AppSyncTutorial-Post` 的 DynamoDB 表，用于保留 `Post` 数据。
   + 允许 AWS AppSync 与`Post`表交互的 IAM 角色和关联的 IAM 托管策略。

1. 要了解堆栈和所创建资源的更多详细信息，请运行以下 CLI 命令：

   ```
   aws cloudformation describe-stacks --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

1. 稍后要删除资源，您可以运行以下操作：

   ```
   aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB
   ```

## 创建您的 GraphQL API
<a name="creating-your-graphql-api"></a>

要在以下位置创建 GraphQL API，请执行以下操作： AWS AppSync

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择**创建 API**。

1. 在**自定义您的 API 或从 Amazon DynamoDB 导入**下面，选择**从头开始构建**。

   1. 选择同一窗口右侧的**开始**。

1. 在 **API 名称**字段中，将 API 的名称设置为 `AWSAppSyncTutorial`。

1. 选择**创建**。

 AWS AppSync 控制台使用 API 密钥身份验证模式为您创建一个新的 GraphQL API。您可以根据本教程后面的说明，使用控制台设置 GraphQL API 的其余部分，并针对它运行查询。

## 定义基本文章 API
<a name="defining-a-basic-post-api"></a>

现在，您已经创建了 AWS AppSync GraphQL API，可以设置一个允许基本创建、检索和删除帖子数据的基本架构。

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择您刚刚创建的 API。

1. 在**侧边栏**中，选择**架构**。

   1. 在**架构**窗格中，将内容替换为以下代码：

     ```
     schema {
         query: Query
         mutation: Mutation
     }
     
     type Query {
         getPost(id: ID): Post
     }
     
     type Mutation {
         addPost(
             id: ID!
             author: String!
             title: String!
             content: String!
             url: String!
         ): Post!
     }
     
     type Post {
         id: ID!
         author: String
         title: String
         content: String
         url: String
         ups: Int!
         downs: Int!
         version: Int!
     }
     ```

1. 选择**保存**。

此架构定义 `Post` 类型，执行操作以添加并获取 `Post` 对象。

## 为 DynamoDB 表配置数据来源
<a name="configuring-the-data-source-for-the-ddb-tables"></a>

接下来，将架构中定义的查询和变更链接到 `AppSyncTutorial-Post` DynamoDB 表。

首先， AWS AppSync 需要注意你的表格。要实现此目的，需要在 AWS AppSync 中设置数据源：

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**数据来源**。

1. 选择**创建数据来源**。

   1. 对于**数据来源名称**，输入 `PostDynamoDBTable`。

   1. 对于**数据来源类型**，选择 **Amazon DynamoDB 表**。

   1. 对于**区域**，选择 **US-WEST-2**。

   1. 对于**表名**，选择 **AppSyncTutorial-Pos** t DynamoDB 表。

   1. 创建新的 IAM 角色（建议），或者选择具有 `lambda:invokeFunction` IAM 权限的现有角色。现有角色需要具有一个信任策略，如[附加数据来源](attaching-a-data-source.md)一节中所述。

      以下是一个示例 IAM 策略，该策略具有对资源执行操作所需的权限：

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction", 
                       "arn:aws:lambda:us-east-1:111122223333:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. 选择**创建**。

## 设置 AddPost 解析器 (DynamoDB) PutItem
<a name="setting-up-the-addpost-resolver-dynamodb-putitem"></a>

**在知道 DynamoDB 表之后 AWS AppSync ，您可以通过定义解析器将其链接到各个查询和变更。**您创建的第一个解析器是 `addPost` 解析器，可用于在 `AppSyncTutorial-Post` DynamoDB 表中创建文章。

解析器具有以下组件：
+ GraphQL 架构中的位置，用于附加解析器。在本例中，您将设置 `addPost` 类型的 `Mutation` 字段的解析器。在调用方调用 `mutation { addPost(...){...} }` 时，将调用该解析器。
+ 此解析器所用的数据来源。在本例中，您要使用之前定义的 `PostDynamoDBTable` 数据来源，这样您就可以在 `AppSyncTutorial-Post` DynamoDB 表中添加条目。
+ 请求映射模板。请求映射模板的目的是接收来自调用者的传入请求，并将其转换为要对 DynamoDB 执行的 AWS AppSync 指令。
+ 响应映射模板。响应映射模板的任务是将 DynamoDB 的响应转换回 GraphQL 期待获得的内容。如果 DynamoDB 中的数据形态与 GraphQL 中的 `Post` 类型不同，此模板很有用。但在此例中它们的形态相同，所以只用于传递数据。

设置解析器：

1. 登录 AWS 管理控制台 并打开[AppSync 控制台](https://console.aws.amazon.com/appsync/)。

   1. 在**APIs 控制面板**中，选择你的 GraphQL API。

   1. 在**侧边栏**中，选择**数据来源**。

1. 选择**创建数据来源**。

   1. 对于**数据来源名称**，输入 `PostDynamoDBTable`。

   1. 对于**数据来源类型**，选择 **Amazon DynamoDB 表**。

   1. 对于**区域**，选择 **US-WEST-2**。

   1. 对于**表名**，选择 **AppSyncTutorial-Pos** t DynamoDB 表。

   1. 创建新的 IAM 角色（建议），或者选择具有 `lambda:invokeFunction` IAM 权限的现有角色。现有角色需要具有一个信任策略，如[附加数据来源](attaching-a-data-source.md)一节中所述。

      以下是一个示例 IAM 策略，该策略具有对资源执行操作所需的权限：

------
#### [ JSON ]

****  

      ```
      { 
           "Version":"2012-10-17",		 	 	  
           "Statement": [ 
               { 
                   "Effect": "Allow", 
                   "Action": [ "lambda:invokeFunction" ], 
                   "Resource": [ 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction", 
                       "arn:aws:lambda:us-west-2:123456789012:function:myFunction:*" 
                   ] 
               } 
           ] 
       }
      ```

------

1. 选择**创建**。

1. 选择**架构**选项卡。

1. 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的 **addPost** 字段，然后选择**附加**。

1. 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。

1. 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。

1. 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

   ```
   {
       "version" : "2017-02-28",
       "operation" : "PutItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
       "attributeValues" : {
           "author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
           "title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
           "content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
           "url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
           "ups" : { "N" : 1 },
           "downs" : { "N" : 0 },
           "version" : { "N" : 1 }
       }
   }
   ```

   **注意**：为所有键和属性值指定了*类型*。例如，您将 `author` 字段设置为 `{ "S" : "${context.arguments.author}" }`。该`S`部分向 DynamoDB 表示该值将是一个字符串值。 AWS AppSync 实际的值由 `author` 参数填充。与此类似，`version` 字段是一个数字字段，因为它使用 `N` 作为类型。最后，您还将初始化 `ups`、`downs` 和 `version` 字段。

   在本教程中，您已指定 GraphQL `ID!` 类型（用于索引插入到 DynamoDB 的新项目）作为客户端参数的一部分。 AWS AppSync 附带了一个名为的自动生成身份的实用程序`$utils.autoId()`，您也可以以以下形式使用该实用程序`"id" : { "S" : "${$utils.autoId()}" }`。然后，就可以在 `id: ID!` 的架构定义中省去 `addPost()`，因为它将自动插入。您不会在本教程中使用该技术，但在写入到 DynamoDB 表时，您应该将其视为一种很好的做法。

   有关映射模板的更多信息，请参阅 [解析器映射模板概述](resolver-mapping-template-reference-overview.md#aws-appsync-resolver-mapping-template-reference-overview)参考文档。有关 GetItem 请求映射的更多信息，请参阅[GetItem](aws-appsync-resolver-mapping-template-reference-dynamodb-getitem.md)参考文档。有关类型的更多信息，请参阅[类型系统（请求映射）](aws-appsync-resolver-mapping-template-reference-dynamodb-typed-values-request.md)参考文档。

1. 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

   ```
   $utils.toJson($context.result)
   ```

    **注意**：由于 `AppSyncTutorial-Post` 表中的数据形态与 GraphQL 中 `Post` 类型的形态完全匹配，响应映射模板只会直接传递结果。还请注意，此教程中的所有示例均使用同一响应映射模板，所以您只需创建一个文件。

1. 选择**保存**。

### 调用 API 以添加文章
<a name="call-the-api-to-add-a-post"></a>

现在，解析器已设置完毕， AWS AppSync 可以将传入的`addPost`突变转换为 DynamoDB 操作。 PutItem 现在，您可以运行一个变更，在表中添加内容。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下变更：

  ```
  mutation addPost {
    addPost(
      id: 123
      author: "AUTHORNAME"
      title: "Our first post!"
      content: "This is our first post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 新创建的文章的结果应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

以下是具体过程：
+ AWS AppSync 收到了`addPost`变异请求。
+ AWS AppSync 接受了请求和请求映射模板，并生成了请求映射文档。如下所示：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "PutItem",
      "key" : {
          "id" : { "S" : "123" }
      },
      "attributeValues" : {
          "author": { "S" : "AUTHORNAME" },
          "title": { "S" : "Our first post!" },
          "content": { "S" : "This is our first post." },
          "url": { "S" : "https://aws.amazon.com/appsync/" },
          "ups" : { "N" : 1 },
          "downs" : { "N" : 0 },
          "version" : { "N" : 1 }
      }
  }
  ```
+ AWS AppSync 使用请求映射文档生成并执行 DynamoDB `PutItem` 请求。
+ AWS AppSync 获取`PutItem`请求的结果并将其转换回 GraphQL 类型。

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ 通过响应映射文档进行传递，没有变化。
+ 在 GraphQL 响应中返回新创建的对象。

## 设置 GetPost 解析器 (DynamoDB) GetItem
<a name="setting-up-the-getpost-resolver-ddb-getitem"></a>

您现在能够将数据添加到 `AppSyncTutorial-Post` DynamoDB 表中，您需要设置 `getPost` 查询，以使其可以从 `AppSyncTutorial-Post` 表中检索该数据。为了实现此目的，您要设置另一解析器。
+ 选择**架构**选项卡。
+ 在右侧的**数据类型**窗格中，找到 **Query** 类型上的 **getPost** 字段，然后选择**附加**。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
      }
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。

### 调用 API 以获取文章
<a name="call-the-api-to-get-a-post"></a>

现在，解析器已经设置完毕， AWS AppSync 知道如何将传入的`getPost`查询转换为 DynamoDB `GetItem` 操作。现在，您可以运行查询，检索之前创建的文章。
+ 选择 **Queries** 选项卡。
+ 在**Queries (查询)** 窗格中粘贴以下内容：

  ```
  query getPost {
    getPost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 从 DynamoDB 中检索的文章应显示在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "getPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our first post!",
        "content": "This is our first post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

以下是具体过程：
+ AWS AppSync 收到了`getPost`查询请求。
+ AWS AppSync 接受了请求和请求映射模板，并生成了请求映射文档。如下所示：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "GetItem",
      "key" : {
          "id" : { "S" : "123" }
      }
  }
  ```
+ AWS AppSync 使用请求映射文档生成并执行 DynamoDB GetItem 请求。
+ AWS AppSync 获取`GetItem`请求的结果并将其转换回 GraphQL 类型。

  ```
  {
      "id" : "123",
      "author": "AUTHORNAME",
      "title": "Our first post!",
      "content": "This is our first post.",
      "url": "https://aws.amazon.com/appsync/",
      "ups" : 1,
      "downs" : 0,
      "version" : 1
  }
  ```
+ 通过响应映射文档进行传递，没有变化。
+ 在响应中返回检索到的对象。

或者，采用以下示例：

```
query getPost {
  getPost(id:123) {
    id
    author
    title
  }
}
```

如果您的 `getPost` 查询仅需要 `id`、`author` 和 `title`，您可以将请求映射模板更改为使用投影表达式仅指定您希望从 DynamoDB 表中获取的属性，以避免将不必要的数据从 DynamoDB 传输到 AWS AppSync。例如，请求映射模板可能类似于以下代码片段：

```
{
    "version" : "2017-02-28",
    "operation" : "GetItem",
    "key" : {
        "id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
    },
    "projection" : {
     "expression" : "#author, id, title",
     "expressionNames" : { "#author" : "author"}
    }
}
```

## 创建 UpdatePost 突变 (DynamoDB) UpdateItem
<a name="create-an-updatepost-mutation-ddb-updateitem"></a>

到目前为止，您可以在 DynamoDB 中创建和检索 `Post` 对象。现在，您要设置一项新的变更，以便更新对象。您将使用 UpdateItem DynamoDB 操作来执行此操作。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Mutation` 类型，添加新的 `updatePost` 变更，如下所示：

  ```
  type Mutation {
      updatePost(
          id: ID!,
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post
      addPost(
          author: String!
          title: String!
          content: String!
          url: String!
      ): Post!
  }
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **updatePost** 字段，然后选择**附加**。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "SET author = :author, title = :title, content = :content, #url = :url ADD version :one",
          "expressionNames": {
              "#url" : "url"
          },
          "expressionValues": {
              ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author),
              ":title" : $util.dynamodb.toDynamoDBJson($context.arguments.title),
              ":content" : $util.dynamodb.toDynamoDBJson($context.arguments.content),
              ":url" : $util.dynamodb.toDynamoDBJson($context.arguments.url),
              ":one" : { "N": 1 }
          }
      }
  }
  ```

   **注意：**此解析器使用的是 D UpdateItem ynamoDB，这与操作有很大不同。 PutItem 您仅要求 DynamoDB 更新某些属性，而不是编写整个项目。这是使用 DynamoDB 更新表达式完成的。表达式本身是在 `expression` 部分的 `update` 字段中指定的。它会设置 `author`、`title`、`content` 和 URL 属性，还会递增 `version` 字段。要使用的值不会出现在表达式本身；表达式中的占位符名称以冒号打头，并在 `expressionValues` 字段中进行定义。最后，DynamoDB 具有一些保留字，它们不能出现在 `expression` 中。例如，`url` 是保留关键字，所以要更新 `url` 字段，您可使用名称占位符，并在 `expressionNames` 字段中定义它们。

  有关`UpdateItem`请求映射的更多信息，请参阅[UpdateItem](aws-appsync-resolver-mapping-template-reference-dynamodb-updateitem.md)参考文档。有关如何编写更新表达式的更多信息，请参阅 [DynamoDB 文档 UpdateExpressions ](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)。
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```

### 调用 API 以更新文章
<a name="call-the-api-to-update-a-post"></a>

现在解析器已经设置好了， AWS AppSync 知道如何将传入的`update`突变转换为 DynamoDB 操作。`Update`现在，您可以运行变更，以更新您之前写入的项目。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下变更。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation updatePost {
    updatePost(
      id:"123"
      author: "A new author"
      title: "An updated author!"
      content: "Now with updated content!"
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 在 DynamoDB 中更新的文章应显示在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An updated author!",
        "content": "Now with updated content!",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 2
      }
    }
  }
  ```

在此示例中，没有修改`ups`和`downs`字段，因为请求映射模板没有要求 AWS AppSync 和 DynamoDB 对这些字段执行任何操作。此外，该`version`字段增加了 1，因为您要求 AWS AppSync 和 DynamoDB 向该字段添加 1。`version`

## 修改 UpdatePost 解析器 (DynamoDB) UpdateItem
<a name="modifying-the-updatepost-resolver-dynamodb-updateitem"></a>

`updatePost` 变更看上去不错，但它有两个主要问题：
+ 如果您只希望更新一个字段，则必须更新所有字段。
+ 如果两个人同时修改对象，您可能会丢失信息。

为了解决这些问题，您要修改 `updatePost` 变更，做到只修改请求中指定的参数，然后在 `UpdateItem` 操作中添加条件。

1. 选择**架构**选项卡。

1. 在**架构**窗格中修改 `Mutation` 类型中的 `updatePost` 字段，删除 `author`、`title`、`content` 和 `url` 参数的感叹号，确保 `id` 字段不变。这样它们就会成为可选参数。还要新增一个必需 `expectedVersion` 参数。

   ```
   type Mutation {
       updatePost(
           id: ID!,
           author: String,
           title: String,
           content: String,
           url: String,
           expectedVersion: Int!
       ): Post
       addPost(
           author: String!
           title: String!
           content: String!
           url: String!
       ): Post!
   }
   ```

1. 选择**保存**。

1. 在右侧的**数据类型**窗格中，找到 **Mutation** 类型的 **updatePost** 字段。

1. 选择**PostDynamoDBTable**打开现有的解析器。

1. 在 **Configure the request mapping template (配置请求映射模板)** 中修改请求映射模板，如下所示：

   ```
   {
       "version" : "2017-02-28",
       "operation" : "UpdateItem",
       "key" : {
           "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
       },
   
       ## Set up some space to keep track of things you're updating **
       #set( $expNames  = {} )
       #set( $expValues = {} )
       #set( $expSet = {} )
       #set( $expAdd = {} )
       #set( $expRemove = [] )
   
       ## Increment "version" by 1 **
       $!{expAdd.put("version", ":one")}
       $!{expValues.put(":one", { "N" : 1 })}
   
       ## Iterate through each argument, skipping "id" and "expectedVersion" **
       #foreach( $entry in $context.arguments.entrySet() )
           #if( $entry.key != "id" && $entry.key != "expectedVersion" )
               #if( (!$entry.value) && ("$!{entry.value}" == "") )
                   ## If the argument is set to "null", then remove that attribute from the item in DynamoDB **
   
                   #set( $discard = ${expRemove.add("#${entry.key}")} )
                   $!{expNames.put("#${entry.key}", "$entry.key")}
               #else
                   ## Otherwise set (or update) the attribute on the item in DynamoDB **
   
                   $!{expSet.put("#${entry.key}", ":${entry.key}")}
                   $!{expNames.put("#${entry.key}", "$entry.key")}
                   $!{expValues.put(":${entry.key}", { "S" : "${entry.value}" })}
               #end
           #end
       #end
   
       ## Start building the update expression, starting with attributes you're going to SET **
       #set( $expression = "" )
       #if( !${expSet.isEmpty()} )
           #set( $expression = "SET" )
           #foreach( $entry in $expSet.entrySet() )
               #set( $expression = "${expression} ${entry.key} = ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to ADD **
       #if( !${expAdd.isEmpty()} )
           #set( $expression = "${expression} ADD" )
           #foreach( $entry in $expAdd.entrySet() )
               #set( $expression = "${expression} ${entry.key} ${entry.value}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Continue building the update expression, adding attributes you're going to REMOVE **
       #if( !${expRemove.isEmpty()} )
           #set( $expression = "${expression} REMOVE" )
   
           #foreach( $entry in $expRemove )
               #set( $expression = "${expression} ${entry}" )
               #if ( $foreach.hasNext )
                   #set( $expression = "${expression}," )
               #end
           #end
       #end
   
       ## Finally, write the update expression into the document, along with any expressionNames and expressionValues **
       "update" : {
           "expression" : "${expression}"
           #if( !${expNames.isEmpty()} )
               ,"expressionNames" : $utils.toJson($expNames)
           #end
           #if( !${expValues.isEmpty()} )
               ,"expressionValues" : $utils.toJson($expValues)
           #end
       },
   
       "condition" : {
           "expression"       : "version = :expectedVersion",
           "expressionValues" : {
               ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
           }
       }
   }
   ```

1. 选择**保存**。

此模板是一个更复杂的示例。它演示了映射模板的强大功能和灵活性。它遍历所有参数，跳过 `id` 和 `expectedVersion`。如果参数设置为某个值，它会要求 AWS AppSync 和 DynamoDB 在 DynamoDB 中更新该对象上的该属性。如果该属性设置为空，它会要求 AWS AppSync 和 DynamoDB 从帖子对象中移除该属性。如果未指定参数，该属性会保留原样。它还会递增 `version` 字段。

还有一个新的 `condition` 部分。条件表达式允许您根据执行 AWS AppSync 操作之前已在 DynamoDB 中的对象的状态告知和 DynamoDB 请求是否应该成功。在该示例中，只有在当前位于 DynamoDB 中的项目的 `version` 字段与 `expectedVersion` 参数完全匹配时，您才希望 `UpdateItem` 请求成功。

有关条件表达式的更多信息，请参阅[条件表达式](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md)参考文档。

### 调用 API 以更新文章
<a name="id1"></a>

让我们尝试使用新的解析器更新 `Post` 对象：
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation updatePost {
    updatePost(
      id:123
      title: "An empty story"
      content: null
      expectedVersion: 2
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 在 DynamoDB 中更新的文章应显示在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "updatePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      }
    }
  }
  ```

在此请求中，您要求 AWS AppSync 和 DynamoDB 仅更新和字段`title`。`content`它不会处理所有其他字段（除了递增 `version` 字段）。您将 `title` 属性设置为新的值，并从文章中删除 `content` 属性。`author`、`url`、`ups` 和 `downs` 字段没有变化。

请尝试再次执行变更请求，保持请求完全不变。您可以看到类似以下内容的响应：

```
{
  "data": {
    "updatePost": null
  },
  "errors": [
    {
      "path": [
        "updatePost"
      ],
      "data": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 3
      },
      "errorType": "DynamoDB:ConditionalCheckFailedException",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
    }
  ]
}
```

请求失败，因为条件表达式的评估结果为 false：
+ 第一次运行请求时，DynamoDB 中的文章的 `version` 字段的值为 `2`，它与 `expectedVersion` 参数匹配。请求成功，这意味着 DynamoDB 中的 `version` 字段已增加到 `3`。
+ 第二次运行请求时，DynamoDB 中的文章的 `version` 字段的值为 `3`，它与 `expectedVersion` 参数不匹配。

这种模式通常被称为*乐观锁*。

 AWS AppSync DynamoDB 解析器的一个特点是它返回 DynamoDB 中帖子对象的当前值。您可以在 GraphQL 响应的 `data` 部分的 `errors` 字段中找到这个值。您的应用程序可以利用此信息决定应如何继续。在该示例中，您可以看到 DynamoDB 中的对象的 `version` 字段设置为 `3`，因此，您只需将 `expectedVersion` 参数更新为 `3`，请求就会再次成功。

有关如何处理条件检查失败的更多信息，请参阅[条件表达式](aws-appsync-resolver-mapping-template-reference-dynamodb-condition-expressions.md)映射模板参考文档。

## 创建 upvotePost 和 downvotePost 突变 (DynamoDB) UpdateItem
<a name="create-upvotepost-and-downvotepost-mutations-ddb-updateitem"></a>

`Post` 类型有 `ups` 和 `downs` 字段，用于记录点赞和差评，但现在还无法通过 API 使用它们。让我们添加一些变更，对文章点赞和差评。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Mutation` 类型，添加新的 `upvotePost` 和 `downvotePost` 变更，如下所示：

  ```
  type Mutation {
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **upvotePost** 字段，然后选择**附加**。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD ups :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 `downvotePost` 字段，然后选择**附加**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD downs :plusOne, version :plusOne",
          "expressionValues" : {
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。

### 调用 API，为文章点赞或差评
<a name="call-the-api-to-upvote-and-downvote-a-post"></a>

现在，新的解析器已经设置完毕， AWS AppSync 知道如何将传入`upvotePost`或`downvote`突变转换为 DynamoDB 操作。 UpdateItem 现在您可以运行变更，为之前创建的文章点赞或差评。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation votePost {
    upvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 将在 DynamoDB 中更新文章，并且应显示在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "upvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 0,
        "version": 4
      }
    }
  }
  ```
+ 再选择几次 ** (执行查询)** 按钮。您应看到，每次您执行查询时，`ups` 和 `version` 字段均会递增 1。
+ 更改查询以调用 `downvotePost` 变更，如下所示：

  ```
  mutation votePost {
    downvotePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。这次您应看到，每次您执行查询时，`downs` 和 `version` 字段均会递增 1。

  ```
  {
    "data": {
      "downvotePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

## 设置 DeletePost 解析器 (DynamoDB) DeleteItem
<a name="setting-up-the-deletepost-resolver-ddb-deletepost"></a>

接下来您要设置的变更是删除一个文章。您将使用 `DeleteItem` DynamoDB 操作完成该操作。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Mutation` 类型，添加新的 `deletePost` 变更，如下所示：

  ```
  type Mutation {
      deletePost(id: ID!, expectedVersion: Int): Post
      upvotePost(id: ID!): Post
      downvotePost(id: ID!): Post
      updatePost(
          id: ID!,
          author: String,
          title: String,
          content: String,
          url: String,
          expectedVersion: Int!
      ): Post
      addPost(
          author: String!,
          title: String!,
          content: String!,
          url: String!
      ): Post!
  }
  ```

  这次您将 `expectedVersion` 字段设为可选，稍后在添加请求映射模板时将对此进行说明。
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **delete** 字段，然后选择**附加**。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "DeleteItem",
      "key": {
          "id": $util.dynamodb.toDynamoDBJson($context.arguments.id)
      }
      #if( $context.arguments.containsKey("expectedVersion") )
          ,"condition" : {
              "expression"       : "attribute_not_exists(id) OR version = :expectedVersion",
              "expressionValues" : {
                  ":expectedVersion" : $util.dynamodb.toDynamoDBJson($context.arguments.expectedVersion)
              }
          }
      #end
  }
  ```

   **注意**：`expectedVersion` 参数是可选的。如果调用方在请求中设置 `expectedVersion` 参数，模板将添加一个条件，只有在已删除项目或 DynamoDB 中的文章的 `version` 属性与 `expectedVersion` 完全匹配时，才允许 `DeleteItem` 请求成功。如果未设置此参数，则 `DeleteItem` 请求中不指定条件表达式。无论 `version` 值如何，或者项目在 DynamoDB 中是否存在，该请求都会成功。
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```

   **注意**：即使您要删除一个项目，如果该项目不是已经删除，还是可以返回要删除的项目。
+ 选择**保存**。

有关`DeleteItem`请求映射的更多信息，请参阅[DeleteItem](aws-appsync-resolver-mapping-template-reference-dynamodb-deleteitem.md)参考文档。

### 调用 API 以删除文章
<a name="call-the-api-to-delete-a-post"></a>

现在解析器已经设置好了， AWS AppSync 知道如何将传入的`delete`突变转换为 DynamoDB 操作。`DeleteItem`现在，您可以运行变更，从表中删除一些内容。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation deletePost {
    deletePost(id:123) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 该文章已从 DynamoDB 中删除。请注意， AWS AppSync 返回的是从 DynamoDB 中删除的项目的值，该值应显示在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "A new author",
        "title": "An empty story",
        "content": null,
        "url": "https://aws.amazon.com/appsync/",
        "ups": 6,
        "downs": 4,
        "version": 12
      }
    }
  }
  ```

只有调用的 `deletePost` 将项目从 DynamoDB 中实际删除，才会返回值。
+ 再次选择 **Execute query (执行查询)**。
+ 调用仍然成功，但没有返回任何值。

  ```
  {
    "data": {
      "deletePost": null
    }
  }
  ```

现在，让我们尝试删除一篇文章，但这次指定 `expectedValue`。但首先您需要创建一个新文章，因为您刚刚删除了一直在使用的文章。
+ 在 **Queries (查询)** 窗格中，粘贴以下变更：

  ```
  mutation addPost {
    addPost(
      id:123
      author: "AUTHORNAME"
      title: "Our second post!"
      content: "A new post."
      url: "https://aws.amazon.com/appsync/"
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 新创建的文章的结果应出现在查询窗格右侧的结果窗格中。记下新建对象的 `id`，因为一会您将用到它。如下所示：

  ```
  {
    "data": {
      "addPost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```

现在让我们尝试删除这个文章，但放入 `expectedVersion` 的错误值：
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 9999
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。

  ```
  {
    "data": {
      "deletePost": null
    },
    "errors": [
      {
        "path": [
          "deletePost"
        ],
        "data": {
          "id": "123",
          "author": "AUTHORNAME",
          "title": "Our second post!",
          "content": "A new post.",
          "url": "https://aws.amazon.com/appsync/",
          "ups": 1,
          "downs": 0,
          "version": 1
        },
        "errorType": "DynamoDB:ConditionalCheckFailedException",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "message": "The conditional request failed (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ConditionalCheckFailedException; Request ID: ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ)"
      }
    ]
  }
  ```

  请求失败，因为条件表达式的评估结果为 false：DynamoDB 中的文章的 `version` 值与参数中指定的 `expectedValue` 不匹配。对象的当前值返回到 GraphQL 响应的 `data` 部分的 `errors` 字段中。
+ 重试请求，但更正 `expectedVersion`：

  ```
  mutation deletePost {
    deletePost(
      id:123
      expectedVersion: 1
    ) {
      id
      author
      title
      content
      url
      ups
      downs
      version
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 这次请求成功，并返回从 DynamoDB 中删除的值：

  ```
  {
    "data": {
      "deletePost": {
        "id": "123",
        "author": "AUTHORNAME",
        "title": "Our second post!",
        "content": "A new post.",
        "url": "https://aws.amazon.com/appsync/",
        "ups": 1,
        "downs": 0,
        "version": 1
      }
    }
  }
  ```
+ 再次选择 **Execute query (执行查询)**。
+ 调用仍然成功，但这次没有返回任何值，因为已在 DynamoDB 中删除该文章。

```
{
  "data": {
    "deletePost": null
  }
}
```

## 设置 allPost 解析器 (DynamoDB Scan)
<a name="setting-up-the-allpost-resolver-dynamodb-scan"></a>

到目前为止，只有在您知道要查看的每篇文章的 `id` 时，才能使用该 API。让我们添加新的解析器，它可以返回表中的所有文章。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Query` 类型，添加新的 `allPost` 查询，如下所示：

  ```
  type Query {
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```
+ 添加新 `PaginationPosts` 类型：

  ```
  type PaginatedPosts {
      posts: [Post!]!
      nextToken: String
  }
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Query** 类型上的新创建的 **allPost** 字段，然后选择**附加**。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan"
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```

  此解析器有两个可选参数：`count` 指定单次调用可返回的项目数量上限；`nextToken` 可用于检索下一组结果（稍后您将展示 `nextToken` 的值来自何处）。
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **注意**：此响应映射模板与目前我们所用到的其他模板均不同。`allPost` 查询的结果是 `PaginatedPosts`，其中包含一组文章和一个分页标记。此对象的形状与 D AWS AppSync ynamoDB 解析器返回的形状不同：帖子列表在 DynamoDB 解析器结果`items`中被调用，但会被调用。 AWS AppSync `posts` `PaginatedPosts`
+ 选择**保存**。

有关 `Scan` 请求映射的更多信息，请参阅 [Scan](aws-appsync-resolver-mapping-template-reference-dynamodb-scan.md) 参考文档。

### 调用 API 以扫描所有文章
<a name="call-the-api-to-scan-all-posts"></a>

现在，解析器已经设置完毕， AWS AppSync 知道如何将传入的`allPost`查询转换为 DynamoDB `Scan` 操作。现在您可以扫描整个表，检索所有文章。

在进行尝试之前，您需要在表中填充一些数据，因为您已经删除了之前使用的所有内容。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下变更：

  ```
  mutation addPost {
    post1: addPost(id:1 author: "AUTHORNAME" title: "A series of posts, Volume 1" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post2: addPost(id:2 author: "AUTHORNAME" title: "A series of posts, Volume 2" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post3: addPost(id:3 author: "AUTHORNAME" title: "A series of posts, Volume 3" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post4: addPost(id:4 author: "AUTHORNAME" title: "A series of posts, Volume 4" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post5: addPost(id:5 author: "AUTHORNAME" title: "A series of posts, Volume 5" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post6: addPost(id:6 author: "AUTHORNAME" title: "A series of posts, Volume 6" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post7: addPost(id:7 author: "AUTHORNAME" title: "A series of posts, Volume 7" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post8: addPost(id:8 author: "AUTHORNAME" title: "A series of posts, Volume 8" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
    post9: addPost(id:9 author: "AUTHORNAME" title: "A series of posts, Volume 9" content: "Some content" url: "https://aws.amazon.com/appsync/" ) { title }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。

现在，让我们扫描表，每次返回 5 个结果。
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  query allPost {
    allPost(count: 5) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 最前面的 5 个文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          },
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRkJEdXdUK09hcnovRGhNTGxLTGdMUEFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF6ajFodkhKU1paT1pncTRaUUNBUkNBZ2dHWnJiR1dQWGxkMDB1N0xEdGY4Z2JsbktzRjRua1VCcks3TFJLcjZBTFRMeGFwVGJZMDRqOTdKVFQyYVRwSzdzbVdtNlhWWFVCTnFIOThZTzBWZHVkdDI2RlkxMHRqMDJ2QTlyNWJTUWpTbWh6NE5UclhUMG9KZWJSQ2JJbXBlaDRSVlg0Tis0WTVCN1IwNmJQWWQzOVhsbTlUTjBkZkFYMVErVCthaXZoNE5jMk50RitxVmU3SlJ5WmpzMEFkSGduM3FWd2VrOW5oeFVVd3JlK1loUks5QkRzemdiMDlmZmFPVXpzaFZ4cVJRbC93RURlOTcrRmVJdXZNby9NZ1F6dUdNbFRyalpNR3FuYzZBRnhwa0VlZTFtR0FwVDFISElUZlluakptYklmMGUzUmcxbVlnVHVSbDh4S0trNmR0QVoraEhLVDhuNUI3VnF4bHRtSnlNUXBrZGl6KzkyL3VzNDl4OWhrMnVxSW01ZFFwMjRLNnF0dm9ZK1BpdERuQTc5djhzb0grVytYT3VuQ2NVVDY4TVZ1Wk5KYkRuSEFSSEVlaTlVNVBTelU5RGZ6d2pPdmhqWDNJMWhwdWUrWi83MDVHVjlPQUxSTGlwZWZPeTFOZFhwZTdHRDZnQW00bUJUK2c1eC9Ec3ZDbWVnSDFDVXRTdHVuU1ZFa2JpZytQRC9oMUwyRTNqSHhVQldaa28yU256WUc0cG0vV1RSWkFVZHZuQT09In0="
      }
    }
  }
  ```

您可以看到 5 个结果，还有一个 `nextToken`，可用于获得下一组结果。
+ 更新 `allPost` 查询，加入上一组结果的 `nextToken`：

  ```
  query allPost {
    allPost(
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnRlluNktJRWl6V0ZlR3hJOVJkaStrZUFBQUI1akNDQWVJR0NTcUdTSWIzRFFFSEJxQ0NBZE13Z2dIUEFnRUFNSUlCeUFZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5cW8yUGFSZThnalFpemRCTUNBUkNBZ2dHWk1JODhUNzhIOFVUZGtpdFM2ZFluSWRyVDg4c2lkN1RjZzB2d1k3VGJTTWpSQ2U3WjY3TkUvU2I1dWNETUdDMmdmMHErSGJSL0pteGRzYzVEYnE1K3BmWEtBdU5jSENJdWNIUkJ0UHBPWVdWdCtsS2U5L1pNcWdocXhrem1RaXI1YnIvQkt6dU5hZmJCdE93NmtoM2Jna1BKM0RjWWhpMFBGbmhMVGg4TUVGSjBCcXg3RTlHR1V5N0tUS0JLZlV3RjFQZ0JRREdrNzFYQnFMK2R1S2IrVGtZZzVYMjFrc3NyQmFVTmNXZmhTeXE0ZUJHSWhqZWQ5c3VKWjBSSTc2ZnVQdlZkR3FLNENjQmxHYXhpekZnK2pKK1FneEU1SXduRTNYYU5TR0I4QUpmamR2bU1wbUk1SEdvWjlMUUswclczbG14RDRtMlBsaTNLaEVlcm9pem5zcmdINFpvcXIrN2ltRDN3QkJNd3BLbGQzNjV5Nnc4ZnMrK2FnbTFVOUlKOFFrOGd2bEgySHFROHZrZXBrMWlLdWRIQ25LaS9USnBlMk9JeEVPazVnRFlzRTRUU09HUlVJTkxYY2MvdW1WVEpBMUthV2hWTlAvdjNlSnlZQUszbWV6N2h5WHVXZ1BkTVBNWERQdTdjVnVRa3EwK3NhbGZOd2wvSUx4bHNyNDVwTEhuVFpyRWZvVlV1bXZ5S2VKY1RUU1lET05hM1NwWEd2UT09In0="
    ) {
      posts {
        id
        author
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 其余的 4 个文章应出现在查询窗格右侧的结果窗格中。在这组结果中没有 `nextToken`，因为您已查看了所有 9 篇文章，没有其余文章了。如下所示：

  ```
  {
    "data": {
      "allPost": {
        "posts": [
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## 设置 allPostsBy作者解析器（DynamoDB 查询）
<a name="setting-up-the-allpostsbyauthor-resolver-ddb-query"></a>

除了扫描 DynamoDB 以查找所有文章以外，您还可以查询 DynamoDB 以检索特定作者创建的文章。您以前创建的 DynamoDB 表已具有一个名为 `author-index` 的 `GlobalSecondaryIndex`，您可以将其与 DynamoDB `Query` 操作一起使用以检索特定作者创建的所有文章。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Query` 类型，添加新的 `allPostsByAuthor` 查询，如下所示：

  ```
  type Query {
      allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
      allPost(count: Int, nextToken: String): PaginatedPosts!
      getPost(id: ID): Post
  }
  ```

   **注意**：这次使用与 `allPost` 查询相同的 `PaginatedPosts` 类型。
+ 选择**保存**。
+ 在右侧的 “**数据类型**” 窗格中，在 “**查询**” 类型上找到新创建的 “**allPostsBy作者**” 字段，然后选择 “**附加**”。
+ 在**操作**菜单中，选择**更新运行时**，然后选择**单位解析器 (仅限 VTL)**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Query",
      "index" : "author-index",
      "query" : {
        "expression": "author = :author",
          "expressionValues" : {
            ":author" : $util.dynamodb.toDynamoDBJson($context.arguments.author)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": "${context.arguments.nextToken}"
      #end
  }
  ```

  与 `allPost` 解析器相似，此解析器也有两个可选参数：`count`指定单次调用可返回的项目数量上限；`nextToken` 可用于检索下一组结果（`nextToken` 的值可从上次调用获得）。
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```

   **注意**：此处使用的响应映射模板与 `allPost` 解析器中所用的相同。
+ 选择**保存**。

了解有关 `Query` 请求映射的更多信息，请参阅 [Query](aws-appsync-resolver-mapping-template-reference-dynamodb-query.md) 参考文档。

### 调用 API 以查询某一作者的所有文章
<a name="call-the-api-to-query-all-posts-by-an-author"></a>

现在解析器已经设置好了， AWS AppSync 知道如何将传入的`allPostsByAuthor`突变转换为针对索引的 DynamoDB 操作`Query`。`author-index`现在，您可以查询表，检索某一作者的所有文章。

但首先我们需要在表中再填充一些文章，因为目前所有文章都是同一作者。
+ 选择 **Queries** 选项卡。
+ 在 **Queries (查询)** 窗格中，粘贴以下变更：

  ```
  mutation addPost {
    post1: addPost(id:10 author: "Nadia" title: "The cutest dog in the world" content: "So cute. So very, very cute." url: "https://aws.amazon.com/appsync/" ) { author, title }
    post2: addPost(id:11 author: "Nadia" title: "Did you know...?" content: "AppSync works offline?" url: "https://aws.amazon.com/appsync/" ) { author, title }
    post3: addPost(id:12 author: "Steve" title: "I like GraphQL" content: "It's great" url: "https://aws.amazon.com/appsync/" ) { author, title }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。

现在，让我们查询表，返回作者为 `Nadia` 的所有文章。
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  query allPostsByAuthor {
    allPostsByAuthor(author: "Nadia") {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 作者为 `Nadia` 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you know...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

`Query` 的分页方式与 `Scan` 相同。例如，如果我们查找作者为 `AUTHORNAME` 的所有文章，每次显示 5 个结果。
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 作者为 `AUTHORNAME` 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "6",
            "title": "A series of posts, Volume 6"
          },
          {
            "id": "4",
            "title": "A series of posts, Volume 4"
          },
          {
            "id": "2",
            "title": "A series of posts, Volume 2"
          },
          {
            "id": "7",
            "title": "A series of posts, Volume 7"
          },
          {
            "id": "1",
            "title": "A series of posts, Volume 1"
          }
        ],
        "nextToken": "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
      }
    }
  }
  ```
+ 用上次查询返回的值更新 `nextToken` 参数，如下所示：

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "AUTHORNAME"
      count: 5
      nextToken: "eyJ2ZXJzaW9uIjoxLCJ0b2tlbiI6IkFRSUNBSGo4eHR0RG0xWXhUa1F0cEhXMEp1R3B0M1B3eThOSmRvcG9ad2RHYjI3Z0lnSExqRnVhVUR3ZUhEZ2QzNGJ2QlFuY0FBQUNqekNDQW9zR0NTcUdTSWIzRFFFSEJxQ0NBbnd3Z2dKNEFnRUFNSUlDY1FZSktvWklodmNOQVFjQk1CNEdDV0NHU0FGbEF3UUJMakFSQkF5Qkg4Yk1obW9LVEFTZHM3SUNBUkNBZ2dKQ3dISzZKNlJuN3pyYUVKY1pWNWxhSkNtZW1KZ0F5N1dhZkc2UEdTNHpNQzJycTkwZHFJTFV6Z25wck9Gd3pMS3VOQ2JvUXc3VDI5eCtnVExIbGg4S3BqbzB1YjZHQ3FwcDhvNDVmMG9JbDlmdS9JdjNXcFNNSXFKTXZ1MEVGVWs1VzJQaW5jZGlUaVRtZFdYWlU1bkV2NkgyRFBRQWZYYlNnSmlHSHFLbmJZTUZZM0FTdmRIL0hQaVZBb1RCMk1YZkg0eGJOVTdEbjZtRFNhb2QwbzdHZHJEWDNtODQ1UXBQUVNyUFhHemY0WDkyajhIdlBCSWE4Smcrb0RxbHozUVQ5N2FXUXdYWWU2S0h4emI1ejRITXdEdXEyRDRkYzhoMi9CbW10MzRMelVGUVIyaExSZGRaZ0xkdzF5cHJZdFZwY3dEc1d4UURBTzdOcjV2ZEp4VVR2TVhmODBRSnp1REhXREpTVlJLdDJwWmlpaXhXeGRwRmNod1BzQ3d2aVBqMGwrcWFFWU1jMXNQbENkVkFGem43VXJrSThWbS8wWHlwR2xZb3BSL2FkV0xVekgrbGMrYno1ZEM2SnVLVXdtY1EyRXlZeDZiS0Izbi9YdUViWGdFeU5PMWZTdE1rRlhyWmpvMVpzdlYyUFRjMzMrdEs0ZDhkNkZrdjh5VVR6WHhJRkxIaVNsOUx6VVdtT3BCaWhrTFBCT09jcXkyOHh1UmkzOEM3UFRqMmN6c3RkOUo1VUY0azBJdUdEbVZzM2xjdWg1SEJjYThIeXM2aEpvOG1HbFpMNWN6R2s5bi8vRE1EbDY3RlJraG5QNFNhSDBpZGI5VFEvMERLeFRBTUdhcWpPaEl5ekVqd2ZDQVJleFdlbldyOGlPVkhScDhGM25WZVdvbFRGK002N0xpdi9XNGJXdDk0VEg3b0laUU5lYmZYKzVOKy9Td25Hb1dyMTlWK0pEb2lIRVFLZ1cwMWVuYjZKUXo5Slh2Tm95ZzF3RnJPVmxGc2xwNlRHa1BlN2Rnd2IrWT0ifQ=="
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 作者为 `AUTHORNAME` 的剩余文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "8",
            "title": "A series of posts, Volume 8"
          },
          {
            "id": "5",
            "title": "A series of posts, Volume 5"
          },
          {
            "id": "3",
            "title": "A series of posts, Volume 3"
          },
          {
            "id": "9",
            "title": "A series of posts, Volume 9"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## 使用集
<a name="using-sets"></a>

到目前为止，该`Post`类型一直是一个平面 key/value 对象。您还可以使用 AWS AppSyncDynamo数据库解析器对复杂的对象进行建模，例如集合、列表和地图。

让我们更新 `Post` 类型，加入标签。一篇文章可以具有 0 个或更多标签，这些标签作为字符串集存储在 DynamoDB 中。您还将设置一些变更，用于添加并删除标签；还要用一个新查询扫描具有特定标签的文章。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中修改 `Post` 类型，添加新的 `tags` 字段，如下所示：

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
  }
  ```
+ 在 **Schema (架构)** 窗格中修改 `Query` 类型，添加新的 `allPostsByTag` 查询，如下所示：

  ```
  type Query {
    allPostsByTag(tag: String!, count: Int, nextToken: String): PaginatedPosts!
    allPostsByAuthor(author: String!, count: Int, nextToken: String): PaginatedPosts!
    allPost(count: Int, nextToken: String): PaginatedPosts!
    getPost(id: ID): Post
  }
  ```
+ 在 **Schema (架构)** 窗格中修改 `Mutation` 类型，添加新的 `addTag` 和 `removeTag` 变更，如下所示：

  ```
  type Mutation {
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ 选择**保存**。
+ 在右侧**的数据类型**窗格中，在**查询**类型上找到新创建的**allPostsBy标签**字段，然后选择**附加**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "Scan",
      "filter": {
        "expression": "contains (tags, :tag)",
          "expressionValues": {
            ":tag": $util.dynamodb.toDynamoDBJson($context.arguments.tag)
          }
      }
      #if( ${context.arguments.count} )
          ,"limit": $util.toJson($context.arguments.count)
      #end
      #if( ${context.arguments.nextToken} )
          ,"nextToken": $util.toJson($context.arguments.nextToken)
      #end
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  {
      "posts": $utils.toJson($context.result.items)
      #if( ${context.result.nextToken} )
          ,"nextToken": $util.toJson($context.result.nextToken)
      #end
  }
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **addTag** 字段，然后选择**附加**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "ADD tags :tags, version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **removeTag** 字段，然后选择**附加**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
      "version" : "2017-02-28",
      "operation" : "UpdateItem",
      "key" : {
          "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
      },
      "update" : {
          "expression" : "DELETE tags :tags ADD version :plusOne",
          "expressionValues" : {
              ":tags" : { "SS": [ $util.toJson($context.arguments.tag) ] },
              ":plusOne" : { "N" : 1 }
          }
      }
  }
  ```
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。

### 调用 API 以处理标签
<a name="call-the-api-to-work-with-tags"></a>

现在您已经设置了解析器， AWS AppSync 知道如何将传入的`addTag``removeTag`、和`allPostsByTag`请求转换为 D `UpdateItem` ynamo `Scan` DB 和操作。

我们选择您之前创建的一个文章进行尝试。例如，我们使用作者为 `Nadia` 的一篇文章。
+ 选择 **Queries** 选项卡。
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  query allPostsByAuthor {
    allPostsByAuthor(
      author: "Nadia"
    ) {
      posts {
        id
        title
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ Nadia 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "allPostsByAuthor": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world"
          },
          {
            "id": "11",
            "title": "Did you known...?"
          }
        ],
        "nextToken": null
      }
    }
  }
  ```
+ 让我们使用标题为 `"The cutest dog in the world"` 的文章。记下其 `id`，因为您稍后将用到它。

现在，让我们尝试添加一个 `dog` 标签。
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation addTag {
    addTag(id:10 tag: "dog") {
      id
      title
      tags
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 将使用新标签更新该文章。

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

您可以添加更多标签，如下所示：
+ 更新变更以将 `tag` 参数更改为 `puppy`。

  ```
  mutation addTag {
    addTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 将使用新标签更新该文章。

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog",
          "puppy"
        ]
      }
    }
  }
  ```

您也可以删除标签：
+ 在 **Queries (查询)** 窗格中，粘贴以下更改。您还需要将 `id` 参数更新为您以前记下的值。

  ```
  mutation removeTag {
    removeTag(id:10 tag: "puppy") {
      id
      title
      tags
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 文章已更新，`puppy` 标签已删除。

  ```
  {
    "data": {
      "addTag": {
        "id": "10",
        "title": "The cutest dog in the world",
        "tags": [
          "dog"
        ]
      }
    }
  }
  ```

您也可以搜索所有具有标签的文章：
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  query allPostsByTag {
    allPostsByTag(tag: "dog") {
      posts {
        id
        title
        tags
      }
      nextToken
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ 将返回具有 `dog` 标签的所有文章，如下所示：

  ```
  {
    "data": {
      "allPostsByTag": {
        "posts": [
          {
            "id": "10",
            "title": "The cutest dog in the world",
            "tags": [
              "dog",
              "puppy"
            ]
          }
        ],
        "nextToken": null
      }
    }
  }
  ```

## 使用列表和映射
<a name="using-lists-and-maps"></a>

除了使用 DynamoDB 集以外，您还可以使用 DynamoDB 列表和映射对单个对象中的复杂数据进行建模。

我们可以为文章添加评论功能。这会建模为 DynamoDB 中的 `Post` 对象上的映射对象列表。

 **注意**：在真正的应用程序中，您会在评论自身的表中对评论进行建模。在本教程中，您仅将评论添加到 `Post` 表。
+ 选择**架构**选项卡。
+ 在 **Schema (架构)** 窗格中，添加新的 `Comment` 类型，如下所示：

  ```
  type Comment {
      author: String!
      comment: String!
  }
  ```
+ 在 **Schema (架构)** 窗格中修改 `Post` 类型，添加新的 `comments` 字段，如下所示：

  ```
  type Post {
    id: ID!
    author: String
    title: String
    content: String
    url: String
    ups: Int!
    downs: Int!
    version: Int!
    tags: [String!]
    comments: [Comment!]
  }
  ```
+ 在 **Schema (架构)** 窗格中修改 `Mutation` 类型，添加新的 `addComment` 变更，如下所示：

  ```
  type Mutation {
    addComment(id: ID!, author: String!, comment: String!): Post
    addTag(id: ID!, tag: String!): Post
    removeTag(id: ID!, tag: String!): Post
    deletePost(id: ID!, expectedVersion: Int): Post
    upvotePost(id: ID!): Post
    downvotePost(id: ID!): Post
    updatePost(
      id: ID!,
      author: String,
      title: String,
      content: String,
      url: String,
      expectedVersion: Int!
    ): Post
    addPost(
      author: String!,
      title: String!,
      content: String!,
      url: String!
    ): Post!
  }
  ```
+ 选择**保存**。
+ 在右侧的**数据类型**窗格中，找到 **Mutation** 类型上的新创建的 **addComment** 字段，然后选择**附加**。
+ 在 **Data source name (数据源名称)** 中，选择 **PostDynamoDBTable**。
+ 在 **Configure the request mapping template (配置请求映射模板)** 中，粘贴以下内容：

  ```
  {
    "version" : "2017-02-28",
    "operation" : "UpdateItem",
    "key" : {
      "id" : $util.dynamodb.toDynamoDBJson($context.arguments.id)
    },
    "update" : {
      "expression" : "SET comments = list_append(if_not_exists(comments, :emptyList), :newComment) ADD version :plusOne",
      "expressionValues" : {
        ":emptyList": { "L" : [] },
        ":newComment" : { "L" : [
          { "M": {
            "author": $util.dynamodb.toDynamoDBJson($context.arguments.author),
            "comment": $util.dynamodb.toDynamoDBJson($context.arguments.comment)
            }
          }
        ] },
        ":plusOne" : $util.dynamodb.toDynamoDBJson(1)
      }
    }
  }
  ```

  此更新表达式将一个列表（包含新评论）追加到现有的 `comments` 列表中。如果这个列表不存在，将创建它。
+ 在 **Configure the response mapping template (配置响应映射模板)** 中，粘贴以下内容：

  ```
  $utils.toJson($context.result)
  ```
+ 选择**保存**。

### 调用 API 以添加评论
<a name="call-the-api-to-add-a-comment"></a>

现在您已经设置了解析器， AWS AppSync 知道如何将传入的`addComment`请求转换为 DynamoDB 操作`UpdateItem`。

让我们尝试在您已添加标签的文章中添加评论。
+ 选择 **Queries** 选项卡。
+ 在**Queries (查询)** 窗格中粘贴以下查询：

  ```
  mutation addComment {
    addComment(
      id:10
      author: "Steve"
      comment: "Such a cute dog."
    ) {
      id
      comments {
        author
        comment
      }
    }
  }
  ```
+ 选择 **Execute query (执行查询)**（橙色播放按钮）。
+ Nadia 的全部文章应出现在查询窗格右侧的结果窗格中。如下所示：

  ```
  {
    "data": {
      "addComment": {
        "id": "10",
        "comments": [
          {
            "author": "Steve",
            "comment": "Such a cute dog."
          }
        ]
      }
    }
  }
  ```

如果您多次执行该请求，列表中将追加多条评论。

## 结论
<a name="conclusion"></a>

在本教程中，您构建了一个 API，允许我们使用 AWS AppSync 和 GraphQL 在 DynamoDB 中操作 Post 对象。有关更多信息，请参阅[解析器映射模板参考](resolver-mapping-template-reference.md#aws-appsync-resolver-mapping-template-reference)。

要进行清理，您可以从控制台中删除 AppSync GraphQL API。

要删除 DynamoDB 表和您为本教程创建的 IAM 角色，您可以运行以下命令来删除`AWSAppSyncTutorialForAmazonDynamoDB`堆栈，或者访问 CloudFormation 控制台删除堆栈：

```
aws cloudformation delete-stack \
    --stack-name AWSAppSyncTutorialForAmazonDynamoDB
```