提取所有子项属于graphql中的特定父项

时间:2017-10-06 10:51:05

标签: graphql graphql-java

我正在使用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
 }
}

有人可以帮我解决这个问题吗?

1 个答案:

答案 0 :(得分:1)

因为我在现实生活中经常被问到这个问题,所以我会在这里详细回答这个问题,这样人们就可以更轻松地用谷歌搜索(我有点需要注意)。

正如评论中所指出的,每个级别的选择必须是明确的,并且没有无限递归查询的概念,例如将节点下的所有内容都放到底部(或 get这个父母的所有孩子递归到底部)。

原因主要是允许此类查询很容易让您陷入危险境地:用户可以轻松地从服务器请求整个对象图!对于任何非平凡的数据大小,这将杀死服务器并立即使网络饱和。另外,一旦遇到递归关系会发生什么?

但是,你可以在这里使用一个半控制的逃生舱。如果您需要所有的范围是有限的(确实应该是),您可以将特定查询的输出类型映射为(复杂)标量。< / p>

在您的情况下,这意味着将LearningResource映射为标量。然后,fetchLearningResource将有效地返回 JSON blob ,其中blob恰好是所有子项及其子项的递归。一旦达到标量字段,查询分辨率就不会下降,因为标量是叶节点,所以它不能逐级解析子节点。这意味着您必须自行递归获取一次所有,因为GraphQL引擎无法帮助您。这也意味着子选择变得不可能(因为标量可以不具有子选择 - 再次,它们是叶节点),因此客户端总是将所有子节点和每个子节点的所有字段都返回。如果您仍需要在某些情况下限制选择的功能,则可以公开2个不同的查询,例如fetchLearningResourcefetchAllLearningResources,前者将按原样映射,后者将按照解释返回标量。

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} }} 方法。 当然,这只有在您的架构无法被利用或完全控制客户端的情况下才有意义,因此没有危险。你也可以只选择性地为某些类型做这件事,但使用它会非常混乱。