【问题标题】:How to remove N+1 queries如何删除 N+1 个查询
【发布时间】:2020-02-14 16:11:14
【问题描述】:

我有一个 Rails API,目前有很多我想减少的 N+1 查询。

如您所见,它在返回数据之前经历了很多循环。

关系如下:

公司模式

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

作业序列化器

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 查询来自序列化程序内部时,我不确定如何处理它尝试着眼于工具和技术。

任何帮助将不胜感激!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord rails-activerecord active-model-serializers


    【解决方案1】:

    您可能非常渴望加载工具和技术,因为您知道序列化程序会使用它们:

    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等。这些调用可以优化。 return 在 ruby​​ 中是隐含的,因此只有在您提早退出时才显式返回。

    class SimpleJobSerializer < ActiveModel::Serializer
      # ...
      def tool_list
        object.tools.map { |t| { label: tool.label, icon: tool.icon } }
      end
      # ...
    end
    

    【讨论】:

    • 这非常有用!非常感谢:)
    【解决方案2】:

    尝试嵌套预加载:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-30
      • 1970-01-01
      相关资源
      最近更新 更多