我正在使用GrapgQL和Java。我需要提取所有属于特定父级的子级。我使用了以下方法,但它只会获取父级,但不会获取任何子级。
schema {
query: Query
}
type LearningResource{
id: ID
name: String
type: String
children: [LearningResource]
}
type Query {
fetchLearningResource: LearningResource
}
@Component
public class LearningResourceDataFetcher实现DataFetcher {
@Override
public LearningResource get(DataFetchingEnvironment dataFetchingEnvironment) {
LearningResource lr3 = new LearningResource();
lr3.setId("id-03");
lr3.setName("Resource-3");
lr3.setType("Book");
LearningResource lr2 = new LearningResource();
lr2.setId("id-02");
lr2.setName("Resource-2");
lr2.setType("Paper");
LearningResource lr1 = new LearningResource();
lr1.setId("id-01");
lr1.setName("Resource-1");
lr1.setType("Paper");
List<LearningResource> learningResources = new ArrayList<>();
learningResources.add(lr2);
learningResources.add(lr3);
learningResource1.setChildren(learningResources);
return lr1;
}
}
return RuntimeWiring.newRuntimeWiring().type("Query", typeWiring -> typeWiring.dataFetcher("fetchLearningResource", learningResourceDataFetcher)).build();
我的控制器端点
@RequestMapping(value = "/queryType", method = RequestMethod.POST)
public ResponseEntity query(@RequestBody String query) {
System.out.println(query);
ExecutionResult result = graphQL.execute(query);
System.out.println(result.getErrors());
System.out.println(result.getData().toString());
return ResponseEntity.ok(result.getData());
}
我的请求如下:
{
fetchLearningResource
{
name
}
}
有人可以帮我解决这个问题吗?
答案 0 :(得分:1)
因为我在现实生活中经常被问到这个问题,所以我会在这里详细回答这个问题,这样人们就可以更轻松地用谷歌搜索(我有点需要注意)。
正如评论中所指出的,每个级别的选择必须是明确的,并且没有无限递归查询的概念,例如将节点下的所有内容都放到底部(或 get这个父母的所有孩子递归到底部)。
原因主要是允许此类查询很容易让您陷入危险境地:用户可以轻松地从服务器请求整个对象图!对于任何非平凡的数据大小,这将杀死服务器并立即使网络饱和。另外,一旦遇到递归关系会发生什么?
但是,你可以在这里使用一个半控制的逃生舱。如果您需要所有的范围是有限的(确实应该是),您可以将特定查询的输出类型映射为(复杂)标量。< / p>
在您的情况下,这意味着将LearningResource
映射为标量。然后,fetchLearningResource
将有效地返回 JSON blob ,其中blob恰好是所有子项及其子项的递归。一旦达到标量字段,查询分辨率就不会下降,因为标量是叶节点,所以它不能逐级解析子节点。这意味着您必须自行递归获取一次,所有,因为GraphQL引擎无法帮助您。这也意味着子选择变得不可能(因为标量可以不具有子选择 - 再次,它们是叶节点),因此客户端总是将所有子节点和每个子节点的所有字段都返回。如果您仍需要在某些情况下限制选择的功能,则可以公开2个不同的查询,例如fetchLearningResource
和fetchAllLearningResources
,前者将按原样映射,后者将按照解释返回标量。
graphql-java ExtendedScalars项目提供了一个对象标量实现。
模式可能如下所示:
schema {
query: Query
}
scalar Object
type Query {
fetchLearningResource: Object
}
您可以使用上述方法生成标量实现:
RuntimeWiring.newRuntimeWiring()
.scalar(ExtendedScalars.Object) //register the scalar impl
.type("Query", typeWiring -> typeWiring.dataFetcher("fetchLearningResource", learningResourceDataFetcher)).build();
根据您处理此查询结果的方式,DataFetcher
fetchLearningResource
可能需要将生成的对象转换为地图图(类似JSON的对象),然后再返回到客户。如果你只是JSON序列化结果,你可以跳过这个。请注意,您在此处采取所有安全机制,并且必须注意不要产生巨大的结果。如果您需要在许多地方使用此功能,您很可能会使用完全错误的技术解决您的问题。
我已经不使用您的代码对此进行了测试,因此我可能会跳过重要的内容,但这应该足以让您(或任何人在Google上搜索)进入正确的轨道(如果您&#39 ;确定 正确的轨道。)
更新:我发现有人实施了一个自定义Instrumentation
,在查询后立即重写了该查询,并添加了所有如果没有选择任何字段,则递归地选择字段。这有效地允许他们隐式选择所有。
在graphql-java v11和之前的版本中,您可以改变已解析的查询(由Document
类表示),但从v12开始,it will no longer be possible,但instrumentations in turn gain the ability to replace the Document
显式地通过新的{{1} }} 方法。
当然,这只有在您的架构无法被利用或完全控制客户端的情况下才有意义,因此没有危险。你也可以只选择性地为某些类型做这件事,但使用它会非常混乱。