README.md in batch-loader-1.4.1 vs README.md in batch-loader-1.5.0
- old
+ new
@@ -35,13 +35,13 @@
* [Batch key](#batch-key)
* [Caching](#caching)
* [Replacing methods](#replacing-methods)
* [Installation](#installation)
* [API](#api)
+* [Related tools](#related-tools)
* [Implementation details](#implementation-details)
* [Development](#development)
-* [Related gems](#related-gems)
* [Contributing](#contributing)
* [Alternatives](#alternatives)
* [License](#license)
* [Code of Conduct](#code-of-conduct)
@@ -233,27 +233,42 @@
Batching is particularly useful with GraphQL. Using such techniques as preloading data in advance to avoid N+1 queries can be very complicated, since a user can ask for any available fields in a query.
Let's take a look at the simple [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) schema example:
```ruby
-Schema = GraphQL::Schema.define do
- query QueryType
+class MyProjectSchema < GraphQL::Schema
+ query Types::QueryType
end
-QueryType = GraphQL::ObjectType.define do
- name "Query"
- field :posts, !types[PostType], resolve: ->(obj, args, ctx) { Post.all }
+module Types
+ class QueryType < Types::BaseObject
+ field :posts, [PostType], null: false
+
+ def posts
+ Post.all
+ end
+ end
end
-PostType = GraphQL::ObjectType.define do
- name "Post"
- field :user, !UserType, resolve: ->(post, args, ctx) { post.user } # N+1 queries
+module Types
+ class PostType < Types::BaseObject
+ name "Post"
+
+ field :user, UserType, null: false
+
+ def user
+ post.user # N+1 queries
+ end
+ end
end
-UserType = GraphQL::ObjectType.define do
- name "User"
- field :name, !types.String
+module Types
+ class UserType < Types::BaseObject
+ name "User"
+
+ field :name, String, null: false
+ end
end
```
If we want to execute a simple query like the following, we will get N+1 queries for each `post.user`:
@@ -265,31 +280,36 @@
name
}
}
}
"
-Schema.execute(query)
+MyProjectSchema.execute(query)
```
To avoid this problem, all we have to do is to change the resolver to return `BatchLoader::GraphQL` ([#32](https://github.com/exAspArk/batch-loader/pull/32) explains why not just `BatchLoader`):
```ruby
-PostType = GraphQL::ObjectType.define do
- name "Post"
- field :user, !UserType, resolve: ->(post, args, ctx) do
- BatchLoader::GraphQL.for(post.user_id).batch do |user_ids, loader|
- User.where(id: user_ids).each { |user| loader.call(user.id, user) }
+module Types
+ class PostType < Types::BaseObject
+ name "Post"
+
+ field :user, UserType, null: false
+
+ def user
+ BatchLoader::GraphQL.for(post.user_id).batch do |user_ids, loader|
+ User.where(id: user_ids).each { |user| loader.call(user.id, user) }
+ end
end
end
end
```
And setup GraphQL to use the built-in `lazy_resolve` method:
```ruby
-Schema = GraphQL::Schema.define do
- query QueryType
+class MyProjectSchema < GraphQL::Schema
+ query Types::QueryType
use BatchLoader::GraphQL
end
```
That's it.
@@ -302,11 +322,11 @@
BatchLoader.for(post.user_id).batch(default_value: NullUser.new) do |user_ids, loader|
User.where(id: user_ids).each { |user| loader.call(user.id, user) }
end
```
-For batches where the value is some kind of collection, such as an Array or Hash, `loader` also supports being called with a block, which yields the _current_ value, and returns the _next_ value. This is extremely useful for 1:Many relationships:
+For batches where the value is some kind of collection, such as an Array or Hash, `loader` also supports being called with a block, which yields the _current_ value, and returns the _next_ value. This is extremely useful for 1:Many (`has_many`) relationships:
```ruby
BatchLoader.for(user.id).batch(default_value: []) do |user_ids, loader|
Comment.where(user_id: user_ids).each do |comment|
loader.call(comment.user_id) { |memo| memo << comment }
@@ -439,26 +459,30 @@
| `key` | `nil` | Pass custom key to uniquely identify the batch block. |
| `items` | - | List of collected items for batching. |
| `loader` | - | Lambda which should be called to load values loaded in batch. |
| `args` | `{default_value: nil, cache: true, replace_methods: true, key: nil}` | Arguments passed to the `batch` method. |
+## Related tools
+
+These gems are built by using `BatchLoader`:
+
+* [decidim-core](https://github.com/decidim/decidim/) – participatory democracy framework made with Ruby on Rails.
+* [ams_lazy_relationships](https://github.com/Bajena/ams_lazy_relationships/) – ActiveModel Serializers add-on for eliminating N+1 queries.
+* [batch-loader-active-record](https://github.com/mathieul/batch-loader-active-record/) – ActiveRecord lazy association generator to avoid N+1 DB queries.
+
+`BatchLoader` in other programming languages:
+
+* [batch_loader](https://github.com/exaspark/batch_loader) - Elixir implementation.
+
## Implementation details
See the [slides](https://speakerdeck.com/exaspark/batching-a-powerful-way-to-solve-n-plus-1-queries) [37-42].
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
-
-## Related gems
-
-These gems are built by using `BatchLoader`:
-
-* [decidim-core](https://github.com/decidim/decidim/) – participatory democracy framework made with Ruby on Rails.
-* [ams_lazy_relationships](https://github.com/Bajena/ams_lazy_relationships/) – ActiveModel Serializers add-on for eliminating N+1 queries.
-* [batch-loader-active-record](https://github.com/mathieul/batch-loader-active-record/) – ActiveRecord lazy association generator to avoid N+1 DB queries.
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/exAspArk/batch-loader. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.