GraphQL без N+1 это легко благодаря N1Loader

от автора

N1Loader разработан для легкого избежания N+1 проблемы
любого типа. К счатью, гем очень легко интегрировать в GraphQL
API. Без дальнейших отлагательств, давайте рассмотрим простой, но самодостаточный пример.

# Добавляем N1Loader с интеграцией ArLazyPreload  require "n1_loader/ar_lazy_preload" require 'graphql'  # Создаем SQLite таблицы в памяти, не относится к библиотеке. require_relative 'context/setup_database' # ArLazyPreload требует Rails приложение. Этот код необходим, чтобы избежать этого. require_relative 'context/setup_ar_lazy'  class User < ActiveRecord::Base   has_many :payments    n1_optimized :payments_total do |users|     # Fetch payments for a group of users we will preload in a single query     total_per_user = Payment.group(:user_id).where(user: users).sum(:amount).tap { |h| h.default = 0 }      users.each do |user|       total = total_per_user[user.id]       # No promises here, simply add a value for to a user.       fulfill(user, total)     end   end end  class Payment < ActiveRecord::Base   belongs_to :user    validates :amount, presence: true end  # Запускаем ArLazyPreload глобально ArLazyPreload.config.auto_preload = true # Или используем preload_associations_lazily когда загружаем объекты из базы данных  class UserType < GraphQL::Schema::Object   field :payments_total, Integer end  class QueryType < GraphQL::Schema::Object   field  :users, [UserType]    def users     User.all   end end  class Schema < GraphQL::Schema   query QueryType end  query_string = <<~GQL   {     users {       paymentsTotal     }   } GQL  # Исполняем запрос без N+1! p Schema.execute(query_string)['data']

На этом волшебство N1Loader не заканчивается, так как библиотека так же поддерживает аргументы, которые могут приходить с запросом. И все это без N+1!

class User < ActiveRecord::Base   n1_optimized :payments_total do     argument :from     argument :to      def perform(users)       total_per_user =         Payment           .group(:user_id)           .where(created_at: from..to)           .where(user: users)           .sum(:amount)           .tap { |h| h.default = 0 }        users.each do |user|         total = total_per_user[user.id]         fulfill(user, total)       end     end   end end  class UserType < GraphQL::Schema::Object   field :payments_total, Integer do     argument :from, Time     argument :to, Time   end end  query_string = <<~GQL   {     users {       paymentsTotal     }   } GQL  # Нет N+1 и больше не будет! p Schema.execute(query_string, variables: {from: 3.days.ago, to: 1.day.ago})['data']

Описывайте загрузку данные единожды — и используйте в любой части приложения без N+1!

Посмотрите страницу N1Loader с другими возможностями и попробуйте в своем проекте!


ссылка на оригинал статьи https://habr.com/ru/post/663834/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *