graphql中的'子查询'
问题背景
本文介绍通过一个graphql查询返回有依赖关系的数据,实现类似于mysql中子查询的能力。
mysql中的子查询
mysql中的子查询:当查询执行时,首先执行查询A并返回一个结果集。然后,将此结果集作为查询B的输入。例如有活动表和商品表,查询商品类活动涉及的商品信息如下:
-- 参与活动的商品信息
select *
from commodity_info_table
where id in
(
-- 活动类型为 "commodity" 时、该id为商品id
select item_id
-- 包含活动类型、涉及主体id、活动开始时间、结束时间等
from activity_info_table
-- activity_type_code 为活动类型
where activity_type_code = "commodity"
)
graphql中的子查询
上述数据往往会有 商品 和 活动 两个研发部门维护,并通过接口提供服务。既无法通过子查询直接查询DB,也不可能存在 获取所有活动商品详情数据的接口。
如果活动接口数据和商品接口数据治理在graphql平台上,原生的graphql查询也难以通过一个查询直接获取这种关联数据。一般解决方案为使用两次查询解决:两次网络io、并需要客户端有相应的硬编码支持:
graphql查询活动数据 ——> 解析活动中的商品id ——> graphql查询商品数据。
解决方案
示例schema
对于上述问题做如下schema定义:
type Query{
activityInfoList(activityType:String): [Activity]
commodityList(commodityIds:[Int]): [Commodity]
}
type Activity{
id: Int
itemId: Int
activityType: String
# ...
}
type Commodity{
id: Int
name: String
price: Int
}
定义计算指令
graphql提供了指令机制用于graphql查询执行能力的动态拓展、指令机制类似于java中的注解。我们通过定义‘源数据指令’和‘参数转换指令’实现数据之间的依赖传递。
directive @argumentTransform(argumentName:String!, operateType:ParamTransformType!, expression:String!, dependencySources:[String!]) on FIELD
enum ParamTransformType{
MAP # 参数转换
FILTER # 列表类型参数过滤
LIST_MAP # 列表类型参数元素转换
}
指令详细说明参考。通过指令实现的graphql“子查询”如下:
query getActivityCommodityInfo{
# 根据活动类型获取活动信息
activityInfoList(activityType:"commodity"){
# 将 活动商品id注册为源数据,因为 itemId定义在列表中,所以最后itemIdList结果为List
itemId @fetchSource(name:"itemIdList")
}
commodityList(commodityIds: 1)
@argumentTransform(
argumentName:"commodityIds", # 进行转换的参数名称
operateType:MAP,expression:"itemIdList", # 转换类型和表达式
dependencySources:"itemIdList" # 依赖的源数据
){
id
# ......
}
}