教程:DynamoDB 解析器 - Amazon AppSync
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

教程:DynamoDB 解析器

本教程展示了如何将自己的Amazon DynamoDB 表引入 GraphQL APIAmazon AppSync 并将其连接到 GraphQL API。

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

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

设置 DynamoDB p

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

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

    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

    或者,您可以在您的Amazon账户中在美国西部 2(俄勒冈)地区启动以下Amazon CloudFormation堆栈。

    这会创建以下内容:

    • 一个名AppSyncTutorial-Post为的 DynamoDB 表用于保存Post数据。

    • 一个 IAM 角色以及关联的 IAM 托管策略,允许 Amazon AppSync 与 Post 表交互。

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

    aws cloudformation describe-stacks --stack-name AWSAppSyncTutorialForAmazonDynamoDB
  3. 稍后要删除资源,您可以运行以下操作:

    aws cloudformation delete-stack --stack-name AWSAppSyncTutorialForAmazonDynamoDB

创建一个 GrapQL API

要在 Amazon AppSync 中创建 GraphQL API:

  1. 登录 Amazon Web Services Management Console并打开 AppSync 控制台

    1. API 控制面板中,选择创建 API

  2. 在 “自定义 API 或从 Amazon DynamoDB 导入” 窗口下,选择 “从头开始构建”。

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

  3. API 名称字段中,将 API 的名称设置为AWSAppSyncTutorial

  4. 选择 Create(创建)

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

定义基本的帖子 API

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

  1. 登录 Amazon Web Services Management Console并打开 AppSync 控制台

    1. API 控制面板中,选择您刚刚创建的 API。

  2. 侧栏中,选择架构

    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! }
  3. 选择 Save(保存)。

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

为 DynamoDB 表配置数据源

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

首先,Amazon AppSync 需要知道您的表。要实现此目的,需要在 Amazon AppSync 中设置数据源:

  1. 登录 Amazon Web Services Management Console并打开 AppSync 控制台

    1. API 控制面板中,选择你的 GraphQL API。

    2. 在侧中,选择数据源

  2. 选择 Create data source

    1. 数据源名称中,输入PostDynamoDBTable

    2. 对于数据源类型,选择 Amazon DynamoDB 表

    3. 对于区域,选择 US-WEST-2

    4. 对于表名称,选择 AppSyncTutorial-Pos t DynamoDB 表。

    5. 创建新的 IAM 角色(推荐)或选择具有lambda:invokeFunction IAM 权限的现有角色。现有角色需要信任策略,如附加数据源部分所述。

      以下是一个 IAM 策略,该策略具有执行操作所需的权限:

      { "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:*" ] } ] }
  3. 选择 Create(创建)

设置 addPost 解析器 (DynamoDB PutItem)

获Amazon AppSync 知 DynamoDB 表后,您可以通过定义解析器将其链接到单个查询和突变。您创建的第一个解析器是addPost解析器,它使您能够在AppSyncTutorial-Post DynamoDB 表中创建帖子。

解析程序具有以下组件:

  • GraphQL 架构中的位置,用于附加解析程序。在本例中,您将设置 addPost 类型的 Mutation 字段的解析程序。调用者调用 mutation { addPost(...){...} } 时,将调用此解析程序。

  • 此解析程序所用的数据源。在本例中,您要使用之前定义的 PostDynamoDBTable 数据源,这样您就可以在 AppSyncTutorial-Post DynamoDB 表中添加条目。

  • 请求映射模板。请求映射模板的目的是将调用者的传入请求转换为指令,这样 Amazon AppSync 就可以针对 DynamoDB 执行操作。

  • 响应映射模板。响应映射模板的任务是将 DynamoDB 的响应转换回 GraphQL 期待获得的内容。如果 DynamoDB 中的数据形态与 GraphQL 中的 Post 类型不同,此模板很有用。但在此例中它们的形态相同,所以只用于传递数据。

设置解析程序:

  1. 登录 Amazon Web Services Management Console并打开 AppSync 控制台

    1. API 控制面板中,选择你的 GraphQL API。

    2. 在侧中,选择数据源

  2. 选择 Create data source

    1. 数据源名称中,输入PostDynamoDBTable

    2. 对于数据源类型,选择 Amazon DynamoDB 表

    3. 对于区域,选择 US-WEST-2

    4. 对于表名称,选择 AppSyncTutorial-Pos t DynamoDB 表。

    5. 创建新的 IAM 角色(推荐)或选择具有lambda:invokeFunction IAM 权限的现有角色。现有角色需要信任策略,如附加数据源部分所述。

      以下是一个 IAM 策略,该策略具有执行操作所需的权限:

      { "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:*" ] } ] }
  3. 选择 Create(创建)

  4. 选择 Schema (架构) 选项卡。

  5. 在右侧的 “数据类型” 窗格中,找到 “突变” 类型上的 addPos t t 字段,然后选择 “附加”。

  6. 数据源名称中,选择 PostDynamodbTable

  7. 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部分向Amazon AppSync 和 DynamoDB 表示该值将是字符串值。实际的值由 author 参数填充。与此类似,version 字段是一个数字字段,因为它使用 N 作为类型。最后,您还将初始化 upsdownsversion 字段。

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

    有关映射模板的更多信息,请参阅 解析程序映射模板概述参考文档。有关 GetItem 请求映射的更多信息,请参阅GetItem参考文档。有关类型的更多信息,请参阅类型系统(请求映射)参考文档。

  8. Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

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

  9. 选择 Save(保存)。

调用 API 来添加文章

现在解析器已经设置好了,Amazon 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 } } }

以下是具体过程:

  • Amazon AppSync 收到了addPost突变请求。

  • Amazon 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 } } }
  • Amazon AppSync 使用请求映射文档生成和执行了 DynamoDBPutItem 请求。

  • Amazon 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)

现在,您可以向AppSyncTutorial-Post DynamoDB 表添加数据,您需要设置getPost查询,这样它才能从AppSyncTutorial-Post表中检索数据。为了实现此目的,您要设置另一解析程序。

  • 选择 Schema (架构) 选项卡。

  • 在右侧的数据类型窗格中,找到查询类型上的 getPost 字段,然后选择附加

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

调用 API 以获取文章

现在解析器已经设置好了,Amazon AppSync 知道如何将传入的getPost查询转换为 DynamoDBGetItem 操作。现在,您可以运行查询,检索之前创建的文章。

  • 选择 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 } } }

以下是具体过程:

  • Amazon AppSync 收到了getPost查询请求。

  • Amazon AppSync 接收了请求和请求映射模板,并生成了请求映射文档。如下所示:

    { "version" : "2017-02-28", "operation" : "GetItem", "key" : { "id" : { "S" : "123" } } }
  • Amazon AppSync 使用请求映射文档生成和执行了 DynamoDB GetItem 请求。

  • Amazon 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查询只需要idauthor、和title,则可以将请求映射模板更改为使用投影表达式仅指定您想要从 DynamoDB 表中获取的属性,以避免从 DynamoDB 向其传输不必要的数据Amazon 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)

到目前为止,您可以在 DynamoDB 中创建和检索Post对象。现在,您要设置一项新的更改,以便更新对象。你将使用 UpdateItem DynamoDB 操作来完成此操作。

  • 选择 Schema (架构) 选项卡。

  • 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! }
  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,找到突变类型上新创建的 UpdatePos t 字段,然后选择 Att ach

  • 数据源名称中,选择 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 } } } }

    注意:此解析器使用的是 DynamoDB UpdateItem,这与 PutItem 操作有很大不同。你不是写整篇文章,而是让 DynamoDB 更新某些属性。通过将和DynamoDB 更新表达式结合使用来执行此操作。表达式本身是在 expression 部分的 update 字段中指定的。它会设置 authortitlecontent 和 URL 属性,还会递增 version 字段。要使用的值不会出现在表达式本身;表达式中的占位符名称以冒号打头,并在 expressionValues 字段中进行定义。最后,DynamoDB 的保留字不能出现在中expression。例如,url 是保留关键字,所以要更新 url 字段,您可使用名称占位符,并在 expressionNames 字段中定义它们。

    有关UpdateItem请求映射的更多信息,请参阅UpdateItem参考文档。有关如何编写更新表达式的更多信息,请参阅 DynamoDB UpdateExpressions 文档

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

调用 API 以更新文章

现在解析器已经设置好了,Amazon AppSync 知道如何将传入的update突变转换为 DynamoDBUpdate 操作。现在,您可以运行更改,以更新您之前写入的项目。

  • 选择 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 } } }

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

修改 UpdatePost 解析器 (DynamoDB UpdateItem)

