如何在 AWS AppSync 中按嵌套字段的查询进行过滤
How to Filter by Query on Nested Fields in AWS AppSync
问题和预期结果
我正在使用概念验证模式和 DynamoDB Table 设置来过滤嵌套字段值。我非常普遍地遵循了这些想法 here as well as the documentation for $utils.transform.toDynamoDBFilterExpression
(here).
基本思想是这样的:使用相同的原则,我想按任意深度的嵌套字段(DynamoDB 中的 32 个文档路径长度限制不足)进行过滤。相关设置如下所示:
AppSync 架构(对命名约定表示歉意;应该是一个快速而肮脏的 PoC):
query {
listActiveListingsBySubAndFilter(
filter: TableTestMasterDataTable_ImportV1FilterInput!,
limit: Int,
nextToken: String
): TestMasterDataTable_ImportV1Connection
}
input TableBooleanFilterInput {
ne: Boolean
eq: Boolean
}
input TableDataObjectFilterInput {
beds: TableFloatFilterInput
baths: TableFloatFilterInput
}
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 TableStringFilterInput {
ne: String
eq: String
le: String
lt: String
ge: String
gt: String
contains: String
notContains: String
between: [String]
beginsWith: String
}
input TableTestMasterDataTable_ImportV1FilterInput {
id: TableStringFilterInput
status: TableStringFilterInput
sub: TableStringFilterInput
data: TableDataObjectFilterInput
}
type TestMasterDataTable_ImportV1 {
id: String!
status: String!
sub: String!
data: AWSJSON
}
type TestMasterDataTable_ImportV1Connection {
items: [TestMasterDataTable_ImportV1]
nextToken: String
}
input UpdateTestMasterDataTable_ImportV1Input {
id: String!
status: String
sub: String!
data: AWSJSON
}
VTL 请求和响应解析器:
## Request resolver
#set( $filter = $ctx.args.filter )
#set( $path = $filter.data )
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "listings-index", ## GSI on table with HASH: status, RANGE: sub
"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" : $util.transform.toDynamoDBFilterExpression($path),
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}
## Response resolver
{
"items": $util.toJson($ctx.result.items),
"nextToken": $util.toJson($util.defaultIfNullOrBlank($context.result.nextToken, null))
}
示例 DynamoDB Table 元素:
{
"_meta": {
"exposure": 0.08,
"lastActive": 1557800000,
"lastUpdated": 1557878400,
"lastView": 1557878500,
"numViews": 63,
"posted": 1557878400
},
"buildingID": "325-5th-Ave,-New-York,-NY-10016,-USA",
"data": {
"agent": [
{
"agentID": "daeo@gmail.com"
},
{
"agentID": "ben@gmail.com"
}
],
"amenities": [
"hot tub",
"time machine"
],
"baths": 2,
"beds": 2
},
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active",
"sub": "new-york/manhattan/listings",
"unitNum": "37C",
"unitRefID": "325-5th-Ave,-New-York,-NY-10016,-USA#37C"
}
基于所有这些,如果我 运行 以下查询:
listActiveListingsBySubAndFilter(filter: {
"sub" : {
"eq" : "new-york/manhattan/listings"
},
"data": {
"beds": {
"eq": 2.0
}
}) {
items {
id
status
}
nextToken
}
我希望在 return 中得到这样的东西:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [
{
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active"
}
],
"nextToken": null
}
}
}
注意:这是唯一预期的 return,因为此时数据库中只有一项符合这些要求。
实际结果
综上所述,我得到的(或没有得到的)结果没有多大意义。无论查询(data.beds
、data.baths
),如果字段嵌套在data
中,return是相同的:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [],
"nextToken": null
}
}
}
我已验证查询按预期工作并且过滤器表达式的格式正确(它适用于其他非嵌套字段,如 id
)。令人困惑的是过滤器似乎没有被应用(或者可能以某种非直观的方式应用?)。作为参考,以下是上述内容的典型 CloudWatch 日志片段:
{
"context": {
"arguments": {
"filter": {
"sub": {
"eq": "new-york/manhattan/listings"
},
"data": {
"beds": {
"eq": 2
}
}
},
"limit": 200
},
"stash": {},
"outErrors": []
},
"fieldInError": false,
"errors": [],
"parentType": "Query",
"graphQLAPIId": "q7ueubhsorehbjpr5e6ymj7uua",
"transformedTemplate": "\n\n{\n \"version\" : \"2017-02-28\",\n \"operation\" : \"Query\",\n \"index\" : \"listings-index\",\n \"query\" : {\n \"expression\": \"#status = :status and #sub = :sub\",\n \"expressionNames\" : {\n \t\"#status\" : \"status\",\n \"#sub\" : \"sub\"\n \t},\n \"expressionValues\" : {\n \":status\" : {\"S\":\"Active\"},\n \":sub\" : {\"S\":\"new-york/manhattan/listings\"}\n }\n },\n \"filter\" : {\"expression\":\"(#beds = :beds_eq)\",\"expressionNames\":{\"#beds\":\"beds\"},\"expressionValues\":{\":beds_eq\":{\"N\":2.0}}},\n \"limit\": 200,\n \"nextToken\": null\n}"
}
注意 transformedTemplate
中的过滤器 expressionValues
值:{ "N" : 2.0 }
(无 $util.toDynamoDBJson
格式)并将其与 DynamoDB 中该字段上的对象中的值进行比较。
我已经尝试了所有方法,包括将字段本身更改为字符串并执行各种过滤操作,如 eq
和 contains
以查看这是否是某种奇怪的类型不一致,但没有成功。
截至目前,我有两个备份解决方案,它们涉及 "pulling up" 我可能想要过滤的所有相关字段(用我宁愿嵌套的属性使我的记录混乱)或创建一个新的嵌套仅包含用于过滤的高级字段的类型——即将记录有效地拆分为记录引用和记录过滤器引用。在这种情况下,我们会得到一些“Listing
”记录,其 data
字段值类似于 ListingFilterData
—— 例如:
type Listing {
id: String!
sub: String!
status: String!
data: ListingFilterData!
}
type ListingFilterData {
beds: Float!
baths: Float!
}
两者都是可行的,但我宁愿尝试解决当前的问题,而不是向我的 table.
添加一堆额外的数据
有什么想法吗?
更新 2019 年 9 月 17 日
经过一番折腾,我偶然发现了隐含的解决方案 。根据我可以收集到的解决方案,我使用以下 VTL 请求解析器成功实现了硬编码嵌套查询过滤器(并更改过滤器表达式键名以避免 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))
}
这 return 是我的预期结果:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [
{
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active"
}
],
"nextToken": null
}
}
}
似乎有进展,但是关于如何动态创建文档路径和为嵌套属性创建表达式名称有什么想法吗? 运行 更多想法,如果有任何有趣的事情出现,我们会反馈...
2019 年 9 月 17 日更新 #2
在进一步使用请求解析器之后,我想我找到了一种快速而肮脏的方法来动态获取路径和目标变量,以便为我的嵌套属性创建过滤器表达式。 注意:整个事情仍然return是一个空的结果集,它假设只有一个过滤键(目前),但保留关键字位似乎已经解决了。仍然想知道为什么结果没有按预期显示。
#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))
}
检查 CloudWatch 日志 transformedTemplate
显示表达式名称和值已被适当替换:
"filter" : {
"expression\" : "#ffp = :beds",
"expressionNames" : {
"#ffp" : "filterData.beds"
},
"expressionValues" : {
":beds" : { "N": 2.0 }
}
}
更新于 2019 年 9 月 18 日
我可能终于发现了问题的根源:似乎评估 expressionNames
的方式不允许将键作为文档路径。如果我 运行 以下任一过滤器(注意使用非保留 DynamoDB 关键字来说明问题与表达式名称替换有关),我将得到我正在寻找的结果:
"filter" : {
"expression" : "filterData.beds = :beds", ## filter path parent.target = :target
"expressionValues" : {
":beds" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
或
"filter" : {
"expression" : "filterData.beds = :${fp}", ## filter path parent.target = :target
"expressionValues" : {
":{fp}" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
现在,如果我做一个小改动,只尝试用表达式名称值替换
"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
}
}
我收到以下错误消息:
"ExpressionAttributeNames contains invalid key: Syntax error; key: \"#filterData.beds\" (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException"
即使使用硬编码路径替换,VTL 似乎也将路径读取为单个键名。动态交换表达式的值时出现同样的问题,因此没有硬编码字符串。
已解决
我偶然发现了这个 gem,它给了我一些额外的东西,我需要找到一个具有动态键名的可行解决方案!
过滤器表达式现在如下所示:
"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
}
}
这里的问题是,虽然表达式属性名称通常被解释为文档路径,但随着替换名称的引入,解释器将键名称视为标量属性而不是文档路径。您需要单独识别路径元素并替换每个元素。
问题和预期结果
我正在使用概念验证模式和 DynamoDB Table 设置来过滤嵌套字段值。我非常普遍地遵循了这些想法 here as well as the documentation for $utils.transform.toDynamoDBFilterExpression
(here).
基本思想是这样的:使用相同的原则,我想按任意深度的嵌套字段(DynamoDB 中的 32 个文档路径长度限制不足)进行过滤。相关设置如下所示:
AppSync 架构(对命名约定表示歉意;应该是一个快速而肮脏的 PoC):
query {
listActiveListingsBySubAndFilter(
filter: TableTestMasterDataTable_ImportV1FilterInput!,
limit: Int,
nextToken: String
): TestMasterDataTable_ImportV1Connection
}
input TableBooleanFilterInput {
ne: Boolean
eq: Boolean
}
input TableDataObjectFilterInput {
beds: TableFloatFilterInput
baths: TableFloatFilterInput
}
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 TableStringFilterInput {
ne: String
eq: String
le: String
lt: String
ge: String
gt: String
contains: String
notContains: String
between: [String]
beginsWith: String
}
input TableTestMasterDataTable_ImportV1FilterInput {
id: TableStringFilterInput
status: TableStringFilterInput
sub: TableStringFilterInput
data: TableDataObjectFilterInput
}
type TestMasterDataTable_ImportV1 {
id: String!
status: String!
sub: String!
data: AWSJSON
}
type TestMasterDataTable_ImportV1Connection {
items: [TestMasterDataTable_ImportV1]
nextToken: String
}
input UpdateTestMasterDataTable_ImportV1Input {
id: String!
status: String
sub: String!
data: AWSJSON
}
VTL 请求和响应解析器:
## Request resolver
#set( $filter = $ctx.args.filter )
#set( $path = $filter.data )
{
"version" : "2017-02-28",
"operation" : "Query",
"index" : "listings-index", ## GSI on table with HASH: status, RANGE: sub
"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" : $util.transform.toDynamoDBFilterExpression($path),
"limit": $util.defaultIfNull($ctx.args.limit, 20),
"nextToken": $util.toJson($util.defaultIfNullOrEmpty($ctx.args.nextToken, null))
}
## Response resolver
{
"items": $util.toJson($ctx.result.items),
"nextToken": $util.toJson($util.defaultIfNullOrBlank($context.result.nextToken, null))
}
示例 DynamoDB Table 元素:
{
"_meta": {
"exposure": 0.08,
"lastActive": 1557800000,
"lastUpdated": 1557878400,
"lastView": 1557878500,
"numViews": 63,
"posted": 1557878400
},
"buildingID": "325-5th-Ave,-New-York,-NY-10016,-USA",
"data": {
"agent": [
{
"agentID": "daeo@gmail.com"
},
{
"agentID": "ben@gmail.com"
}
],
"amenities": [
"hot tub",
"time machine"
],
"baths": 2,
"beds": 2
},
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active",
"sub": "new-york/manhattan/listings",
"unitNum": "37C",
"unitRefID": "325-5th-Ave,-New-York,-NY-10016,-USA#37C"
}
基于所有这些,如果我 运行 以下查询:
listActiveListingsBySubAndFilter(filter: {
"sub" : {
"eq" : "new-york/manhattan/listings"
},
"data": {
"beds": {
"eq": 2.0
}
}) {
items {
id
status
}
nextToken
}
我希望在 return 中得到这样的东西:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [
{
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active"
}
],
"nextToken": null
}
}
}
注意:这是唯一预期的 return,因为此时数据库中只有一项符合这些要求。
实际结果
综上所述,我得到的(或没有得到的)结果没有多大意义。无论查询(data.beds
、data.baths
),如果字段嵌套在data
中,return是相同的:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [],
"nextToken": null
}
}
}
我已验证查询按预期工作并且过滤器表达式的格式正确(它适用于其他非嵌套字段,如 id
)。令人困惑的是过滤器似乎没有被应用(或者可能以某种非直观的方式应用?)。作为参考,以下是上述内容的典型 CloudWatch 日志片段:
{
"context": {
"arguments": {
"filter": {
"sub": {
"eq": "new-york/manhattan/listings"
},
"data": {
"beds": {
"eq": 2
}
}
},
"limit": 200
},
"stash": {},
"outErrors": []
},
"fieldInError": false,
"errors": [],
"parentType": "Query",
"graphQLAPIId": "q7ueubhsorehbjpr5e6ymj7uua",
"transformedTemplate": "\n\n{\n \"version\" : \"2017-02-28\",\n \"operation\" : \"Query\",\n \"index\" : \"listings-index\",\n \"query\" : {\n \"expression\": \"#status = :status and #sub = :sub\",\n \"expressionNames\" : {\n \t\"#status\" : \"status\",\n \"#sub\" : \"sub\"\n \t},\n \"expressionValues\" : {\n \":status\" : {\"S\":\"Active\"},\n \":sub\" : {\"S\":\"new-york/manhattan/listings\"}\n }\n },\n \"filter\" : {\"expression\":\"(#beds = :beds_eq)\",\"expressionNames\":{\"#beds\":\"beds\"},\"expressionValues\":{\":beds_eq\":{\"N\":2.0}}},\n \"limit\": 200,\n \"nextToken\": null\n}"
}
注意 transformedTemplate
中的过滤器 expressionValues
值:{ "N" : 2.0 }
(无 $util.toDynamoDBJson
格式)并将其与 DynamoDB 中该字段上的对象中的值进行比较。
我已经尝试了所有方法,包括将字段本身更改为字符串并执行各种过滤操作,如 eq
和 contains
以查看这是否是某种奇怪的类型不一致,但没有成功。
截至目前,我有两个备份解决方案,它们涉及 "pulling up" 我可能想要过滤的所有相关字段(用我宁愿嵌套的属性使我的记录混乱)或创建一个新的嵌套仅包含用于过滤的高级字段的类型——即将记录有效地拆分为记录引用和记录过滤器引用。在这种情况下,我们会得到一些“Listing
”记录,其 data
字段值类似于 ListingFilterData
—— 例如:
type Listing {
id: String!
sub: String!
status: String!
data: ListingFilterData!
}
type ListingFilterData {
beds: Float!
baths: Float!
}
两者都是可行的,但我宁愿尝试解决当前的问题,而不是向我的 table.
添加一堆额外的数据有什么想法吗?
更新 2019 年 9 月 17 日
经过一番折腾,我偶然发现了隐含的解决方案 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))
}
这 return 是我的预期结果:
{
"data": {
"listActiveListingsBySubAndFilter": {
"items": [
{
"id": "325-5th-Ave,-New-York,-NY-10016,-USA#37C:1557878400",
"status": "Active"
}
],
"nextToken": null
}
}
}
似乎有进展,但是关于如何动态创建文档路径和为嵌套属性创建表达式名称有什么想法吗? 运行 更多想法,如果有任何有趣的事情出现,我们会反馈...
2019 年 9 月 17 日更新 #2
在进一步使用请求解析器之后,我想我找到了一种快速而肮脏的方法来动态获取路径和目标变量,以便为我的嵌套属性创建过滤器表达式。 注意:整个事情仍然return是一个空的结果集,它假设只有一个过滤键(目前),但保留关键字位似乎已经解决了。仍然想知道为什么结果没有按预期显示。
#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))
}
检查 CloudWatch 日志 transformedTemplate
显示表达式名称和值已被适当替换:
"filter" : {
"expression\" : "#ffp = :beds",
"expressionNames" : {
"#ffp" : "filterData.beds"
},
"expressionValues" : {
":beds" : { "N": 2.0 }
}
}
更新于 2019 年 9 月 18 日
我可能终于发现了问题的根源:似乎评估 expressionNames
的方式不允许将键作为文档路径。如果我 运行 以下任一过滤器(注意使用非保留 DynamoDB 关键字来说明问题与表达式名称替换有关),我将得到我正在寻找的结果:
"filter" : {
"expression" : "filterData.beds = :beds", ## filter path parent.target = :target
"expressionValues" : {
":beds" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
或
"filter" : {
"expression" : "filterData.beds = :${fp}", ## filter path parent.target = :target
"expressionValues" : {
":{fp}" : $util.dynamodb.toDynamoDBJson(${fv.eq}) ## :target : value to filter for
}
}
现在,如果我做一个小改动,只尝试用表达式名称值替换
"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
}
}
我收到以下错误消息:
"ExpressionAttributeNames contains invalid key: Syntax error; key: \"#filterData.beds\" (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException"
即使使用硬编码路径替换,VTL 似乎也将路径读取为单个键名。动态交换表达式的值时出现同样的问题,因此没有硬编码字符串。
已解决
我偶然发现了这个 gem,它给了我一些额外的东西,我需要找到一个具有动态键名的可行解决方案!
过滤器表达式现在如下所示:
"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
}
}
这里的问题是,虽然表达式属性名称通常被解释为文档路径,但随着替换名称的引入,解释器将键名称视为标量属性而不是文档路径。您需要单独识别路径元素并替换每个元素。