README.md in graphql-groups-0.1.1 vs README.md in graphql-groups-0.1.2

- old
+ new

@@ -1,47 +1,49 @@ # GraphQL Groups +[![Gem Version](https://badge.fury.io/rb/graphql-groups.svg)](https://badge.fury.io/rb/graphql-groups) [![Build Status](https://github.com/hschne/graphql-groups/workflows/Build/badge.svg)](https://github.com/hschne/graphql-groups/workflows/Build/badge.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/692d4125ac8548fb145e/maintainability)](https://codeclimate.com/github/hschne/graphql-groups/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/692d4125ac8548fb145e/test_coverage)](https://codeclimate.com/github/hschne/graphql-groups/test_coverage) -Create flexible and performant aggregation queries with [graphql-ruby](https://github.com/rmosolgo/graphql-ruby) +Statistics and aggregates built on top of [graphql-ruby](https://github.com/rmosolgo/graphql-ruby). ## Installation -Add this line to your application's Gemfile and execute bundler. +Add this line to your application's Gemfile and run `bundle install`. ```ruby gem 'graphql-groups' ``` ```bash $ bundle install ``` ## Usage -Create a new group type to specify which attributes you wish to group by inheriting from `GraphQL::Groups::GroupType` +Create a new group type to specify which attributes you wish to group by inheriting from `GraphQL::Groups::GroupType`: ```ruby class AuthorGroupType < GraphQL::Groups::GroupType scope { Author.all } + by :name by :age end ``` -Include the new type in your schema using the `group` keyword. +Include the new type in your schema using the `group` keyword, and you are done. ```ruby -class QueryType < BaseType +class QueryType < GraphQL::Schema::Object include GraphQL::Groups group :author_groups, AuthorGroupType end ``` -You can then run an aggregation query for this grouping. +You can then run a query to retrieve statistical information about your data, for example the number of authors per age. ```graphql query myQuery{ authorGroups { age { @@ -65,18 +67,78 @@ }, ... ] } } - ``` +## Why? + +`graphql-ruby` lacks a built in way to query statistical data of collections. It is possible to add this functionality by +using `group_by` (see for example [here](https://dev.to/gopeter/how-to-add-a-groupby-field-to-your-graphql-api-1f2j)), +but this performs poorly for large amounts of data. + +`graphql-groups` allows you to write flexible, readable queries while leveraging your database to group and +aggregate data. See [performance](#Performance) for a benchmark. + ## Advanced Usage +#### Grouping by Multiple Attributes + +This library really shines when you want to group by multiple attributes, or otherwise retrieve complex statistical information +within a single GraphQL query. + +For example, to get the number of authors grouped by their name, and then also by age, you could construct a query similar to this: + +```graphql +query myQuery{ + authorGroups { + name { + key + count + groupBy { + age { + key + count + } + } + } + } +} +``` + +```json +{ + "authorGroups":{ + "name":[ + { + "key":"Ada", + "count":2, + "groupBy": { + "age": [ + { + "key":"30", + "count":1 + }, + { + "key":"35", + "count":1 + } + ] + } + }, + ... + ] + } +} +``` + +`graphql-groups` will automatically execute the required queries and return the results in a easily parsable response. + #### Custom Grouping Queries -To customize how items are grouped, you may specify the grouping query by creating a method of the same name in the group type. +To customize which queries are executed to group items, you may specify the grouping query by creating a method of the same name in the group type. ```ruby class AuthorGroupType < GraphQL::Groups::Schema::GroupType scope { Author.all } @@ -109,12 +171,37 @@ end end end ``` -For more examples see the [feature spec](./spec/graphql/feature_spec.rb) and [test schema](./spec/graphql/support/test_schema) +When defining a group type's scope you may access the parents `object` and `context`. +```ruby +class QueryType < GraphQL::Schema::Object + field :statistics, StatisticsType, null: false + + def statistics + Book.all + end +end + +class StatisticsType < GraphQL::Schema::Object + include GraphQL::Groups + + group :books, BookGroupType +end + +class BookGroupType < GraphQL::Groups::Schema::GroupType + # `object` refers to `Book.all` + scope { object.where(author_id: context[:current_person]) } + + by :name +end +``` + +For more examples see the [feature spec](./spec/graphql/feature_spec.rb) and [test schema](./spec/graphql/support/test_schema.rb) + ### Custom Aggregates Per default `graphql-groups` supports aggregating `count` out of the box. If you need to other aggregates, such as sum or average you may add them to your schema by creating a custom `GroupResultType`. Wire this up to your schema by specifying the result type in your group type. @@ -156,23 +243,39 @@ end ``` For more examples see the [feature spec](./spec/graphql/feature_spec.rb) and [test schema](./spec/graphql/support/test_schema) +## Performance + +While it is possible to add grouping to your GraphQL schema by using `group_by` (see [above](#why)) this performs poorly for large amounts of data. The graph below shows the number of requests per second possible with both implementations. + +![benchmark](benchmark/benchmark.jpg) + +The benchmark queries the author count grouped by name, using an increasing number of authors. While the in-memory approach of grouping works well for a small number of records, it is outperformed quickly as that number increases. + +Benchmarks are generated using [benchmark-ips](https://github.com/evanphx/benchmark-ips). The benchmark script used to generate the report be found [here](./benchmark/benchmark.rb) + ## Limitations and Known Issues -*This gem is in early development!*. There are a number of issues that are still being addressed. There is no guarantee -that this libraries API will not change fundamentally from one release to the next. +*This gem is in early development!* There are a number of issues that are still being addressed. There is no guarantee +that this libraries API will not change fundamentally from one release to the next. Please refer to the [issue tracker](https://github.com/hschne/graphql-groups/issues) for a list of known issues. +## Credits + +![Meister](meister.png) + +graphql-groups is supported by and battle-tested at [Meister](https://www.meisterlabs.com/) + ## 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). ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/graphql-groups. 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. +Bug reports and pull requests are welcome on GitHub at https://github.com/hschne/graphql-groups. 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. ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).