Skip to content

Query optimization (field selection and query merging) #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pushrax opened this issue Jul 7, 2015 · 11 comments
Closed

Query optimization (field selection and query merging) #6

pushrax opened this issue Jul 7, 2015 · 11 comments

Comments

@pushrax
Copy link
Contributor

pushrax commented Jul 7, 2015

For the future, it would be good to start thinking about how the Ruby interface should facilitate these concepts. Discussion at graphql/graphql-js#26 brings up some good points.

@rmosolgo do you hang out in IRC anywhere? Might have a few questions for you soon.

@rmosolgo
Copy link
Owner

rmosolgo commented Jul 7, 2015

Woah, cool idea. I've had in mind to allow people to provide their own Resolver or ResolutionStrategy classes. They just have to implement #initialize(...) and #result.

Here are the defaults so far: https://github.com/rmosolgo/graphql-ruby/tree/1e04237f87f48694680375eb092019595e5b3887/lib/graph_ql/query

They're all quite coupled with query execution right now but I can imagine specifying a custom selection resolver, eg

DatabaseAccessType = GraphQL::Type.new do 
  self.selection_resolver = DatabaseQueryReducingResolver 
  self.fields = {
    # ... 
  }
end 

If lots of types use the same resolver, you could make a custom type generator, like

class DatabaseDrivenType  < GraphQL::Type
  def initialize(*args) 
    self.selection_resolver = DatabaseQueryReducingResolver 
    super 
  end
end

DatabaseAccessType = DatabaseDrivenType.new do 
  self.fields = { ... }
end

What do you think of that? A dependency-injection-type approach where you can specify custom resolvers.

(I have that approach in mind for all components: anything can be a "type" as long as it implements such-and-such methods, anything can be a field, etc.)

No, I haven't been on IRC much since the ol' batman days :P

@rmosolgo
Copy link
Owner

Someone was mentioning using the AST to provide this functionality, maybe Visitor would come in handy:

https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graph_ql/parser/visitor.rb

Right now it's only used by Validator.

@rmosolgo
Copy link
Owner

I wonder if Connections will offer something for this case, too

https://graphql.slack.com/archives/general/p1436828850000593

@keithpitt
Copy link
Contributor

Hey @rmosolgo, I was wondering where you got up to with this? I'm looking at including graphql-ruby in the stack of the app I work on, but I'm just putting all the bits together in my head before I tackle it.

@rmosolgo
Copy link
Owner

Here's what I'm trying out now: #15

That exposes information ahead-of-time about what fields the object will serve. I think it will be sufficient, but I'm still working on a real live SQL proof of concept!

@rmosolgo
Copy link
Owner

rmosolgo commented Sep 4, 2015

stopgap: provide access to the AST node via query context: 5fcc6b0

@dylanahsmith
Copy link
Contributor

I've implemented serial query batching in graphql-batch as a query execution strategy. I tried to generalize the problem enough to make it very flexible, so it can support computed fields with multiple queries, where some queries can depend on others.

I haven't prototyped the field selection optimization.

@rmosolgo
Copy link
Owner

Very cool, I'm looking forward to trying it! I've added a link to the readme, too

@rmosolgo
Copy link
Owner

rmosolgo commented Feb 1, 2016

I spent a long time trying to make a two-pass execution strategy where you could thread some value through the query once to "plan" (eg, gather info for Rails includes), then again to actually execute.

It wasn't good. One big blocker is unions / interfaces -- you can't know which part of the query will be executed unless you have the underlying object, but you can't have the object unless you've executed the query up to that point!

That made me realize that graphql-batch is really the way to go. It took some getting used to, but the API is really good once you get the hang of it. I put a simple example with database queries here: https://github.com/rmosolgo/graphql-batch-example

@rmosolgo
Copy link
Owner

Please feel free to reopen this or open a new issue if theres something this library could do better to support this kind of use case!

@rmosolgo
Copy link
Owner

0.16.0 introduces ctx.irep_node. You can inspect the node's children to see all fields which were queried and what types they'll apply to:

ctx.irep_node.children.each do |irep_child|
  irep_child.name # alias or field name 
  irep_child.definition # => <#GraphQL::Field ...> Field definition 
  irep_child.on_types # => Set<types...> Type definitions which this field will apply to
  irep_child.ast_node # => <#GraphQL::Language::Nodes::Field ... >
end 

I haven't had a ton of time to play around with it, but this is the data structure used for query execution now, I think it will provide better support for look-ahead too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants