- Newest
- Most votes
- Most comments
The following statements in the VTL are relevant:
#set( $filter = $ctx.args.filter )
#set( $path = $filter.data )
...
"filter" : $util.transform.toDynamoDBFilterExpression($path)
Now, the filter passed in the query is:
{
"sub" : {
"eq" : "new-york/manhattan/listings"
},
"data": {
"beds": {
"eq": 2.0
}
}
}
Here, $path is the value of "data", ie {"beds":{"eq": 2.0}}
But, the object in the table is :
"data": {
"agent": [
{
"agentID": "daeo@gmail.com"
},
{
"agentID": "ben@gmail.com"
}
],
"amenities": [
"hot tub",
"time machine"
],
"baths": 2,
"beds": 2
}
The parent property "data" is missing in the query created by the VTL. Therefore, it does not match anything returned by the query and the result is null. You can try to add the parent node "data".
After some more fiddling, I happened upon the solution implied here: https://stackoverflow.com/questions/55132782/appsync-graphql-how-to-filter-a-list-by-nested-value. Following from what I could glean about solution, I successfully implemented a hardcoded nested query filter using the following VTL request resolver (and changing the filter expression keyname to avoid a reserved word conflict on data`):
#set( $filter = $ctx.args.filter )
#set( $path = $filter.filterData ) ## currently, unused
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "listings-index",
"query" : {
"expression": "#status = :status and #sub = :sub",
"expressionNames" : {
"#status" : "status",
"#sub" : "sub"
},
"expressionValues" : {
":status" : $util.dynamodb.toDynamoDBJson("Active"),
":sub" : $util.dynamodb.toDynamoDBJson($filter.sub.eq)
}
},
"filter" : {
"expression" : "#filterData.beds = :beds",
"expressionValues" : {
":beds" : $util.dynamodb.toDynamoDBJson(2.0)
}
},
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}
This returns my expected result:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [
{
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active"
}
],
"nextToken": null
}
}
}
Update #2 from 9/17/19
After further playing around with the request resolver, I think I've found a quick and dirty way to dynamically grab the path and target vars for creating a filter expression for my nested attributes. Note: The whole thing still returns an empty result set and it assumes there's only one filter key (for now), but the reserved keyword bit seems to have been solved. Still wondering why the results aren't showing up as expected though.
#set( $filter = $ctx.args.filter )
#foreach( $parent in $filter.keySet() )
#set( $path = $parent )
#end
#set( $target = $filter[$path] )
#foreach( $ff in $target.keySet() ) ## should only contain one Map key-value pair
#set( $fp = $ff )
#end
#set( $fv = $target[$fp] )
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "listings-index",
"query" : {
"expression": "#status = :status and #sub = :sub",
"expressionNames" : {
"#status" : "status",
"#sub" : "sub"
},
"expressionValues" : {
":status" : $util.dynamodb.toDynamoDBJson("Active"),
":sub" : $util.dynamodb.toDynamoDBJson($filter.sub.eq)
}
},
"filter" : {
"expression" : "#ffp = :$fp", ## filter path parent.target = :target
"expressionNames" : {
"#ffp" : "${path}.${fp}"
},
"expressionValues" : {
":$fp" : $util.dynamodb.toDynamoDBJson(${fv.eq}), ## :target : value to filter for
}
},
"limit": $util.defaultIfNull($ctx.args.limit, 200),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}
Inspecting the CloudWatch log transformedTemplate shows the expression names and values are being substituted appropriately:
"filter" : {
"expression\" : "#ffp = :beds",
"expressionNames" : {
"#ffp" : "data.beds"
},
"expressionValues" : {
":beds" : { "N": 2.0 }
}
}
"Parent" category of beds (data) is present in the VTL query as far as I can tell.
Update from 09/18/19
I may have finally discovered the root of the problem: it seems that the way in which expressionNames are evaluated does not allow for a key to be a docpath. If I run either of the following filters (notice the use of a non-reserved DynamoDB keyword to illustrate the problem is with expression name substitution), I'll get the result I'm looking for:
"filter" : {
"expression" : "filterData.beds = :beds", ## filter path parent.target = :target
"expressionValues" : {
":beds" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
or
"filter" : {
"expression" : "filterData.beds = :${fp}", ## filter path parent.target = :target
"expressionValues" : {
":{fp}" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
Now, if I make a minor change, only attempting to substitute with an expression name value
"filter" : {
"expression" : "#filterData.beds = :${fp}", ## filter path parent.target = :target
"expressionNames": {
"#filterData.beds" : "filterData.beds"
},
"expressionValues" : {
":{fp}" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
I get the following error message:
"ExpressionAttributeNames contains invalid key: Syntax error; key: \"#filterData.beds\" (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException"
Even with a hardcoded path substitution, VTL seems to read the path as a single key name. Same issue when swapping the values of the expressions dynamically so there're no hardcoded strings.
SOLVED
I happened upon this https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html#Expressions.ExpressionAttributeNames.AttributeNamesContainingDots by accident, and it gave me the little bit extra I needed to find a workable solution with dynamic key names!
Here's what the filter expression looks like now:
"filter" : {
"expression" : "#path.#filter = :${fp}", ## filter path parent.target = :target
"expressionNames": {
"#path" : "${path}",
"#filter" : "${fp}"
},
"expressionValues" : {
":${fp}" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
The hold up here was that while expression attribute names are generally interpreted as document paths, with the introduction of the substituted name, the interpreter treats the key name as a scalar attribute and NOT as a document path. You need to individually identify the path elements and substitute for each.
Relevant content
- asked 3 years ago
- asked 5 years ago
- asked 4 months ago
- AWS OFFICIALUpdated 3 years ago
- AWS OFFICIALUpdated 3 years ago
- AWS OFFICIALUpdated 3 years ago