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

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

问题描述

我正在尝试创建一种结构,该结构列出对带有 postId 且有w.r.t排序的帖子的评论。他们的 lastChangeTime 降序。



模式中的模型在下面共享。

 类型评论{
id:ID!
postId:字符串!
用户:字符串!
lastChangeTime:字符串
commentBody:字符串
}

它具有支持DynamoDB表和适用于该表的通用CRUD解析器。
id 字段是表中的主键。



我打算将查询构建为

  {
version: 2017-02-28,
operation :查询,
索引: postId-index,
查询:{
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
}

要使其正常工作,我应该如何添加 postId (即 postId-index )上的全球二级索引(GSI)



我应该在定义时在 lastChangeTime 上添加排序键 ?还是 lastChangeTime 字段需要对其单独的索引进行排序?

解决方案

很容易。您可以通过两种不同的方式来做到这一点,也可以同时使用两种方式来获得更好的灵活性。 (如果您已经解决了,我希望它会对其他人有所帮助。)



通过这种方式,您可以使用查询参数动态设置sortDirection。



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


  1. 第一点是您的注释类型-您正在使用

     类型注释{
    id:ID!
    postId:字符串!
    ##类型定义的其余部分
    }


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



更好的方法是:

 类型注释{
postID:ID! ##在AppSync控制台
的DataSource中将其选择为主键。 ##在AppSync控制台的$ Data中将其选择为排序键
##类型定义的其余部分
}

如果执行此操作,则DynamoDB表的结构将类似于以下所示(



通过这种方式,因为所有注释都将彼此相邻记录(在相同的PostId下)AppSync响应时间会更快。




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



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

  type Comment {
postID:ID!
commentID:字符串!
内容:字符串!
添加时间:整数!
}

类型CommentConnection {
项目:[Comment]
nextToken:字符串
}

类型Post {
id:ID!
postContent:字符串!
添加时间:整数!
##选项1.获取带有所有相关注释的帖子详细信息。
##如果提供了 startFromTime,它将从该时间戳开始获取所有评论。
##如果未提供 startFromTime,它将获取所有评论。
条评论(
过滤器:TableCommentFilterInput,
sortDirection:SortDirection,
startFromTime:Int,
限制:Int,
nextToken:字符串
) :CommentConnection
}

类型Query {
##选项2。它将仅获取给定PostId的注释。
##如果提供了 startFromTime,它将从该时间戳开始获取所有评论。
##如果未提供 startFromTime,它将获取所有评论。
postCommentsByAddTime(
postID:String !,
startFromTime:Int !,
sortDirection:SortDirection,
filter:TableCommentFilterInput,
count:Int,
nextToken:字符串
):PaginatedComments
##您的其他查询
}

##模式定义的其余部分

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



完整模式代码在此处(展开下面的代码片段):

  type Comment {postID:ID! commentID:字符串!内容:字符串! addTime:Int!}类型CommentConnection {项目:[Comment] nextToken:字符串} input CreateCommentInput {postID:ID! commentID:字符串!内容:字符串! addTime:Int!} input CreatePostInput {postContent:字符串! addTime:Int!}输入DeleteCommentInput {postID:ID! commentID:字符串!}输入DeletePostInput {id:ID!}类型变异{createComment(输入:CreateCommentInput!):评论updateComment(输入:UpdateCommentInput!):评论deleteComment(输入:DeleteCommentInput!):评论createPost(输入:CreatePostInput!) :发布updatePost(input:UpdatePostInput!):张贴deletePost(input:DeletePostInput!):张贴}类型PaginatedComments {项目:[评论!]! nextToken:字符串}类型Post {id:ID! postContent:字符串! addTime:整数! comments(filter:TableCommentFilterInput,sortDirection:SortDirection,startFromTime:Int,limit:Int,nextToken:String):CommentConnection} type PostConnection {项目:[Post] nextToken:String} type Query {getComment(postID:ID !, commentID:String !):评论listComments(过滤器:TableCommentFilterInput,限制:Int,nextToken:字符串):CommentConnection getPost(id:ID!):发表listPosts(过滤器:TablePostFilterInput,限制:Int,nextToken:字符串):PostConnection postCommentsByAddTime(postID:字符串!,startFromTime:Int !, sortDirection:SortDirection,过滤器:TableCommentFilterInput,计数:Int,nextToken:String):PaginatedComments}枚举SortDirection {ASC DESC} type Subscription {onCreateComment(postID:ID,commentID:String,content:String, :Int):Comment @aws_subscribe(mutations:[ createComment])onUpdateComment(postID:ID,commentID:字符串,content:字符串,addTime:Int):Comment @aws_subscribe(mutations:[ updateComment])onDeleteComm ent(postID:ID,commentID:字符串,内容:字符串,addedTime:Int):Comment @aws_subscribe(mutations:[ deleteComment])onCreatePost(id:ID,postContent:String,addedTime:Int):发表@aws_subscribe(变异:[ createPost])onUpdatePost(id:ID,postContent:字符串,addedTime:Int):Post @aws_subscribe(变异:[ updatePost])onDeletePost(id:ID,postContent:字符串,addedTime:Int): Post @aws_subscribe(mutations:[ deletePost])}输入TableBooleanFilterInput {ne:布尔值eq:布尔值} input TableCommentFilterInput {postID:TableIDFilterInput注释ID:TableStringFilterInput内容:TableStringFilterInput输入时间:TableIntFilterInput}输入TableFloatFilterInput {ne:浮点数:浮点数:浮点数:浮点数ge:浮点数gt:浮点数包含:浮点数不包含:浮点数之间:[Float]} input TableIDFilterInput {ne:ID eq:ID le:ID lt:ID ge:ID gt:ID contains:ID notContains: :[ID] startsWith:ID} input TableIntFilterInput {ne:Int eq:Int le:Int lt:Int ge:Int g t:Int包含:Int not包含:Int之间:[Int]}输入TablePostFilterInput {id:TableIDFilterInput postContent:TableStringFilterInput添加时间:TableIntFilterInput}输入TableStringFilterInput {ne:字符串eq:字符串le:字符串lt:字符串ge:字符串gt:字符串包含:字符串不包含:字符串之间:[String] startsWith:字符串} input UpdateCommentInput {postID:ID! commentID:字符串!内容:字符串添加时间:整数}输入UpdatePostInput {id:ID! postContent:字符串添加时间:Int}模式{查询:查询突变:突变订阅:订阅}  


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

请求映射模板中:

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

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

响应中映射模板:

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




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

请求映射模板中:

  {
version: 2017-02-28,
operation: Query,
index: postID-addedTime-index,
query:{
expression: postID =:postID和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
}

响应映射模板中:

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

就是这样。



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

  query ListPosts {
listPosts {
个项目{
id
postContent
##以下所有参数均可为空
注释(startFromTime:121111112222,计数:4
##默认sortDirection为ASC,您可以更改此值方式
## sortDirection:DESC
){
项{
postID
commentID
内容
添加时间
}
}
}
}
}

查询GetPost {
getPost(id: 6548e596-d1ed-4203-a32f-52cfab8c9b20){
id
评论(
##您可以还应添加以下所有
## sortDirection:DESC,
## startFromTime:189283212122
##计数中的全部三个或任何一个或不添加:#
项{
postID
commentID
内容
addTime
}
}
}
}

查询GetCommentsByTime {
postCommentsByAddTime(postID: 6548e596-d1ed-4203-a32f-52cfab8c9b20,startFromTime:12423455352342,count:2){
项目{
postID
commentID
内容
添加时间
}
}
}


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-使用GSI排序键将全局二级索引添加到DynamoDB和分页的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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