如何递归遍历 java 中的 graphql 文档并找到它到达的最深层次
How can I recursively traverse a graphql document in java and find the deepest level it reaches
我的目标是遍历 graphql Java 文档 object 和 return 最大深度。
示例:深度 0
{
name
}
示例:深度 1
{
viewer{
viewerId
}
}
示例:深度 2
{
viewer{
albums{
albumId
}
}
}
示例:深度 2。如您所见,albums/songs 都在同一 parent 'viewer'
下
{
viewer{
albums{
albumId
}
songs{
songId
}
}
}
示例:深度 3
{
viewer{
albums{
tracks{
trackId
}
}
}
}
我已经编写了遍历它的基本代码,但我的代码不适用于深度 = 2 的第二个版本。它 returns 深度 = 3 而不是 2。原因是因为它在计数在同一个 parent 下两次。本质上逻辑是这样的:只要字段有 children.
,深度 = 深度 + 1
import graphql.language.Document;
import graphql.language.Node;
import graphql.language.OperationDefinition;
public int checkDepthLimit(String query) {
Document document;
try {
document = documentParser.parseDocument(query);
} catch (Exception e) {}
Optional<Node> queryNode = document.getChildren().stream()
.filter(n -> (n.getClass() == OperationDefinition.class))
.findFirst();
return checkDepthLimit(queryNode.get());
}
private int checkDepthLimit(Node queryNode) {
int depth = 0;
String nodeType = queryNode.getClass().getSimpleName().toUpperCase();
if (nodeType.equals("FIELD")) {
if (!queryNode.getChildren().isEmpty()) {
depth += 1;
}
}
List<Node> nodeChildren = queryNode.getChildren();
for (int i = 0; i < nodeChildren.size(); i++) {
depth += checkDepthLimit(nodeChildren.get(i));
}
return depth;
}
String query = "{
viewer{
viewerId
}"
QueryComplexity c = new QueryComplexity();
int depth = c.checkDepthLimit(query);
我被困住了,如果对递归有更深入了解的人能够帮助我,我将不胜感激。
错误是您迭代子项的地方。正如您已经认识到的那样 ("counting twice"),您将每个子级的深度添加到当前深度,但您应该只添加最深的一个。
这样就可以了:
List<Node> nodeChildren = queryNode.getChildren();
int maxChildDepth = 0;
for (int i = 0; i < nodeChildren.size(); i++) {
final int currentChildDepth = checkDepthLimit(nodeChildren.get(i));
maxChildDepth = Math.max(maxChildDepth, currentChildDepth);
}
depth += maxChildDepth;
return depth;
从 graphql-java v4.0 开始,有一个内置的 QueryTraversal
class to help you with inspecting the query AST and a related instrumentation for limiting the depth: MaxQueryDepthInstrumentation
就像注册任何其他仪器一样注册它:
GraphQL runtime = GraphQL.newGraphQL(schema)
.instrumentation(new MaxQueryDepthInstrumentation(MAX_DEPTH))
.build();
还有 MaxQueryComplexityInstrumentation.
graphql-spqr(由我编写)还具有指定字段复杂性的声明方式:
public static class PetService {
@GraphQLQuery(name = "pet")
@GraphQLComplexity("type == 'big' ? 10 : 2") //JavaScript expression calculating the complexity
public Pet findPet(@GraphQLArgument(name = "type") String type) {
return db.findPetByType(type);
}
}
您可以手动注册ComplexityAnalysisInstrumentation
,同上:
GraphQL runtime = GraphQL.newGraphQL(schema)
.instrumentation(new ComplexityAnalysisInstrumentation(new JavaScriptEvaluator(), MAX_DEPTH))
.build();
或使用 SPQR 的 GraphQLRuntime
包装器:
GraphQL runtime = GraphQLRuntime.newGraphQL(schema)
.maximumQueryComplexity(maxComplexity)
.build();
我的目标是遍历 graphql Java 文档 object 和 return 最大深度。
示例:深度 0
{
name
}
示例:深度 1
{
viewer{
viewerId
}
}
示例:深度 2
{
viewer{
albums{
albumId
}
}
}
示例:深度 2。如您所见,albums/songs 都在同一 parent 'viewer'
下{
viewer{
albums{
albumId
}
songs{
songId
}
}
}
示例:深度 3
{
viewer{
albums{
tracks{
trackId
}
}
}
}
我已经编写了遍历它的基本代码,但我的代码不适用于深度 = 2 的第二个版本。它 returns 深度 = 3 而不是 2。原因是因为它在计数在同一个 parent 下两次。本质上逻辑是这样的:只要字段有 children.
,深度 = 深度 + 1import graphql.language.Document;
import graphql.language.Node;
import graphql.language.OperationDefinition;
public int checkDepthLimit(String query) {
Document document;
try {
document = documentParser.parseDocument(query);
} catch (Exception e) {}
Optional<Node> queryNode = document.getChildren().stream()
.filter(n -> (n.getClass() == OperationDefinition.class))
.findFirst();
return checkDepthLimit(queryNode.get());
}
private int checkDepthLimit(Node queryNode) {
int depth = 0;
String nodeType = queryNode.getClass().getSimpleName().toUpperCase();
if (nodeType.equals("FIELD")) {
if (!queryNode.getChildren().isEmpty()) {
depth += 1;
}
}
List<Node> nodeChildren = queryNode.getChildren();
for (int i = 0; i < nodeChildren.size(); i++) {
depth += checkDepthLimit(nodeChildren.get(i));
}
return depth;
}
String query = "{
viewer{
viewerId
}"
QueryComplexity c = new QueryComplexity();
int depth = c.checkDepthLimit(query);
我被困住了,如果对递归有更深入了解的人能够帮助我,我将不胜感激。
错误是您迭代子项的地方。正如您已经认识到的那样 ("counting twice"),您将每个子级的深度添加到当前深度,但您应该只添加最深的一个。
这样就可以了:
List<Node> nodeChildren = queryNode.getChildren();
int maxChildDepth = 0;
for (int i = 0; i < nodeChildren.size(); i++) {
final int currentChildDepth = checkDepthLimit(nodeChildren.get(i));
maxChildDepth = Math.max(maxChildDepth, currentChildDepth);
}
depth += maxChildDepth;
return depth;
从 graphql-java v4.0 开始,有一个内置的 QueryTraversal
class to help you with inspecting the query AST and a related instrumentation for limiting the depth: MaxQueryDepthInstrumentation
就像注册任何其他仪器一样注册它:
GraphQL runtime = GraphQL.newGraphQL(schema)
.instrumentation(new MaxQueryDepthInstrumentation(MAX_DEPTH))
.build();
还有 MaxQueryComplexityInstrumentation.
graphql-spqr(由我编写)还具有指定字段复杂性的声明方式:
public static class PetService {
@GraphQLQuery(name = "pet")
@GraphQLComplexity("type == 'big' ? 10 : 2") //JavaScript expression calculating the complexity
public Pet findPet(@GraphQLArgument(name = "type") String type) {
return db.findPetByType(type);
}
}
您可以手动注册ComplexityAnalysisInstrumentation
,同上:
GraphQL runtime = GraphQL.newGraphQL(schema)
.instrumentation(new ComplexityAnalysisInstrumentation(new JavaScriptEvaluator(), MAX_DEPTH))
.build();
或使用 SPQR 的 GraphQLRuntime
包装器:
GraphQL runtime = GraphQLRuntime.newGraphQL(schema)
.maximumQueryComplexity(maxComplexity)
.build();