GraphQL DGS-Kotlin:如何依据子DataLoader结果过滤父结果集
问题描述
我拥有getUserList()解析器与地址DataLoader,希望仅返回存在地址的用户:即父解析器返回10个用户,但地址DataLoader仅为其中8个用户返回地址时,需从父结果集中移除无地址的2个用户。该如何实现?我曾考虑尝试SchemaDerivative,但不确定字段是否能访问子数据。
注:上述为示例场景,我知道该场景通过数据库查询更易实现,但实际场景复杂,需关联多张表,且地址只能通过DataLoader加载。
提供的代码示例
class UserEntity(val id: UUID, val name: String, val email: String){ val occupation: String? =null } class AddressEntity(val id:UUID){ val line1: String, val line2 : String, val city: City, val state: State, val zipcode: ZipCode @ManyToOne @JoinColumn(name = "user_id", referencedColumnName = "id") var user: UserEntity } class UserResolver(private val userService: UserService) { @DgsQuery(field = DgsConstants.QUERY.GetUsers) fun getUsersByLocation( dfe: DataFetchingEnvironment ): UsersPagedResult { return userService.getAllUsersByLocation(getLocation(dfe)) } @DgsData(parentType = "User", field = "addressList") fun getAddressList(dfe: DgsDataFetchingEnvironment): CompletableFuture<Address>? { val dataLoader: DataLoader<UUID, Address> = dfe.getDataLoader<UUID, Address>( AddressDataLoader::class.java ) val user = dfe.getSource<User>() return dataLoader.load(user.id) } }
解决方案
方法一:父解析器中批量预加载并过滤
既然地址只能通过DataLoader加载,可在父解析器中先获取所有用户,批量加载对应地址后直接过滤掉无地址的用户,再封装分页结果返回。
修改后的UserResolver示例:
class UserResolver(private val userService: UserService) { @DgsQuery(field = DgsConstants.QUERY.GetUsers) fun getUsersByLocation( dfe: DataFetchingEnvironment ): CompletableFuture<UsersPagedResult> { // 获取初始用户列表 val usersResult = userService.getAllUsersByLocation(getLocation(dfe)) val userIds = usersResult.users.map { it.id } // 从DataFetchingEnvironment获取地址DataLoader val addressDataLoader = dfe.getDataLoader<UUID, Address>(AddressDataLoader::class.java) // 批量加载所有用户的地址,过滤无地址用户 return addressDataLoader.loadMany(userIds) .thenApply { addressMap -> val filteredUsers = usersResult.users.filter { user -> addressMap[user.id] != null } // 重新封装分页结果,总计数可根据业务需求调整(比如查真实有地址的总数,或直接用过滤后数量) UsersPagedResult( users = filteredUsers, totalCount = filteredUsers.size, page = usersResult.page, size = usersResult.size ) } } @DgsData(parentType = "User", field = "addressList") fun getAddressList(dfe: DgsDataFetchingEnvironment): CompletableFuture<Address>? { val dataLoader: DataLoader<UUID, Address> = dfe.getDataLoader(AddressDataLoader::class.java) val user = dfe.getSource<User>() return dataLoader.load(user.id) } }
方法二:利用DGS后处理钩子过滤
如果不想修改父解析器核心逻辑,可借助DGS的结果后处理机制:
- 在父解析器返回用户时保留ID标识
- 实现
DgsDataLoaderListener监听地址加载完成事件,记录有地址的用户ID - 在查询结果序列化前,通过自定义的
DgsResultTransformer过滤掉无地址的用户
不过这种方式步骤更繁琐,不如方法一直接高效。
关于SchemaDerivative的说明
SchemaDerivative主要用于动态生成Schema结构,无法直接访问子字段的加载结果,不适合实现这种数据过滤需求,优先考虑前面两种方法。
内容的提问来源于stack exchange,提问作者user2716913
相关产品推荐
相关产品推荐

