GraphQL Java:如何基于子Resolver过滤父级Deal数据?
解决GraphQL Java中子对象过滤后父级Deal保留的问题
这个场景在GraphQL开发里挺常见的——默认情况下,子Resolver只会过滤自身层级的对象,不会自动向上影响父对象的存在。要移除那些完全没有符合条件Payment的Deal,核心思路是在父级(尤其是根查询Resolver)层面提前做筛选,确保返回的Deal本身就关联至少一个符合条件的子对象,再配合子Resolver过滤掉不符合的子项,就能得到你想要的结果。
下面分两种可行方案详细说明:
方案1:在根查询Resolver中提前筛选Deal(推荐)
这是性能最优的方案,因为我们直接从数据源层面筛选出符合条件的Deal,避免加载大量无关数据后再过滤。
具体步骤
- 调整数据层查询:编写关联查询,直接找出包含指定account的Payment的Deal(如果有其他过滤条件,比如Deal的type,也可以一并加入)。
- 在根Resolver中调用过滤后的数据源:确保
dealPayments方法返回的Deal列表,本身就满足“至少有一个符合条件的Payment”这个前提。 - 子Resolver仍保留子对象过滤:虽然根层面已经筛选了Deal,但单个Deal可能有多个Payment,子Resolver还是要过滤出符合条件的那些,确保返回给客户端的payments列表只包含匹配项。
代码示例
根Query Resolver实现
@Component public class QueryResolver implements GraphQLQueryResolver { private final DealService dealService; public QueryResolver(DealService dealService) { this.dealService = dealService; } public List<Deal> dealPayments(Filter filter) { // 直接获取已经符合条件的Deal列表 return dealService.findDealsWithMatchingPayments(filter); } }
服务层数据查询(以JPA为例)
@Service public class DealService { @Autowired private DealRepository dealRepository; public List<Deal> findDealsWithMatchingPayments(Filter filter) { StringBuilder jpql = new StringBuilder("SELECT DISTINCT d FROM Deal d " + "JOIN d.instruments i JOIN i.legs l JOIN l.payments p WHERE 1=1"); // 拼接Deal的type过滤条件 if (filter.getType() != null) { jpql.append(" AND d.type = :type"); } // 拼接Payment的account过滤条件 if (filter.getAccount() != null) { jpql.append(" AND p.account = :account"); } // 拼接Payment的amountGreaterThan过滤条件 if (filter.getAmountGreaterThan() != null) { jpql.append(" AND p.amount > :amount"); } TypedQuery<Deal> query = dealRepository.createQuery(jpql.toString(), Deal.class); if (filter.getType() != null) { query.setParameter("type", filter.getType()); } if (filter.getAccount() != null) { query.setParameter("account", filter.getAccount()); } if (filter.getAmountGreaterThan() != null) { query.setParameter("amount", filter.getAmountGreaterThan()); } return query.getResultList(); } }
子Resolver(PaymentResolver)实现
@Component public class PaymentResolver implements GraphQLResolver<Leg> { private final PaymentService paymentService; public PaymentResolver(PaymentService paymentService) { this.paymentService = paymentService; } public List<Payment> payments(Leg leg, Filter filter) { // 过滤当前Leg下符合条件的Payment return paymentService.getPaymentsByLegIdAndFilter(leg.getId(), filter); } }
方案2:子对象过滤后回溯过滤父对象(仅特殊场景使用)
如果无法修改根查询的数据源逻辑(比如权限限制、第三方依赖),可以在子Resolver过滤后,逐层向上检查子对象是否为空,再过滤父对象。这种方法性能较差,因为需要先加载所有Deal,再逐层过滤,适合小数据量场景。
具体步骤
- 子Resolver过滤后返回空列表而非null:比如PaymentResolver过滤后返回空列表,而不是null。
- 父Resolver检查子对象列表:比如LegResolver检查payments列表是否为空,若为空则不返回该Leg;InstrumentResolver检查legs列表是否为空,若为空则不返回该Instrument;DealResolver检查instruments列表是否为空,若为空则过滤掉该Deal。
代码示例(InstrumentResolver片段)
@Component public class InstrumentResolver implements GraphQLResolver<Deal> { private final LegResolver legResolver; public InstrumentResolver(LegResolver legResolver) { this.legResolver = legResolver; } public List<Instrument> instruments(Deal deal, Filter filter) { List<Instrument> instruments = deal.getInstruments(); // 过滤掉没有符合条件Leg的Instrument return instruments.stream() .map(instrument -> { List<Leg> filteredLegs = legResolver.legs(instrument, filter); if (filteredLegs.isEmpty()) { return null; } instrument.setLegs(filteredLegs); return instrument; }) .filter(Objects::nonNull) .collect(Collectors.toList()); } }
额外注意事项
你的示例查询有个小语法错误,变量定义应该是:
query AllDealsforAccount($filter: Filter = {account: 123}) { dealPayments(filter: $filter) { id instruments { id legs { id payments { id account } } } } }
要对应Query类型中定义的dealPayments字段,而不是直接写Deal。
内容的提问来源于stack exchange,提问作者Swati Ahuja
相关产品推荐
相关产品推荐

