AWS AppSync Query to shape response data(类似于 SQL 中的 Group By)
AWS AppSync Query to shape response data (Similar to Group By in SQL)
我有一个 DynamoDB table,其中包含客户端所需的所有数据,但是,我想调整客户端接收到的数据以减少客户端操作。
我的架构:
type StateCounty {
id: ID!
StateName: String
CountyName: String
FIPSST: Int
FIPSCNTY: Int
Penetration: String
Date: String
}
和 return 一个自定义查询,我有以下类型:
type Query {
getStateCountybyState(StateName: String): StateCountyConnection
}
这有效 - 并且使用简单的查询
query getStateCountybyState {
getStateCountybyState (StateName: "Delaware") {
items {
StateName
CountyName
Date
}
}
}
结果 return 符合预期:
{
"StateName": "Delaware",
"CountyName": "Kent",
"Date": "02-01-2017"
},
{
"StateName": "Delaware",
"CountyName": "Sussex",
"Date": "02-01-2016"
},
{
"StateName": "Delaware",
"CountyName": "New Castle",
"Date": "02-01-2018"
}
等等
我想return以下格式的数据:
{
"StateName": "Delaware" {
{ "CountyName": "Kent",
"Date": "02-01-2017"
},
{
"CountyName": "Sussex",
"Date": "02-01-2016"
},
{
"CountyName": "New Castle",
"Date": "02-01-2018"
}
}
}
我已尝试将 GroupCounty:[StateCountyGroup] 添加到架构中:
type StateCounty {
id: ID!
StateName: String
CountyName: String
FIPSST: Int
FIPSCNTY: Int
Penetration: String
Date: String
GroupCounty: [StateCountyGroup]
}
然后在查询中引用它
query getStateCountybyState {
getStateCountybyState (StateName: "Delaware") {
items {
StateName
CountyName
Date
GroupCounty: [StateCountyGroup]
}
}
}
我认为我的问题出在解析器中 - 目前,它配置为使用 StateName 作为键,但我不确定如何将 StateName 从主查询传递到子查询。
解析器:
{
"version" : "2017-02-28",
"operation" : "Query",
"query" : {
"expression" : "StateName = :StateName",
"expressionValues" : {
":StateName" : { "S" : "${context.arguments.StateName}" },
}
},
"index" : "StateName-index-copy",
"select" : "ALL_ATTRIBUTES",
}
感谢任何指导 - 我已经多次阅读文档,但找不到示例。
更新
我尝试了理查德的以下建议 - 它绝对是正确的,但是,尽管主题有多种变化,但我还是 return 无效或出现以下错误(我删除了一些县对象 return 为简洁起见在错误中编辑):
"message": "Unable to convert set($myresponse = {\n \"Delaware\":
[{SSA=8000, Eligibles=32295, FIPS=10001, StateName=Delaware, SSACNTY=0,
Date=02-01-2016, CountyName=Kent, Enrolled=3066, Penetration=0.0949,
FIPSCNTY=1, FIPSST=10, SSAST=8, id=6865},
{SSA=8010, Eligibles=91332, FIPS=10003, StateName=Delaware, SSACNTY=10, Date=02-01-2016, CountyName=New Castle, Enrolled=10322, Penetration=0.113, FIPSCNTY=3, FIPSST=10, SSAST=8, id=6866},
{SSA=0, Eligibles=10, FIPS=10, StateName=Delaware, SSACNTY=0, Date=02-01-2018, CountyName=Pending County Designation, Enrolled=0, Penetration=0, FIPSCNTY=0, FIPSST=10, SSAST=0, id=325},
{SSA=8000, Eligibles=33371, FIPS=10001, StateName=Delaware, SSACNTY=0, Date=02-01-2017, CountyName=Kent, Enrolled=3603, Penetration=0.108, FIPSCNTY=1, FIPSST=10, SSAST=8, id=3598},
{SSA=8020, Eligibles=58897, FIPS=10005, StateName=Delaware, SSACNTY=20, Date=02-01-2016, CountyName=Sussex, Enrolled=3760, Penetration=0.0638, FIPSCNTY=5, FIPSST=10, SSAST=8, id=6867}) \nnull\n\n to class java.lang.Object."
}
]
}
从阅读上面的内容来看,听起来您的原始查询是 return 您想要的正确结果,但不是您希望的响应格式,正如您希望 "StateName"成为 top-level JSON 键,其值为作为参数传入的状态的 JSON 对象。那是准确的吗?如果是这样,那么为什么不使用已经有效但具有不同响应模板的相同查询。类似于:
#set($myresponse = {
"$ctx.args.StateName": $ctx.result.items
})
$util.toJson($myresponse)
请注意 $myresponse
与上面的示例不完全相同,因为 "stateName" : "Delaware" { ... }
的示例不完全有效 JSON 所以我不想做假设什么是好的结构,但如果您已经从查询中获得正确的结果,那么重点仍然存在,我只是尝试更改 GraphQL 结果的结构。
现在如果我误读了上面的内容并且你没有从查询中得到正确的结果,我可以阅读你的 "primary query to the subquery" 声明的另一种方式是你正在尝试应用额外的 "filter"
到您的查询结果。如果是这样,那么您需要这样的东西:
{
"version" : "2017-02-28",
"operation" : "Query",
"query" : {
"expression" : "StateName = :StateName",
"expressionValues" : {
":StateName" : { "S" : "${context.arguments.StateName}" },
}
},
"index" : "StateName-index-copy",
"select" : "ALL_ATTRIBUTES",
"filter" : {
"expression" : "#population >= :population",
"expressionNames" : {
"#population" : "population"
},
"expressionValues" : {
":population" : $util.dynamodb.toDynamoDBJson($ctx.args.population)
}
}
}
我在这里使用了一个示例,您的查询可能还需要按每个县的人口规模进行过滤。这可能无法代表您正在寻找的内容,但希望对您有所帮助。
编辑了更多信息 2018 年 4 月 16 日
我已经以 step-by-step 的方式写了更多关于这方面的信息,以分块讨论这些概念。
这里的关键不仅仅是响应模板,还有您请求 returned 的字段(因为这是 GraphQL 的本质)。让我们通过示例来了解一下。现在您 return 使用 GraphQL 处理单个项目(因为您的响应模板正在将数组转换为单个项目),因此您需要更改预期的 GraphQL 查询响应类型。假设您的架构中有一个 GraphQL 类型,如下所示:
type State {
id: ID!
population: String!
governor: String!
}
type Query {
allStates: [State]
}
如果您只是像上面那样转换模板中的响应,您将看到类似 "type mismatch error, expected type LIST" 的错误,如果您 运行 是这样的:
query {
allStates{
id
population
}
}
那是因为您的回复不再是 return单个项目。相反,您需要更改 GraphQL 响应类型 [State]
以匹配您的模板转换正在执行的操作 State
,如下所示:
type State {
StateName: String
}
type Query {
allStates: State
}
现在,如果您的解析器 请求 模板正在做一些事情 returns 项目列表(如 DynamoDB 扫描或查询),您可以将列表转换为单个response 模板中的项目如下所示:
#set($convert = {"StateName" : $ctx.result.items })
$util.toJson($convert)
然后 运行 以下 GraphQL 查询:
query {
allStates{
StateName
}
}
你会得到一个包含你的结果数组的对象:
{
"data": {
"allStates": {
"StateName": "[{id=1, population=10000, governor=John Smith}]"
}
}
}
然而,虽然这可能会指出您遇到的错误,但这是 returning StateName
从您最初的问题来看,我认为您希望通过合并记录来做更多的事情在对一些优化的响应中,以及一些潜在的过滤。一种方法是创建一个数组(或者您可以创建一个映射 {})并根据某些条件填充它。例如,修改您的查询以将 StateName
作为参数:
type Query {
allStates(StateName: String!): Post
}
然后您可以在解析器响应模板中对此进行过滤,方法是使用 #foreach
和 #if()
条件,然后仅当响应中的项目用于您请求的状态:
#set($convert = {"StateName" : [] })
#foreach($item in $ctx.result.items)
#if($item["StateName"]=="$ctx.args.StateName")
$util.qr($convert.get("StateName").add("$item"))
#end
#end
$util.toJson($convert)
所以现在你可以 运行 像这样:
query {
allStates(StateName:"Texas"){
StateName
}
}
这将返回您作为参数传递的特定状态的结果。但是您会注意到查询的选择集是 StateName
。您可以通过在 GraphQL 类型中列出可能的状态来引入更多的灵活性:
type State {
StateName: String
Seattle: String
Texas: String
}
现在您更改解析器响应模板以使用构建 return 数组的参数,因为它可以在选择集中指定它:
#set($convert = {"$ctx.args.StateName" : [] })
#foreach($item in $ctx.result.items)
#if($item["StateName"]=="$ctx.args.StateName")
$util.qr($convert.get("$ctx.args.StateName").add("$item"))
#end
#end
$util.toJson($convert)
所以我可以运行这个查询:
query {
allPosts(StateName:"Seattle"){
Seattle
}
}
我得到了我的结果。请注意,虽然将 Seattle
作为参数传递但请求返回 Texas
:
query {
allPosts(StateName:"Seattle"){
Texas
}
}
这将不起作用,因为您在地图中创建的响应对象是 Seattle: [...]
,但您将 Texas
作为选择集。
您可能想要做的最后一件事是拥有多个州 returned,您可以通过构建一个以州名称为关键字的巨型地图来实现,或者可以使用参数或通过将状态名称添加到 return 类型来设置选择集,如上所示。这取决于你,所以我不确定你会如何想要它,但希望这展示了你可以如何操纵响应来满足你的需要。
我有一个 DynamoDB table,其中包含客户端所需的所有数据,但是,我想调整客户端接收到的数据以减少客户端操作。
我的架构:
type StateCounty {
id: ID!
StateName: String
CountyName: String
FIPSST: Int
FIPSCNTY: Int
Penetration: String
Date: String
}
和 return 一个自定义查询,我有以下类型:
type Query {
getStateCountybyState(StateName: String): StateCountyConnection
}
这有效 - 并且使用简单的查询
query getStateCountybyState {
getStateCountybyState (StateName: "Delaware") {
items {
StateName
CountyName
Date
}
}
}
结果 return 符合预期:
{
"StateName": "Delaware",
"CountyName": "Kent",
"Date": "02-01-2017"
},
{
"StateName": "Delaware",
"CountyName": "Sussex",
"Date": "02-01-2016"
},
{
"StateName": "Delaware",
"CountyName": "New Castle",
"Date": "02-01-2018"
}
等等
我想return以下格式的数据:
{
"StateName": "Delaware" {
{ "CountyName": "Kent",
"Date": "02-01-2017"
},
{
"CountyName": "Sussex",
"Date": "02-01-2016"
},
{
"CountyName": "New Castle",
"Date": "02-01-2018"
}
}
}
我已尝试将 GroupCounty:[StateCountyGroup] 添加到架构中:
type StateCounty {
id: ID!
StateName: String
CountyName: String
FIPSST: Int
FIPSCNTY: Int
Penetration: String
Date: String
GroupCounty: [StateCountyGroup]
}
然后在查询中引用它
query getStateCountybyState {
getStateCountybyState (StateName: "Delaware") {
items {
StateName
CountyName
Date
GroupCounty: [StateCountyGroup]
}
}
}
我认为我的问题出在解析器中 - 目前,它配置为使用 StateName 作为键,但我不确定如何将 StateName 从主查询传递到子查询。
解析器:
{
"version" : "2017-02-28",
"operation" : "Query",
"query" : {
"expression" : "StateName = :StateName",
"expressionValues" : {
":StateName" : { "S" : "${context.arguments.StateName}" },
}
},
"index" : "StateName-index-copy",
"select" : "ALL_ATTRIBUTES",
}
感谢任何指导 - 我已经多次阅读文档,但找不到示例。
更新
我尝试了理查德的以下建议 - 它绝对是正确的,但是,尽管主题有多种变化,但我还是 return 无效或出现以下错误(我删除了一些县对象 return 为简洁起见在错误中编辑):
"message": "Unable to convert set($myresponse = {\n \"Delaware\":
[{SSA=8000, Eligibles=32295, FIPS=10001, StateName=Delaware, SSACNTY=0,
Date=02-01-2016, CountyName=Kent, Enrolled=3066, Penetration=0.0949,
FIPSCNTY=1, FIPSST=10, SSAST=8, id=6865},
{SSA=8010, Eligibles=91332, FIPS=10003, StateName=Delaware, SSACNTY=10, Date=02-01-2016, CountyName=New Castle, Enrolled=10322, Penetration=0.113, FIPSCNTY=3, FIPSST=10, SSAST=8, id=6866},
{SSA=0, Eligibles=10, FIPS=10, StateName=Delaware, SSACNTY=0, Date=02-01-2018, CountyName=Pending County Designation, Enrolled=0, Penetration=0, FIPSCNTY=0, FIPSST=10, SSAST=0, id=325},
{SSA=8000, Eligibles=33371, FIPS=10001, StateName=Delaware, SSACNTY=0, Date=02-01-2017, CountyName=Kent, Enrolled=3603, Penetration=0.108, FIPSCNTY=1, FIPSST=10, SSAST=8, id=3598},
{SSA=8020, Eligibles=58897, FIPS=10005, StateName=Delaware, SSACNTY=20, Date=02-01-2016, CountyName=Sussex, Enrolled=3760, Penetration=0.0638, FIPSCNTY=5, FIPSST=10, SSAST=8, id=6867}) \nnull\n\n to class java.lang.Object."
}
]
}
从阅读上面的内容来看,听起来您的原始查询是 return 您想要的正确结果,但不是您希望的响应格式,正如您希望 "StateName"成为 top-level JSON 键,其值为作为参数传入的状态的 JSON 对象。那是准确的吗?如果是这样,那么为什么不使用已经有效但具有不同响应模板的相同查询。类似于:
#set($myresponse = {
"$ctx.args.StateName": $ctx.result.items
})
$util.toJson($myresponse)
请注意 $myresponse
与上面的示例不完全相同,因为 "stateName" : "Delaware" { ... }
的示例不完全有效 JSON 所以我不想做假设什么是好的结构,但如果您已经从查询中获得正确的结果,那么重点仍然存在,我只是尝试更改 GraphQL 结果的结构。
现在如果我误读了上面的内容并且你没有从查询中得到正确的结果,我可以阅读你的 "primary query to the subquery" 声明的另一种方式是你正在尝试应用额外的 "filter"
到您的查询结果。如果是这样,那么您需要这样的东西:
{
"version" : "2017-02-28",
"operation" : "Query",
"query" : {
"expression" : "StateName = :StateName",
"expressionValues" : {
":StateName" : { "S" : "${context.arguments.StateName}" },
}
},
"index" : "StateName-index-copy",
"select" : "ALL_ATTRIBUTES",
"filter" : {
"expression" : "#population >= :population",
"expressionNames" : {
"#population" : "population"
},
"expressionValues" : {
":population" : $util.dynamodb.toDynamoDBJson($ctx.args.population)
}
}
}
我在这里使用了一个示例,您的查询可能还需要按每个县的人口规模进行过滤。这可能无法代表您正在寻找的内容,但希望对您有所帮助。
编辑了更多信息 2018 年 4 月 16 日
我已经以 step-by-step 的方式写了更多关于这方面的信息,以分块讨论这些概念。
这里的关键不仅仅是响应模板,还有您请求 returned 的字段(因为这是 GraphQL 的本质)。让我们通过示例来了解一下。现在您 return 使用 GraphQL 处理单个项目(因为您的响应模板正在将数组转换为单个项目),因此您需要更改预期的 GraphQL 查询响应类型。假设您的架构中有一个 GraphQL 类型,如下所示:
type State {
id: ID!
population: String!
governor: String!
}
type Query {
allStates: [State]
}
如果您只是像上面那样转换模板中的响应,您将看到类似 "type mismatch error, expected type LIST" 的错误,如果您 运行 是这样的:
query {
allStates{
id
population
}
}
那是因为您的回复不再是 return单个项目。相反,您需要更改 GraphQL 响应类型 [State]
以匹配您的模板转换正在执行的操作 State
,如下所示:
type State {
StateName: String
}
type Query {
allStates: State
}
现在,如果您的解析器 请求 模板正在做一些事情 returns 项目列表(如 DynamoDB 扫描或查询),您可以将列表转换为单个response 模板中的项目如下所示:
#set($convert = {"StateName" : $ctx.result.items })
$util.toJson($convert)
然后 运行 以下 GraphQL 查询:
query {
allStates{
StateName
}
}
你会得到一个包含你的结果数组的对象:
{
"data": {
"allStates": {
"StateName": "[{id=1, population=10000, governor=John Smith}]"
}
}
}
然而,虽然这可能会指出您遇到的错误,但这是 returning StateName
从您最初的问题来看,我认为您希望通过合并记录来做更多的事情在对一些优化的响应中,以及一些潜在的过滤。一种方法是创建一个数组(或者您可以创建一个映射 {})并根据某些条件填充它。例如,修改您的查询以将 StateName
作为参数:
type Query {
allStates(StateName: String!): Post
}
然后您可以在解析器响应模板中对此进行过滤,方法是使用 #foreach
和 #if()
条件,然后仅当响应中的项目用于您请求的状态:
#set($convert = {"StateName" : [] })
#foreach($item in $ctx.result.items)
#if($item["StateName"]=="$ctx.args.StateName")
$util.qr($convert.get("StateName").add("$item"))
#end
#end
$util.toJson($convert)
所以现在你可以 运行 像这样:
query {
allStates(StateName:"Texas"){
StateName
}
}
这将返回您作为参数传递的特定状态的结果。但是您会注意到查询的选择集是 StateName
。您可以通过在 GraphQL 类型中列出可能的状态来引入更多的灵活性:
type State {
StateName: String
Seattle: String
Texas: String
}
现在您更改解析器响应模板以使用构建 return 数组的参数,因为它可以在选择集中指定它:
#set($convert = {"$ctx.args.StateName" : [] })
#foreach($item in $ctx.result.items)
#if($item["StateName"]=="$ctx.args.StateName")
$util.qr($convert.get("$ctx.args.StateName").add("$item"))
#end
#end
$util.toJson($convert)
所以我可以运行这个查询:
query {
allPosts(StateName:"Seattle"){
Seattle
}
}
我得到了我的结果。请注意,虽然将 Seattle
作为参数传递但请求返回 Texas
:
query {
allPosts(StateName:"Seattle"){
Texas
}
}
这将不起作用,因为您在地图中创建的响应对象是 Seattle: [...]
,但您将 Texas
作为选择集。
您可能想要做的最后一件事是拥有多个州 returned,您可以通过构建一个以州名称为关键字的巨型地图来实现,或者可以使用参数或通过将状态名称添加到 return 类型来设置选择集,如上所示。这取决于你,所以我不确定你会如何想要它,但希望这展示了你可以如何操纵响应来满足你的需要。