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.