我遇到了一个性能问题,在过滤器之前调用了Processing.all
,而过滤器又在Processing
模型上运行了第二个查询。 Processing
有大量的记录,并将它们全部加载到内存中,只是对它们运行第二个查询导致RAM的峰值,我想修复。
控制器中的行是:
@filter = ProcessingFilter.new(base_collection: Processing.all, options: params[:filter])
如您所见,Processing.all
作为base_collection
参数传递到此处。
ProcessingFilter
然后运行:
class ProcessingFilter
def initialize(base_collection:, options: {})
@base_collection = base_collection
@options = options
end
def collection
@collection ||= begin
scope = @base_collection
if condition1
scope = scope.joins(:another_entry).where(another_entry: {customer_id: customer_id})
end
if condition2
scope = scope.where('created_at >= ?', created_at_start)
end
if condition3
scope = scope.where('created_at <= ?', created_at_end)
end
if condition4
scope = scope.where(number: processing_number)
end
scope
end
end
end
此过滤器将创建单个ActiveRecord查询的各种if条件链接在一起,这很好。
问题是我无法取消这个过滤器,因为它设置了一些在别处使用的实例变量。我试图想出一种聪明的方法,让第一次没有Processing.all
查询运行,而是让它将其他选项链接在一起,尽管它是在一个单独的类中。这可能吗?
提前致谢!
答案 0 :(得分:2)
免责声明:这本身并不是答案,但因为它太长而需要输入代码:
Processing.all
尚未将记录加载到内存中,因为记录“延迟加载”,因为它只返回ActiveRecord_Relation
个对象。只有在Array
,each
,first
,last
或map
上使用[]
方法后,它才会启动实际上将数据库中的记录提取到内存中。
演示:
processings = Processing.all
puts processings.class
# => Processing::ActiveRecord_Relation
puts processings.first.class
# Processing Load (2.9ms) SELECT "processings".* FROM "processings" ORDER BY "processings"."id" ASC LIMIT 1
# => Processing
既然我们知道.all
并不急于将记录立即加载到内存中,那么我们需要找出当您调用@filter = ProcessingFilter.new(base_collection: Processing.all, options: params[:filter])
时代码仍然会立即将记录加载到内存中的原因
仅限于您显示的代码,我在ProcessingFilter
类中看不到会触发将记录加载到内存中的任何内容(没有调用Array
方法将它们加载到内存中);他们都只是ActiveRecord_Relation
个对象。因此,我目前的猜测是在两个过滤器之间的某个位置,您正在调用Array
方法:
@filter = ProcessingFilter.new(base_collection: Processing.all, options: params[:filter])
# An Array method is called:
@filter.collection.first
如果您在rails console
中执行此操作,则需要附加; nil
以防止“处理”每行调用的值,因为它会立即加载记录:
@filter = ProcessingFilter.new(base_collection: Processing.all, options: params[:filter]); nil
@filter.collection.first; nil
答案 1 :(得分:1)
如果您不使用过滤 default_scopes (或者想要忽略默认过滤),Processing.unscoped
就可以了。
顺便提一下,您目前使用的是哪个版本的导轨?
有关已弃用的.scoped
方法的其他链接:
With Rails 4, Model.scoped is deprecated but Model.all can't replace it
答案 2 :(得分:0)
我会考虑将每个条件提取到自己的范围方法中,然后弃用这个ProcessingFilter
类。它看起来像装饰设计不好。
您可以使用base_collection值来确定被调用的模型,然后将对ProcessingFilter
的调用代理到初始化中的相应范围。应该省去一些麻烦,只需调用base_collection范围。我怀疑你因为这种用法而得到重复的查询调用。