Bootstrap

graphql计算指令之@sortBy:查询中实现列表字段排序

问题背景

在使用graphql进行查询的时候,很多场景需要对list类型字段进行排序,典型如根据时间戳排序指标数据、根据销量排序商品数据。

如下查询,如果数据源是按照id排序返回、而非销量,则需要硬编码实现商品排序。

query sortItemBySaleAmount($itemIdList:[Int]){
    commodity{
        itemList(itemIds: $itemIdList){
            itemId
            name
            saleAmount
        }
    }
}

解决方案

graphql提供了指令机制,该机制类似于java注解,可用于graphql查询执行能力的动态拓展。

定义对列表数据进行排序的指令:

directive @sortBy(comparator: String!, reversed: Boolean = false) on FIELD

  • comparator:比较表达式、表达式参数为每个列表元素,值越大越靠后;

  • reversed:是否进行逆序排序;

  • 表达式结果为null的元素总是排在末尾。

使用指令排序的查询为:

query sortItemBySaleAmount($itemIdList:[Int]){
    commodity{
        itemList(itemIds: $itemIdList)
        # reversed为true表示按照 saleAmount 递减排序
        @sortBy(comparator: "saleAmount",reversed: true)
        {
            itemId
            name
            saleAmount
        }
    }
}

该能力可通过进行实现,该组件基于指令系统、为graphql查询提供数据编排、动态计算和控制流的能力。实现代码如下:

Config wrapperConfig = DefaultConfig.newConfig().build();

DefaultGraphQLSourceBuilder graphqlSourceBuilder = new DefaultGraphQLSourceBuilder();
GraphQLSource graphqlSource = graphqlSourceBuilder
        .wrapperConfig(wrapperConfig)
        .originalSchema(
                // 原始schema对象
                GraphQLSourceHolder.getDefaultSchema()
        ).build();

String query = "" +
        "query sortItemListBySaleAmount($itemIdList:[Int]){\n" +
        "    commodity{\n" +
        "        itemList(itemIds: $itemIdList)\n" +
        "        @sortBy(comparator: \"saleAmount\",reversed: true)\n" +
        "        {\n" +
        "            itemId\n" +
        "            name\n" +
        "            saleAmount\n" +
        "        }\n" +
        "        \n" +
        "        originalItemList: itemList(itemIds: $itemIdList){\n" +
        "            itemId\n" +
        "            name\n" +
        "            saleAmount\n" +
        "        }\n" +
        "    }\n" +
        "}";

ExecutionInput input = ExecutionInput.newExecutionInput(query)
        .variables(Collections.singletonMap("itemIdList", Arrays.asList(2, 1, 3, 4, 5)))
        .build();

ExecutionResult result = graphqlSource.getGraphQL().execute(input);

输出:

assert Objects.equals(data.get("commodity").get("itemList").toString(),
        "[{itemId=5, name=item_name_5, saleAmount=53}, {itemId=4, name=item_name_4, saleAmount=43}," +
                " {itemId=3, name=item_name_3, saleAmount=33}, {itemId=2, name=item_name_2, saleAmount=23}, " +
                "{itemId=1, name=item_name_1, saleAmount=13}]"
);

assert Objects.equals(data.get("commodity").get("originalItemList").toString(),
        "[{itemId=2, name=item_name_2, saleAmount=23}, {itemId=1, name=item_name_1, saleAmount=13}, " +
                "{itemId=3, name=item_name_3, saleAmount=33}, {itemId=4, name=item_name_4, saleAmount=43}, " +
                "{itemId=5, name=item_name_5, saleAmount=53}]"
);

完整示例参考

联系反馈

欢迎大家试用,笔者一直从事graphql的平台化工作,组件的活跃contributor,期待使用、建议,欢迎感兴趣的同学star关注。

其他文章: