Bootstrap

graphql中的'子查询'

问题背景

本文介绍通过一个graphql查询返回有依赖关系的数据,实现类似于mysql中子查询的能力。

  • 项目介绍:

  • 项目地址:https://github.com/graphql-calculator/graphql-calculator

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
    # ...... 
  }
}