Как удалить N + 1 запросов - PullRequest
0 голосов
/ 14 февраля 2020

У меня есть rails API, который в настоящее время содержит довольно много N + 1 запросов, которые я бы хотел сократить.

enter image description here

Как видите, перед возвратом данных проходит немало циклов.

Отношения таковы:

Модель компании

class Company < ApplicationRecord
  has_many :jobs, dependent: :destroy
  has_many :contacts, dependent: :destroy
  has_many :listings
end

Модель работы

class Job < ApplicationRecord
  belongs_to :company
  has_many :listings
  has_and_belongs_to_many :technologies
  has_and_belongs_to_many :tools

  scope :category, -> ( category ) { where category: category }
end

Листинг Модальный

class Listing < ApplicationRecord
  belongs_to :job, dependent: :destroy
  belongs_to :company, dependent: :destroy

  scope :is_active, -> ( active ) { where is_active: active }
end

Job Serializer

class SimpleJobSerializer < ActiveModel::Serializer
  attributes  :id,
              :title, 
              :company_name,                  

  attribute :technology_list, if: :technologies_exist
  attribute :tool_list, if: :tools_exist

  def technology_list
    custom_technologies = []

    object.technologies.each do |technology|
      custom_technology = { label: technology.label, icon: technology.icon }
      custom_technologies.push(custom_technology)
    end

    return custom_technologies
  end

  def tool_list
    custom_tools = []

    object.tools.each do |tool|
      custom_tool = { label: tool.label, icon: tool.icon }
      custom_tools.push(custom_tool)
    end

    return custom_tools    
  end

  def tools_exist
    return object.tools.any?
  end

  def technologies_exist
    return object.technologies.any?
  end

  def company_name
    object.company.name
  end
end

Текущий запрос в контроллере

Job.eager_load(:listings).order("listings.live_date DESC").where(category: "developer", listings: { is_active: true }).first(90)

Я пытался использовать eager_load, чтобы присоединиться к спискам вакансий, чтобы сделать запрос более эффективным, но я ' Я не уверен, как справиться с этим, когда некоторые из n + 1 запросов поступают изнутри сериализатора, когда он пытается взглянуть на инструменты и технологии.

Любая помощь будет принята с благодарностью!

Ответы [ 2 ]

1 голос
/ 14 февраля 2020

Возможно, вам не терпелось загрузить инструменты и технологии, поскольку вы знаете, что сериализатор собирается их использовать:

Job.eager_load(:listings, :tools, :technologies)
   .order("listings.live_date DESC")
   .where(category: "developer", listings: { is_active: true })
   .first(90)

После этого вам действительно необходимо провести рефакторинг этого сериализатора. #each следует использовать только тогда, когда вас интересуют только побочные эффекты итерации, а не возвращаемое значение. Используйте #map, #each_with_object, #inject et c. Эти звонки могут быть оптимизированы. return неявно присутствует в ruby, поэтому вы возвращаетесь в явном виде только в случае раннего освобождения от ответственности.

class SimpleJobSerializer < ActiveModel::Serializer
  # ...
  def tool_list
    object.tools.map { |t| { label: tool.label, icon: tool.icon } }
  end
  # ...
end
0 голосов
/ 14 февраля 2020

Попробуйте вложенную предзагрузку:

Job.preload(:technologies, :tools, company: :listings).order(...).where(...)
...