GraphQL中如何设计带操作符的集合过滤API查询语言?
作为经常处理GraphQL API设计的开发者,这个问题我太熟悉了——既要让客户端能灵活过滤数据,又不能把Schema写得臃肿不堪,还要符合GraphQL的最佳实践。下面是几种行业内广泛采用的方案,完全能解决你提到的带操作符过滤的需求:
1. 强类型字段过滤输入(最推荐的通用方案)
这是社区里最主流的约定,核心思路是为每种数据类型定义带操作符的输入类型,再组合成实体的过滤输入。既保持了GraphQL的强类型优势,又能做到DRY(Don't Repeat Yourself)。
定义基础过滤输入类型
先为常用的数据类型(整数、字符串、日期等)定义通用的过滤输入,这些类型可以在所有实体的过滤逻辑中复用:
# 整数类型的过滤操作符 input IntFilter { eq: Int # 等于 neq: Int # 不等于 gt: Int # 大于 gte: Int # 大于等于 lt: Int # 小于 lte: Int # 小于等于 } # 字符串类型的过滤操作符(包含通配符对应的逻辑) input StringFilter { eq: String # 等于 neq: String # 不等于 contains: String # 包含(对应REST中的*通配符) startsWith: String endsWith: String }
组合成实体的过滤输入
针对你的Person实体,把基础过滤类型组合起来:
input PersonFilter { height: IntFilter weight: IntFilter name: StringFilter } # 最终的查询定义 type Query { familyPeople(filter: PersonFilter): [Person!]! }
客户端查询示例
客户端可以非常直观地组合过滤条件,完全符合GraphQL的查询风格:
query GetFilteredPeople { familyPeople(filter: { height: { eq: 192 } weight: { gte: 65, lte: 100 } name: { contains: "John" } }) { name height weight } }
这个方案的优势:
- 类型安全:客户端能通过GraphQL的自动文档明确知道支持哪些操作符,不会传错参数
- 高度复用:基础过滤类型(
IntFilter、StringFilter)可以在所有需要过滤的实体中重复使用,完全符合DRY原则 - 后端实现清晰:可以基于ORM的查询构建器,写通用的解析逻辑,把过滤输入转换成数据库查询条件
2. 通用表达式过滤(适合复杂动态场景)
如果你的需求是支持非常灵活的动态过滤(比如客户端需要自由组合AND/OR逻辑),可以采用通用条件表达式的方案。不过要注意安全和类型校验的问题。
定义通用过滤结构
enum FilterOperator { EQ NEQ GT GTE LT LTE CONTAINS } input FilterCondition { field: String! # 要过滤的字段名 operator: FilterOperator! # 操作符 value: String! # 过滤值(后端需要做类型转换) } input PersonFilter { and: [PersonFilter!] # 逻辑AND or: [PersonFilter!] # 逻辑OR conditions: [FilterCondition!] # 基础条件 } type Query { familyPeople(filter: PersonFilter): [Person!]! }
客户端查询示例
query GetComplexFilteredPeople { familyPeople(filter: { and: [ { conditions: { field: "height", operator: EQ, value: "192" } }, { or: [ { conditions: { field: "weight", operator: GTE, value: "65" } }, { conditions: { field: "weight", operator: LTE, value: "100" } } ] } ] }) { name height weight } }
这个方案的注意点:
- 需要后端做字段合法性校验,防止客户端查询不存在的字段
- 要处理类型转换(因为
value是字符串类型,需要转换成对应字段的类型) - 注意SQL注入风险,不要直接拼接字符串到数据库查询中,要用参数化查询
3. 行业通用操作符约定
关于操作符的命名,社区已经形成了比较统一的约定,不用自己造轮子:
- 等于:
eq - 不等于:
neq(或not) - 大于:
gt - 大于等于:
gte(或ge) - 小于:
lt - 小于等于:
lte(或le) - 包含(通配符*):
contains(或like) - 前缀匹配:
startsWith - 后缀匹配:
endsWith
最后要强调:绝对不要在GraphQL层外实现过滤逻辑,那样会失去GraphQL的类型安全、自动文档和查询验证的优势。上面的方案都是在GraphQL Schema层面定义过滤规则,后端只需要实现对应的解析逻辑,既符合最佳实践,又能满足DRY原则。
内容的提问来源于stack exchange,提问作者AndrewMcLagan
相关产品推荐
相关产品推荐