updatePost 更改看上去不错,但它有两个主要问题:

  • 如果您只希望更新一个字段,则必须更新所有字段。

  • 如果两个人同时修改对象,您可能会丢失信息。

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

  1. 选择 Schema (架构) 选项卡。

  2. Schema (架构) 窗格中修改 Mutation 类型中的 updatePost 字段,删除 authortitlecontenturl 参数的感叹号,确保 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! }
  3. 选择 Save(保存)。

  4. 在右侧的 “数据类型” 窗格中,找到 “突变” 类型上的 UpdatePos t 字段。

  5. 选择 PostDynamodbTable 打开现有的解析器。

  6. 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) } } }
  7. 选择 Save(保存)。

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

还有一个新的 condition 部分。条件表达式使您能够根据执行操作之前已在 DynamoDB 中的对象的状态告知Amazon AppSync DynamoDB 请求是否应成功。在这种情况下,只有当 DynamoDB 中当前项目的version字段与expectedVersion参数完全匹配时,您才希望UpdateItem请求成功。

有关条件表达式的更多信息,请参阅条件表达式参考文档。

调用 API 以更新文章

让我们尝试用新的解析程序更新 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 } } }

在此请求中,您要求Amazon AppSync 和 DynamoDB 仅更新titlecontent字段。它不会处理所有其他字段(除了递增 version 字段)。您将 title 属性设为新值,并删除文章的 content 属性。authorurlupsdowns 字段没有变化。

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

{ "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参数相匹配。请求成功,这意味着该version字段在 DynamoDB 中递增为3

  • 第二次运行请求时,DynamoDB 中帖子version字段的值为3,该值与expectedVersion参数不匹配。

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

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

有关如何处理条件检查失败的更多信息,请参阅条件表达式映射模板参考文档。

创建 upvotePost 和 downVotePost 突变 (dynamoDB UpdateItem)

Post 类型有 upsdowns 字段,用于记录赞同投票和反对投票,但现在还无法通过 API 使用它们。让我们添加一些更改,对文章进行赞同和反对投票。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Mutation 类型,添加新的 upvotePostdownvotePost 更改,如下所示:

    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! }
  • 选择 Save(保存)。

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的 upvotePos t 字段,然后选择 “附加”。

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,找到突变类型上新创建的downvotePost字段,然后选择 Attac h

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

调用 API,为文章投赞同票或反对票

现在,新的解析器已经设置完毕,Amazon AppSync 知道如何将传入upvotePostdownvote突变转换为 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 } } }
  • 再选择几次 (执行查询) 按钮。您应看到,每次您执行查询时,upsversion 字段均会递增 1。

  • 更改查询以调用 downvotePost 更改,如下所示:

    mutation votePost { downvotePost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。这次您应看到,每次您执行查询时,downsversion 字段均会递增 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)

接下来您要设置的更改是删除一个文章。你将使用DeleteItem DynamoDB 操作来完成此操作。

  • 选择 Schema (架构) 选项卡。

  • 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 字段设为可选,稍后在添加请求映射模板时将对此进行说明。

  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,找到突变类型上新创建的删除字段,然后选择 Attac h

  • 数据源名称中,选择 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属性完全匹配时才允许DeleteItem请求成功expectedVersion。如果未设置此参数,则 DeleteItem 请求中不指定条件表达式。无论该项目的值如何,或者该项目是否存在于 DynamoDB 中,它都会成功。version

  • Configure the response mapping template (配置响应映射模板) 中,粘贴以下内容:

    $utils.toJson($context.result)

    注意:即使您要删除一个项目,如果该项目不是已经删除,还是可以返回要删除的项目。

  • 选择 Save(保存)。

有关DeleteItem请求映射的更多信息,请参阅DeleteItem参考文档。

调用 API 以删除文章

现在解析器已经设置好了,Amazon AppSync 知道如何将传入的delete突变转换为 DynamoDBDeleteItem 操作。现在,您可以运行更改,从表中删除一些内容。

  • 选择 Queries 选项卡。

  • Queries (查询) 窗格中,粘贴以下更改。您还需要更新 id 参数,改为您之前记下的值。

    mutation deletePost { deletePost(id:123) { id author title content url ups downs version } }
  • 选择 Execute query (执行查询)(橙色播放按钮)。

  • 该帖子已从 DynamoDB 中删除。请注意,Amazon 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 中帖子的值与参数中expectedValue指定的值不匹配。version对象的当前值返回到 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)

