GraphQL 多级嵌套查询,如果不手动处理,会导致很严重的 N+1 问题,甚至 (N+1)^n
技术背景是 GraphQL + ActiveRecord + 关系型数据库
举个例子
您原来的 Resolver 代码可能如下所示:
class Resolvers::ProfileResolver < GraphQL::Function
description 'User profile'
type Types::ProfileType
def _call(_, args, ctx)
ctx[:current_user]
end
end
当查询如下的时候:
query{
profile{
id
works{
comments{
replyUser{
name
}
content
}
name
id
likes{
owner{
name
works{
name
likes{
owner{
name
}
}
}
}
}
}
name
}
}
它将导致多级嵌套 N + 1 问题。
如果你要解决它,你可以像这样写:
class Resolvers::ProfileResolver < OptimizedFunction
description 'User profile'
type Types::ProfileType
def _call(_, args, ctx)
User.includes([works: [comments: :reply_user, likes: [owner: [works: [likes: :owner]]]]]).find(ctx[:current_user].id)
end
end
您将在每个 Resolver 中手动解决 N + 1 问题。更糟糕的是,即使只请求了一个字段,也不得不向数据库查询 includes 的所有表。
graphql-batch gem
Resolver
从继承 GraphQL::Function
改为继承于OptimizedFunction
。_call
方法而不是call
includes_klass(your_model_name)
替换 includes 语句。class Resolvers::ProfileResolver < OptimizedFunction
description 'User profile'
type Types::ProfileType
def _call(_, args, ctx)
includes_kclass(User).find(ctx[:current_user].id)
end
end
原理是解析最开始请求的 json 多叉树,与 ActiveRecord 的模型关联关系对比,从请求 json 多叉树中过滤出嵌套的关联关系树。
希望能帮助到和我一样喜欢 rails 和 graphql 的人,欢迎提出改进意见。