amazon-web-services - AWS AppSync-使用GSI排序键将全局二级索引添加到DynamoDB和分页

标签 amazon-web-services amazon-dynamodb aws-appsync

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

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

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

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

我计划建立一个查询,如下所示:
{
  "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
}

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

定义它时,是否应该在lastChangeTime上添加一个排序键,可以吗?还是lastChangeTime字段需要通过其自己的单独索引进行排序?

最佳答案

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

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

下面给出了详细的代码。在此之前,请注意这一点。

  • 第一点是您的评论类型-您正在使用
    type Comment {
      id: ID!
      postId: String!
      ## rest of your type definition
    }
    

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

    更好的方法是:
    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
    }
    

    如果执行此操作,则DynamoDB表的结构将类似于下面显示的结构(from this AWS webpage)。

    (在您的情况下,UserId将为PostId,而GameTitle将为CommentID)

    enter image description here

    这样,因为所有注释都将彼此相邻记录(在同一PostId下),因此AppSync响应时间会更快。

    AppSync docs page中,他们还使用了以下示例:

    enter image description here
  • 正如@Lisa M Shon所述,您可以在CommentTable上启动GSI,其中PostId是分区键,而addTime是排序键。如果要使用下面提供的解析器,则称其为“postID-addedTime-index”。确保在GSI中的addedTime上选择“Number”。

  • enter image description here

    然后,您可以在架构中定义以下类型:
    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
    

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

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

    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相关)。在右侧面板的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 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
        }
    

    在响应映射模板中:
    {
        "items": $utils.toJson($context.result.items)
        #if( ${context.result.nextToken} )
            ,"nextToken": "${context.result.nextToken}"
        #end
    }
    
  • (与选项2相关)。在右侧面板的AppSync控制台的“架构”页面上,找到“查询”并与“postCommentsByAddTime(...):PaginatedComments”相对,单击“附加”,添加CommentTable作为数据源,并在VTL中添加以下解析器代码:

  • 在请求映射模板中:
    {
        "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
    }
    

    在响应映射模板中:
    {
        "items": $utils.toJson($context.result.items)
        #if( ${context.result.nextToken} )
            ,"nextToken": "${context.result.nextToken}"
        #end
    }
    

    这就对了。

    现在,您可以使用以下所有查询:
    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
        }
      }
    }
    

    关于amazon-web-services - AWS AppSync-使用GSI排序键将全局二级索引添加到DynamoDB和分页,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52791010/

    相关文章:

    amazon-web-services - 当对象 acl 为公共(public)时,如何将 S3 存储桶策略设置为(大部分)私有(private)?

    mysql - 使用 Wordpress 从 docker 容器连接到 RDS Mysql

    typescript - 如何将 Typescript AppSync 解析器捆绑到 CDK 中?

    java - 从 dynamoDB 返回更新的项目

    graphql - 无法将大型 graphql 架构上传到 aws appsync

    amazon-web-services - 即使离线被禁用,AWS AppSync 查询也会返回缓存的响应

    linux - Amazon EC2 实例可用磁盘空间差异

    mysql - AWS RDS MySQL Bin 更改日志

    amazon-web-services - 如何在 AWS Dynamo 中获取列的唯一值?

    amazon-dynamodb - Dynamodb 读写单元