如何删除 N+1 查询
How to remove N+1 queries
我有一个 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 查询来自内部时如何处理这个问题序列化程序,因为它试图查看工具和技术。
如有任何帮助,我们将不胜感激!
尝试嵌套预加载:
Job.preload(:technologies, :tools, company: :listings).order(...).where(...)
您可能非常渴望加载工具和技术,因为您知道序列化程序将使用它们:
Job.eager_load(:listings, :tools, :technologies)
.order("listings.live_date DESC")
.where(category: "developer", listings: { is_active: true })
.first(90)
之后你真的需要重构那个序列化器。 #each
只应在您只对迭代的副作用而不是 return 值感兴趣时使用。使用#map
、#each_with_object
、#inject
等。可以优化这些调用。 return
隐含在 ruby 中,因此如果您早早放弃,则只能显式 return。
class SimpleJobSerializer < ActiveModel::Serializer
# ...
def tool_list
object.tools.map { |t| { label: tool.label, icon: tool.icon } }
end
# ...
end
我有一个 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 查询来自内部时如何处理这个问题序列化程序,因为它试图查看工具和技术。
如有任何帮助,我们将不胜感激!
尝试嵌套预加载:
Job.preload(:technologies, :tools, company: :listings).order(...).where(...)
您可能非常渴望加载工具和技术,因为您知道序列化程序将使用它们:
Job.eager_load(:listings, :tools, :technologies)
.order("listings.live_date DESC")
.where(category: "developer", listings: { is_active: true })
.first(90)
之后你真的需要重构那个序列化器。 #each
只应在您只对迭代的副作用而不是 return 值感兴趣时使用。使用#map
、#each_with_object
、#inject
等。可以优化这些调用。 return
隐含在 ruby 中,因此如果您早早放弃,则只能显式 return。
class SimpleJobSerializer < ActiveModel::Serializer
# ...
def tool_list
object.tools.map { |t| { label: tool.label, icon: tool.icon } }
end
# ...
end