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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

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/


Интересные статьи

Интересные статьи

Настройка любой площадки для CMS — это рутинный процесс, который должен быть доведен до автоматизма в каждой уважающей себя компании. А потому частенько воспринимается, как восход солнца — это происхо...
Решения для больших компаний обычно должны выдерживать высокие нагрузки. Когда в штате много десятков тысяч человек, и значительная доля из них ежедневно пользуются ...
Все «за» и «против» 1С-Битрикс, какие есть альтернативы и что выгоднее знать разработчику? Читать далее
Последние достижения в области глубокого обучения привносят существенные улучшения в развитие систем синтеза речи (далее – TTS). Это происходит благодаря применению более эффективных и быстрых ...
В процессе перехода от монолитного приложения к микросервисной архитектуре мы сталкиваемся с новыми проблемами. В монолитном приложении обычно достаточно просто определить, в какой части системы...