- PVSM.RU - https://www.pvsm.ru -
N1Loader [1] разработан для легкого избежания N+1 проблемы [2]
любого типа. К счатью, гем очень легко интегрировать в 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 [3] не заканчивается, так как библиотека так же поддерживает аргументы, которые могут приходить с запросом. И все это без 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 [3] с другими возможностями и попробуйте в своем проекте!
Автор:
djezzzl
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/374645
Ссылки в тексте:
[1] N1Loader: https://github.com/djezzzl/n1_loader#killer-feature-for-graphql-api
[2] N+1 проблемы: https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping
[3] N1Loader: https://github.com/djezzzl/n1_loader
[4] Источник: https://habr.com/ru/post/663834/?utm_source=habrahabr&utm_medium=rss&utm_campaign=663834
Нажмите здесь для печати.