到目前为止,只有当您知道您想查看的每个帖子的 id 时,API 才有用。让我们添加新的解析程序,它可以返回表中的所有文章。

  • 选择 Schema (架构) 选项卡。

  • Schema (架构) 窗格中修改 Query 类型,添加新的 allPost 查询,如下所示:

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

    type PaginatedPosts { posts: [Post!]! nextToken: String }
  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,在查询类型上找到新创建的 allPos t t 字段,然后选择 Attac h

  • 数据源名称中,选择 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,其中包含一组文章和一个分页标记。此对象的形态与 Amazon AppSync DynamoDB 解析程序返回的对象不同:文章列表在 Amazon AppSync DynamoDB 解析程序的结果中称为 items,但在 PaginatedPosts 中称为 posts

  • 选择 Save(保存)。

有关 Scan 请求映射的更多信息,请参阅 Scan 参考文档。

调用 API 以扫描所有文章

现在解析器已经设置好了,Amazon AppSync 知道如何将传入的allPost查询转换为 DynamoDBScan 操作。现在您可以扫描整个表,检索所有文章。

在进行尝试之前,您需要在表中填充一些数据,因为您已经删除了之前使用的所有内容。

  • 选择 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 查询)

除了扫描 DynamoDB 中的所有帖子外,您还可以查询 DynamoDB 以检索特定作者创建的帖子。您之前创建的 DynamoDB 表已经有一个GlobalSecondaryIndex调用,author-index您可以与 DynamoDBQuery 操作一起使用来检索特定作者创建的所有帖子。

  • 选择 Schema (架构) 选项卡。

  • 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 类型。

  • 选择 Save(保存)。

  • 在右侧的 “数据类型” 窗格中,找到查询类型上新创建的 “allPostsBy作者” 字段,然后选择 “附加”。

  • 数据源名称中,选择 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 解析程序中所用的相同。

  • 选择 Save(保存)。

了解有关 Query 请求映射的更多信息,请参阅 Query 参考文档。

调用 API 以查询某一作者的所有文章

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

但首先我们需要在表中再填充一些文章,因为目前所有文章都是同一作者。

  • 选择 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 } } }

使用集

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

让我们更新 Post 类型,加入标签。帖子可以有 0 个或更多标签,这些标签作为字符串集存储在 DynamoDB 中。您还将设置一些更改,用于添加并删除标签;还要用一个新查询扫描具有特定标签的文章。

  • 选择 Schema (架构) 选项卡。

  • 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 类型,添加新的 addTagremoveTag 更改,如下所示:

    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! }
  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,找到查询类型上新创建的allPostsBy标签字段,然后选择附加

  • 数据源名称中,选择 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 }
  • 选择 Save(保存)。

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的 addTag 字段,然后选择 “附加”。

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

  • 在右侧的 “数据类型” 窗格中,在 “突变” 类型上找到新创建的 RemoveTag 字段,然后选择 “附加”。

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

调用 API 以处理标签

现在,您已经设置了解析器,Amazon AppSync 知道如何将传入的addTagremoveTag、和allPostsByTag请求转换为 DynamoDBUpdateItemScan操作。

我们选择您之前创建的一个文章进行尝试。例如,我们使用作者为 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 } } }

使用列表和映射

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

我们可以为文章添加评论功能。这将建模为 DynamoDB 中该Post对象的地图对象列表。

注意:在真正的应用程序中,您会在评论自身的表中对评论进行建模。在本教程中,您仅将评论添加到 Post 表。

  • 选择 Schema (架构) 选项卡。

  • 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! }
  • 选择 Save(保存)。

  • 在右侧的数据类型窗格中,找到突变类型上新创建的 addComment Com ment 字段,然后选择 AddComment 段。

  • 数据源名称中,选择 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)
  • 选择 Save(保存)。

调用 API 以添加评论

现在您已经设置了解析器,Amazon AppSync 知道如何将传入的addComment请求转换为 DynamoDBUpdateItem 操作。

让我们尝试在您已添加标签的文章中添加评论。

  • 选择 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." } ] } } }

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

结论

在本教程中,您构建了一个 API,允许我们使用Amazon AppSync GraphQL 在 DynamoDB 中操作 Post 对象。有关更多信息,请参阅解析程序映射模板参考

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

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

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