【问题标题】:AWS AppSync - Adding Global Secondary Index to DynamoDB and pagination using GSI sort keyAWS AppSync - 将全局二级索引添加到 DynamoDB 并使用 GSI 排序键进行分页
【发布时间】:2019-03-18 08:42:39
【问题描述】:

我正在尝试创建一个结构,该结构列出了带有postId 的帖子的 cmets,并按 w.r.t. 排序。他们的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 字段需要自己单独的索引才能排序?

【问题讨论】:

  • 你可以这样做。在 postId 上创建 GSI,在 lastChangTime 上创建排序键。 AppSync 控制台上就是这种关系的一个示例。你可以看看Start from a sample project: Event App
  • 你有那个示例项目的链接吗?
  • 它在 AppSync 控制台中可用。
  • 好的,谢谢指导;但它是 AppSync 的起始模板,为了查看它,我显然要创建所有 DynamoDB 解析器。这并不简单。
  • 如果您浏览起始模板,它将为您创建所有 dynamodb 表和解析器。它将创建一个完整的工作示例。

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


【解决方案1】:

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

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

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

  1. 第一点是您的评论类型 - 您正在使用

    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 )

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

AppSync docs page 他们也使用了这个例子:

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

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

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 并同时使用。

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

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. (与选项 1 相关)。在右侧面板中 AppSync 控制台的 Schema 页面上,找到 Post 并在“cmets(...): 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
}
  1. (与选项 2 相关)。在右侧面板中 AppSync 控制台的 Schema 页面上找到 Query 并在 'postCommentsByAddTime(...): PaginatedComments' 对面单击 'Attach',将 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
    }
  }
}

【讨论】:

  • 感谢您发布此信息 - 澄清分散在各种 AWS 文档中的内容非常有帮助!
  • 发布此答案多年后,它仍然具有相关性并极大地帮助了我,谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-20
  • 2014-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多