6

I am trying to build an AWS AppSync query with a list using IN:

{
    "version" : "2017-02-28",
    "operation" : "Query",
    "index" : "my-index",
    "query" : {
        "expression": "id IN :ids",
        "expressionValues" : { ":ids" : $util.dynamodb.toStringSet($ctx.args.ids) }
    },
    "limit": $util.defaultIfNull(${ctx.args.first}, 20),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($ctx.args.after, null))
}

However, trying it out with parameters like:

query ListItemsWithIdList {
  listItemsWithIdList(first:20, ids: ["id1", "id2"]) {
    items {
      id
    }
    nextToken
  }
}

It throws an error:

Unable to parse the JSON document: 'Unexpected character ('S' (code 83)): was expecting double-quote to start field name
at [Source: (String)\"{
    \"version\" : \"2017-02-28\",
    \"operation\" : \"Query\",
    \"index\" : \"my-index\",
    \"query\" : {
        \"expression\": \"id IN :ids\",
        \"expressionValues\" : { \":ids\" : {SS=[id1, id2]} }
    },       
    \"limit\": 20,
    \"nextToken\": null
}\"; line: 7, column: 47]'"

It seems OK to use IN for query comparison operator; however, how can I pass a String List as a parameter and fetch the results whose IDs are among those parameters supplied?

EDIT: Corrected variable name typo.

vahdet
  • 6,357
  • 9
  • 51
  • 106

3 Answers3

5

I don't think AWS AppSync support IN just for now. I try to test your scenario and come up with a solution using contains() function.

enter image description here

Here is the result after query:

enter image description here

Another alternative solution is to use Scan (not recommended)

{
    "version" : "2017-02-28",
    "operation" : "Scan",
    "filter" : {
        "expression": "contains (:authors, author)",
        "expressionValues" : {
            ":authors" : $util.dynamodb.toDynamoDBJson($ctx.args.authors)
        }
    }
}

Btw, AWS AppSync support BatchGetItem operation. You can pass a list of keys in a single query and return the results from a table.
Reference: https://docs.aws.amazon.com/appsync/latest/devguide/tutorial-dynamodb-batch.html
Here is an example and I tested it worked like charm:

## REQUEST MAPPING
#set($authors = [])
#foreach( $author in $ctx.args.authors )
    #set($map = {})
    $util.qr($map.put("author", $util.dynamodb.toString($author)))
    $util.qr($authors.add($map))
#end

{
    "version" : "2018-05-29",
    "operation" : "BatchGetItem",
    "tables" : {
        "tbDiary": {
            "keys": $util.toJson($authors),
            "consistentRead": true
        }
    }
}

## RESPONSE MAPPING
$util.toJson($ctx.result.data.tbDiary)
KoingDev
  • 620
  • 6
  • 12
  • Thanks for the approach, but I avoid using `filter` section as it may corrupt the results when used with *pagination*. Did you try `contains` in **query expression** i.e. just where `author = :author` in your first snippet. – vahdet Sep 25 '18 at 18:22
  • @vahdet Hope it is useful! – KoingDev Sep 26 '18 at 01:55
  • `BatchGetItem` looks decent, but I query the performance. Gonna test it. For pagination, it uses a different approach btw: UnprocessedKeys – vahdet Sep 26 '18 at 16:46
0

I think there could be 2 issues here:

  • Your arguments in the listItemsWithIdList query accepts an argument named userIds. You have $ctx.args.ids) in your Resolver template. You need use the same argument name in both places.

  • When you use $util.dynamodb.toStringSet in your Mapping Template, like you see in the error, it gets transformed to { \":ids\" : {SS=[id1, id2]} }. However, you want the id values to be contained within quotes. AWS AppSync provides another utility method for this purpose. You can change your template to use $util.dynamodb.toStringSetJson, which then gets converted to { \":ids\" : {SS=[\"id1\", \"id2\"]} }.

Let me know if this resolved your issue. Here is the reference to the Resolver Mapping Template Utilities.

Shankar Raju
  • 4,356
  • 6
  • 33
  • 52
  • Thanks, I removed the ambiguation caused by userIds / ids from the question. Tried `$util.dynamodb.toStringSetJson` but then the error became `Invalid KeyConditionExpression: Syntax error; token: \":userIds\", near: \"IN :userIds`. I guess `IN` is not allowed for query, this [link](https://stackoverflow.com/a/32751240/4636715) also tells about it. – vahdet Sep 25 '18 at 06:59
0

Hopefully you have already resolved it. However, there are two issues in your setup:

This has to be a filter expression and since you don't have a key condition, this may even have to be a scan instead of a query

Then there are syntax issues (which is what the error is really because of): In the EXPRESSION, add parentheses to the variable "expression": "id IN (:ids"),

And in EXPRESSION VALUES use toStringSetJson instead of toStringSet "expressionValues" : { ":ids" : $util.dynamodb.toStringSetJson($ctx.args.ids) }

Hope this helps.

  • Thanks, the reason I avoided `Scan` was its `filter` is applied after the pagination (see the usage of `limit` and `nextToken` in the OP). You cannot decently get first, let's say 5 results, it may return 3 out of total 10 with `Scan`. – vahdet Dec 20 '18 at 12:58