AWS AppSync - 将全局二级索引添加到 DynamoDB 并使用 GSI 排序键进行分页 [英] AWS AppSync - Adding Global Secondary Index to DynamoDB and pagination using GSI sort key

查看:23
本文介绍了AWS AppSync - 将全局二级索引添加到 DynamoDB 并使用 GSI 排序键进行分页的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个结构,其中列出了对具有 postId 排序的帖子的评论.他们的 lastChangeTime 降序.

架构中的模型在下面共享.

输入注释{身份证:身份证!postId:字符串!用户:字符串!上次更改时间:字符串评论正文:字符串}

它已经有一个支持 DynamoDB 表和通用 CRUD 解析器.而id字段是表中的主键.

我计划构建如下查询:

<代码>{版本":2017-02-28",操作":查询","index" : "postId-index",询问" : {"表达式": "post = :postId",表达式值":{":postId" : {S":${ctx.args.postId}"}}},限制":$util.defaultIfNull($ctx.args.first, 20),"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.after, null)),scanIndexForward":假}

为了使它起作用,我应该如何在postId(即postId-index)上添加全球二级索引(GSI)?

我应该在定义它时在 lastChangeTime 上添加一个 排序键 吗?还是 lastChangeTime 字段需要自己单独的索引才能排序?

解决方案

很简单.你可以用两种不同的方式来做,或者你可以同时使用这两种方式来获得更好的灵活性.(如果你已经解决了,我希望它会帮助别人).

这样做,您可以使用查询参数动态设置 sortDirection.

