0

Im using GraphQL and DynamoDB with the adjacency list pattern, in that I am only using one table.

Say I have the schema -

type GrandParent {
   id: ID!
   children: [Parent]
}

type Parent {
   id: ID!
   children: [Child]
}

type Child {
   id: ID!
}

type Query {
   getGrandParent(id: ID): GrandParent
}

For dynamodb I have the following primary key structure -

for Grandparent pk = GRANDPARENT_<id>, sk = GRANDPARENT

for Parent pk = PARENT_<ID>, sk = GRANDPARENT_<id>

for Child pk = CHILD_<id>, sk = PARENT_<id>

with a GSI that will invert the keys.

So, given a Grandparent ID, I can find all the Parents using the GSI, as I know the Grandparent ID, so I can do - where sk = GRANDPARENT_<id> and pk begins with PARENT_ID which will bring me back all the Parents, but then how can I get the IDs of these parents and now go and get all the Child records? Is it possible to do multiple trips to the DB in one graphql request?

andy mccullough
  • 9,070
  • 6
  • 32
  • 55
  • Better use [Amazon Neptune](https://aws.amazon.com/neptune/) for relationships. Neptune is a Graph DB while DynamoDB is a key-value DB. – Joseph D. May 20 '20 at 16:24
  • I can see why you are thinking to use graph. Though this is a bit of a one off problem I'm hitting on an existing app, unfortunately I can't change db – andy mccullough May 20 '20 at 16:36
  • is this a good case for a pipeline resolver? – andy mccullough May 20 '20 at 17:13
  • yes it is. other way is to have a [nested attributes with args](https://graphql.org/learn/queries/#arguments). AFAIR, AppSync is capable of this. – Joseph D. May 20 '20 at 17:20
  • 1
    If I have understood your question correctly, you can attach a resolver with children of Parent type and write a query to get children where id = ctx.source.id (which is Parent ID). So in this way, there will be only one request with multiple DB transactions. – Myz May 20 '20 at 17:23
  • 1
    no problemwith one query (essential feature of graphql) `...getGrandParent(id: $id) { id children { id children { id ...` ... problem is not too many db requests in each level resolver to return all data ... https://github.com/graphql/dataloader – xadm May 20 '20 at 18:34
  • @Moiz I can see how I can get the Parent objects as they are retrieved using the grandparent id as the sort key, which I know at the start of the query, but I need to get the parent results back, to know their id in order to now go and get their children, this time using the parent id as the sort key, is that still possible in one query? – andy mccullough May 21 '20 at 08:08
  • and that was to @xadm aswell ^^^ – andy mccullough May 21 '20 at 08:10
  • 1
    https://stackoverflow.com/a/60756115/6124657 ... https://www.youtube.com/watch?v=ld2_AS4l19g – xadm May 21 '20 at 09:50
  • thanks guys, piecing all your comments / links together I think i've managed to solve it, I'll update the question with the solution. @xadm the video was also useful for highlighting the N+1 problem, which I almost hit myself as I was going to revert to using a lambda resolver which would ALWAYS query each level of the tree, which would have been very bad... Really appreciate the help! – andy mccullough May 21 '20 at 11:03

1 Answers1

0

Credit to the guys in the comments - piecing together their recommendations I was able to figure it out. My real sticking point was not knowing how to get the values from the resolved parent, using the AppSync resolver mapping template for DynamoDB I was able to use #ctx.source.id to get the parent id and therefore I could build by query correctly as -

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "query" : {
        "expression" : "#sk = :sk and begins_with(#pk, :pk)",
        "expressionNames" : {
            "#pk" : "pk",
            "#sk" : "sk"
        },
        "expressionValues" : {
            ":pk" : $util.dynamodb.toDynamoDBJson("CHILD"),
            ":sk" : $util.dynamodb.toDynamoDBJson("PARENT_$context.source.id")
       }
    },
    "index": "invertedIndex"
}
andy mccullough
  • 9,070
  • 6
  • 32
  • 55
  • resolved parent is usually passed as 1st resolver argument – xadm May 21 '20 at 11:22
  • standard resolver args `(parent_or_root, args, context, info)` - passing input/data to nested (child) resolver explained in link to SO, beside YT – xadm May 21 '20 at 15:33