Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

amazon web services - AWS AppSync - Adding Global Secondary Index to DynamoDB and pagination using GSI sort key

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 )

enter image description here

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:

enter image description here

  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.

enter image description here

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
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...