详细代码如下.在此之前请注意这一点.

  1. 第一点是你的评论类型——你正在使用

    输入注释{身份证:身份证!postId:字符串!## 类型定义的其余部分}

这不是设置链接到帖子的评论类型的最佳方式.

更好的方法是:

输入注释{邮编:身份证!## 在 AppSync 控制台的 DataSource 中选择这个作为主键评论ID:字符串!## 在 AppSync 控制台的 DataSource 中选择这个作为排序键## 类型定义的其余部分}

如果您这样做,您的 DynamoDB 表将具有类似于下图所示的结构 (

这样,因为所有评论将彼此相邻记录(在同一个 PostId 下)AppSync 响应时间会快得多.

  1. 正如@Lisa M Shon 提到的,您可以在 CommentTable 上启动 GSI,其中 PostId 是分区键, addedTime 是排序键.如果您想使用下面提供的解析器,请将其称为postID-addedTime-index".确保在 GSI 中的 addedTime 上选择数字".

然后在您的架构中,您可以定义以下类型:

输入注释{邮编:身份证!评论ID:字符串!内容:字符串!添加时间:诠释!}输入评论连接 {项目:[评论]下一个令牌:字符串}输入帖子{身份证:身份证!帖子内容:字符串!添加时间:诠释!## 选项 1. 获取包含所有相关评论的帖子详细信息.## 如果提供了startFromTime",它将获取从该时间戳开始的所有评论.## 如果未提供startFromTime",它将获取所有评论.评论(过滤器:TableCommentFilterInput,排序方向:排序方向,startFromTime: Int,限制:整数,下一个令牌:字符串): 评论连接}类型查询 {## 选项 2.它将仅获取给定 PostId 的评论.## 如果提供了startFromTime",它将获取从该时间戳开始的所有评论.## 如果未提供startFromTime",它将获取所有评论.postCommentsByAddTime(postID:字符串!,startFromTime: Int!,排序方向:排序方向,过滤器:TableCommentFilterInput,计数:整数,下一个令牌:字符串): 分页评论## 你的其他查询}## 架构定义的其余部分

您可以同时使用 - 选项 1 和选项 2 并同时使用.

完整的架构代码在这里(展开下面的代码段):

type Comment {邮编:身份证!评论ID:字符串!内容:字符串!添加时间:诠释!}输入评论连接 {项目:[评论]下一个令牌:字符串}输入 CreateCommentInput {邮编:身份证!评论ID:字符串!内容:字符串!添加时间:诠释!}输入 CreatePostInput {帖子内容:字符串!添加时间:诠释!}输入删除评论输入{邮编:身份证!评论ID:字符串!}输入 DeletePostInput {身份证:身份证!}类型突变 {createComment(输入:CreateCommentInput!):评论updateComment(输入:UpdateCommentInput!):评论deleteComment(输入:DeleteCommentInput!):评论createPost(输入:CreatePostInput!):发布updatePost(输入:UpdatePostInput!):发布deletePost(输入:DeletePostInput!):发布}类型分页评论 {项目:[评论!]!下一个令牌:字符串}输入帖子{身份证:身份证!帖子内容:字符串!添加时间:诠释!评论(过滤器:TableCommentFilterInput,排序方向:排序方向,startFromTime: Int,限制:整数,下一个令牌:字符串): 评论连接}类型 PostConnection {项目:[帖子]下一个令牌:字符串}类型查询 {getComment(postID:ID!,commentID:字符串!):评论listComments(过滤器:TableCommentFilterInput,限制:Int,nextToken:字符串):CommentConnectiongetPost(id:ID!):发布listPosts(过滤器:TablePostFilterInput,限制:Int,nextToken:字符串):PostConnectionpostCommentsByAddTime(postID:字符串!,startFromTime: Int!,排序方向:排序方向,过滤器:TableCommentFilterInput,计数:整数,下一个令牌:字符串): 分页评论}枚举排序方向 {ASCDESC}类型订阅{onCreateComment(postID: ID,评论ID:字符串,内容:字符串,添加时间:整数): 评论@aws_subscribe(突变:[createComment"])更新评论(postID: ID,评论ID:字符串,内容:字符串,添加时间:整数): 评论@aws_subscribe(突变:[updateComment"])删除评论(postID: ID,评论ID:字符串,内容:字符串,添加时间:整数): 评论@aws_subscribe(突变:[deleteComment"])onCreatePost(id: ID, postContent: String, addedTime: Int): Post@aws_subscribe(突变:[createPost"])onUpdatePost(id: ID, postContent: String, addedTime: Int): Post@aws_subscribe(突变:[updatePost"])onDeletePost(id: ID, postContent: String, addedTime: Int): Post@aws_subscribe(突变:[deletePost"])}输入 TableBooleanFilterInput {ne:布尔值eq:布尔值}输入 TableCommentFilterInput {postID: TableIDFilterInput评论 ID:TableStringFilterInput内容:TableStringFilterInput添加时间:TableIntFilterInput}输入 TableFloatFilterInput {ne: 浮点数eq:浮点数le: 浮点数lt:浮动ge:浮动gt:浮动包含:浮动不包含:浮动之间:[浮动]}输入 TableIDFilterInput {新:身份证情商:身份证乐:身份证lt:身份证通用电气:身份证gt: 身份证包含:ID不包含:ID之间:[ID]开始于:ID}输入 TableIntFilterInput {ne: 诠释情商:诠释乐:诠释lt:诠释通用电气:诠释gt: 诠释包含:诠释不包含:整数之间:[诠释]}输入 TablePostFilterInput {id: TableIDFilterInputpostContent: TableStringFilterInput添加时间:TableIntFilterInput}输入表字符串过滤器输入 {ne:字符串eq:字符串乐:字符串lt:字符串ge:字符串gt:字符串包含:字符串不包含:字符串之间:[字符串]开始于:字符串}输入更新评论输入 {邮编:身份证!评论ID:字符串!内容:字符串添加时间:整数}输入更新后输入 {身份证:身份证!发布内容:字符串添加时间:整数}架构{查询:查询突变:突变订阅:订阅}

  1. (与选项 1 相关).在右侧面板中 AppSync 控制台的 Schema 页面上找到 Post 并在comments(...): CommentConnection"对面单击附加",将CommentTable"添加为源并在 VTL 中添加以下解析器代码:

请求映射模板中:

 #set( $startFromTime = $util.defaultIfNull($context.args.startFromTime, 0) ){版本":2017-02-28",操作":查询","index" : "postID-addedTime-index",询问" : {"表达式": "postID = :postID 和 addedTime > :startFrom",表达式值":{":postID" : { "S" : "$context.source.id" },":startFrom" : { "N" : "$startFromTime" }}},scanIndexForward":#if($context.args.sortDirection)#if( $context.args.sortDirection == "ASC" )真的#别的错误的#结尾#别的真的#结尾,#if( ${context.arguments.count} )"limit": ${context.arguments.count}#结尾#if( ${context.arguments.nextToken} ),"nextToken": "${context.arguments.nextToken}"#结尾}

响应映射模板中:

<代码>{项目":$utils.toJson($context.result.items)#if( ${context.result.nextToken} ),"nextToken": "${context.result.nextToken}"#结尾}

  1. (与选项 2 相关).在右侧面板中 AppSync 控制台的 Schema 页面上找到 Query 并在 'postCommentsByAddTime(...): PaginatedComments' 对面单击 'Attach',将 CommentTable 添加为数据源并在 VTL 中添加以下解析器代码:

请求映射模板中:

<代码>{版本":2017-02-28",操作":查询","index" : "postID-addedTime-index",询问" : {"表达式": "postID = :postID 和 addedTime > :startFrom",表达式值":{":postID" : { "S" : "${context.arguments.postID}" },":startFrom" : { "N" : "${context.arguments.startFromTime}" }}}#if( ${context.arguments.count} )"limit": ${context.arguments.count}#结尾#if( ${context.arguments.nextToken} ),"nextToken": "${context.arguments.nextToken}"#结尾}

响应映射模板中:

<代码>{项目":$utils.toJson($context.result.items)#if( ${context.result.nextToken} ),"nextToken": "${context.result.nextToken}"#结尾}

就是这样.

现在您可以使用以下所有查询:

查询 ListPosts {列表帖子{项目 {ID发布内容## 以下所有参数都可以为空评论(开始时间:121111112222,计数:4##默认的sortDirection是ASC,你可以这样改变## 排序方向:DESC) {项目 {邮政编号评论ID内容添加时间}}}}}查询GetPost {getPost(id:6548e596-d1ed-4203-a32f-52cfab8c9b20"){ID评论 (##您也可以添加所有三个或任何一个或一个都不添加## 排序方向:DESC,## startFromTime: 189283212122##计数:5) {项目 {邮政编号评论ID内容添加时间}}}}查询 GetCommentsByTime {postCommentsByAddTime(postID:6548e596-d1ed-4203-a32f-52cfab8c9b20",startFromTime:12423455352342,计数:2){项目 {邮政编号评论ID内容添加时间}}}

I am trying to create a structure that lists comments for a post with postId ordered w.r.t. their lastChangeTime descending.

The model in the schema is shared below.

type Comment {
  id: ID!
  postId: String!
  user: String!
  lastChangeTime: String
  commentBody: String
}

It has a backing DynamoDB table and generic CRUD resolvers for it already. And id field is the primary key in the table.

I plan to build a query as follows:

{
  "version": "2017-02-28",
  "operation" : "Query",
  "index" : "postId-index",
  "query" : {
    "expression": "post = :postId",
    "expressionValues" : {
      ":postId" : {
        "S" : "${ctx.args.postId}"
      }
    }
  },
  "limit": $util.defaultIfNull($ctx.args.first, 20),
  "nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.after, null)),
  "scanIndexForward": false
}

To make it work, how should I add the Global Secondary Index (GSI) on postId (i.e. postId-index)?

Should I add a sort key on lastChangeTime when defining it and it would be ok? Or lastChangeTime field requires its own separate index to be sorted through?

解决方案

It is easy. You can do it in two different ways or you can use both ways to have better flexibility. ( if you already resolved it I hope it will help someone else ).

Doing it this way you can set sortDirection dynamically using query arguments.

Detailed code is given below. Before that please note this point.

  1. First point is re your Comment type - you are using

    type Comment {
      id: ID!
      postId: String!
      ## rest of your type definition
    }
    

This is not the best way to set up your Comment type linked to a Post.

Better way is:

type Comment {
    postID: ID!         ## select this as Primary key in DataSource in AppSync console
    commentID: String!    ## select this as Sort key in DataSource in AppSync console
    ## rest of your type definition
}

If you do this your DynamoDB table will have structure similar to one shown below ( from this AWS webpage).

( In your case UserId would be PostId and GameTitle would be CommentID )

This way, because all comments would be recorded next to each other (under same PostId ) AppSync response time would be much faster.

In AppSync docs page they also used this example:

  1. As @Lisa M Shon mentioned you can initiate a GSI on CommentTable where PostId is partition key and addedTime is sort key . Call it 'postID-addedTime-index' if you want to use Resolvers provided below. MAKE SURE that you select 'Number' on addedTime in GSI.

Then in your schema you can define following types:

type Comment {
    postID: ID!
    commentID: String!
    content: String!
    addedTime: Int!
}

type CommentConnection {
    items: [Comment]
    nextToken: String
}

type Post {
    id: ID!
    postContent: String!
    addedTime: Int!
    ## Option 1. Gets Post details with all related Comments. 
    ## If 'startFromTime' is provided it will fetch all Comments starting from that timestamp.
    ## If 'startFromTime' is not provided it will fetch all Comments.
    comments(
        filter: TableCommentFilterInput,
        sortDirection: SortDirection,
        startFromTime: Int,
        limit: Int,
        nextToken: String
    ): CommentConnection
}

type Query {
    ## Option 2. It will fetch Comments only for a given PostId.
    ## If 'startFromTime' is provided it will fetch all Comments starting from that timestamp.
    ## If 'startFromTime' is not provided it will fetch all Comments.
    postCommentsByAddTime(
        postID: String!,
        startFromTime: Int!,
        sortDirection: SortDirection,
        filter: TableCommentFilterInput,
        count: Int,
        nextToken: String
    ): PaginatedComments
   ## your other queries
}

    ## rest of your schema definition

You can use both - Option 1 and Option 2 and use both.

Full schema code is here ( expand the snippet below ):

type Comment {
	postID: ID!
	commentID: String!
	content: String!
	addedTime: Int!
}

type CommentConnection {
	items: [Comment]
	nextToken: String
}

input CreateCommentInput {
	postID: ID!
	commentID: String!
	content: String!
	addedTime: Int!
}

input CreatePostInput {
	postContent: String!
	addedTime: Int!
}

input DeleteCommentInput {
	postID: ID!
	commentID: String!
}

input DeletePostInput {
	id: ID!
}

type Mutation {
	createComment(input: CreateCommentInput!): Comment
	updateComment(input: UpdateCommentInput!): Comment
	deleteComment(input: DeleteCommentInput!): Comment
	createPost(input: CreatePostInput!): Post
	updatePost(input: UpdatePostInput!): Post
	deletePost(input: DeletePostInput!): Post
}

type PaginatedComments {
	items: [Comment!]!
	nextToken: String
}

type Post {
	id: ID!
	postContent: String!
	addedTime: Int!
	comments(
		filter: TableCommentFilterInput,
		sortDirection: SortDirection,
		startFromTime: Int,
		limit: Int,
		nextToken: String
	): CommentConnection
}

type PostConnection {
	items: [Post]
	nextToken: String
}

type Query {
	getComment(postID: ID!, commentID: String!): Comment
	listComments(filter: TableCommentFilterInput, limit: Int, nextToken: String): CommentConnection
	getPost(id: ID!): Post
	listPosts(filter: TablePostFilterInput, limit: Int, nextToken: String): PostConnection
	postCommentsByAddTime(
		postID: String!,
		startFromTime: Int!,
		sortDirection: SortDirection,
		filter: TableCommentFilterInput,
		count: Int,
		nextToken: String
	): PaginatedComments
}

enum SortDirection {
	ASC
	DESC
}

type Subscription {
	onCreateComment(
		postID: ID,
		commentID: String,
		content: String,
		addedTime: Int
	): Comment
		@aws_subscribe(mutations: ["createComment"])
	onUpdateComment(
		postID: ID,
		commentID: String,
		content: String,
		addedTime: Int
	): Comment
		@aws_subscribe(mutations: ["updateComment"])
	onDeleteComment(
		postID: ID,
		commentID: String,
		content: String,
		addedTime: Int
	): Comment
		@aws_subscribe(mutations: ["deleteComment"])
	onCreatePost(id: ID, postContent: String, addedTime: Int): Post
		@aws_subscribe(mutations: ["createPost"])
	onUpdatePost(id: ID, postContent: String, addedTime: Int): Post
		@aws_subscribe(mutations: ["updatePost"])
	onDeletePost(id: ID, postContent: String, addedTime: Int): Post
		@aws_subscribe(mutations: ["deletePost"])
}

input TableBooleanFilterInput {
	ne: Boolean
	eq: Boolean
}

input TableCommentFilterInput {
	postID: TableIDFilterInput
	commentID: TableStringFilterInput
	content: TableStringFilterInput
	addedTime: TableIntFilterInput
}

input TableFloatFilterInput {
	ne: Float
	eq: Float
	le: Float
	lt: Float
	ge: Float
	gt: Float
	contains: Float
	notContains: Float
	between: [Float]
}

input TableIDFilterInput {
	ne: ID
	eq: ID
	le: ID
	lt: ID
	ge: ID
	gt: ID
	contains: ID
	notContains: ID
	between: [ID]
	beginsWith: ID
}

input TableIntFilterInput {
	ne: Int
	eq: Int
	le: Int
	lt: Int
	ge: Int
	gt: Int
	contains: Int
	notContains: Int
	between: [Int]
}

input TablePostFilterInput {
	id: TableIDFilterInput
	postContent: TableStringFilterInput
	addedTime: TableIntFilterInput
}

input TableStringFilterInput {
	ne: String
	eq: String
	le: String
	lt: String
	ge: String
	gt: String
	contains: String
	notContains: String
	between: [String]
	beginsWith: String
}

input UpdateCommentInput {
	postID: ID!
	commentID: String!
	content: String
	addedTime: Int
}

input UpdatePostInput {
	id: ID!
	postContent: String
	addedTime: Int
}

schema {
	query: Query
	mutation: Mutation
	subscription: Subscription
}

  1. (related to Option 1). On Schema page of AppSync console in right hand side panel find Post and opposite to 'comments(...): CommentConnection' click 'Attach', add 'CommentTable' as source and add following Resolver code in VTL:

In request mapping template:

    #set( $startFromTime = $util.defaultIfNull($context.args.startFromTime, 0) )
    {
        "version" : "2017-02-28",
        "operation" : "Query",
        "index" : "postID-addedTime-index",
        "query" : {
          "expression": "postID = :postID and addedTime > :startFrom",
            "expressionValues" : {
              ":postID" : { "S" : "$context.source.id" },
              ":startFrom" : { "N" : "$startFromTime" }
            }
        },
        "scanIndexForward":   #if( $context.args.sortDirection )
          #if( $context.args.sortDirection == "ASC" )
              true
          #else
              false
          #end
        #else
            true
        #end,

        #if( ${context.arguments.count} )
            ,"limit": ${context.arguments.count}
        #end
        #if( ${context.arguments.nextToken} )
            ,"nextToken": "${context.arguments.nextToken}"
        #end
    }

In response mapping template:

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

  1. (related to Option 2). On Schema page of AppSync console in right hand side panel find Query and opposite to 'postCommentsByAddTime(...): PaginatedComments' click 'Attach', add CommentTable as data source and add following Resolver code in VTL:

In request mapping template:

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "index" : "postID-addedTime-index",
    "query" : {
      "expression": "postID = :postID and addedTime > :startFrom",
        "expressionValues" : {
          ":postID" : { "S" : "${context.arguments.postID}" },
          ":startFrom" : { "N" : "${context.arguments.startFromTime}" }
        }
    }
    #if( ${context.arguments.count} )
        ,"limit": ${context.arguments.count}
    #end
    #if( ${context.arguments.nextToken} )
        ,"nextToken": "${context.arguments.nextToken}"
    #end
}

In response mapping template:

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

That is it.

Now you can use all of following queries:

query ListPosts {
  listPosts{
    items {
      id
      postContent
      ## all below arguments are nullable
      comments(startFromTime: 121111112222, count: 4
      ## default sortDirection is ASC, you can change it this way
      ## sortDirection: DESC
    ) {
        items {
          postID
          commentID
          content
          addedTime
        }
      }
    }
  }
}

query GetPost {
  getPost(id: "6548e596-d1ed-4203-a32f-52cfab8c9b20") {
    id
    comments (
    ## you can also add all three or any or none of these
    ## sortDirection: DESC,
    ## startFromTime: 189283212122
    ## count: 5
    ) {
        items {
        postID
        commentID
        content
        addedTime
      }
  }
  }
}

query GetCommentsByTime {
  postCommentsByAddTime(postID: "6548e596-d1ed-4203-a32f-52cfab8c9b20", startFromTime: 12423455352342, count: 2) {
    items {
      postID
      commentID
      content
      addedTime
    }
  }
}

这篇关于AWS AppSync - 将全局二级索引添加到 DynamoDB 并使用 GSI 排序键进行分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