当前,我正在尝试最小化rails应用程序中SQL查询的数量。并且使用范围进行记录过滤,发现在我的服务器日志中,该范围触发了SQL查询,尽管它显示了CACHE(0.00毫秒),即使它正在使用记录的属性过滤记录。 (例如,使用obj.status进行过滤)
每次调用
时,我都试图在模型中使用class方法def pdf_files
all.select(&:pdf?)
end
范围,
scope :pdf_files, -> { all.select(&:pdf?) }
它触发sql查询。
我尝试覆盖ActiveRecord :: Associations :: CollectionProxy以包括此方法,
class ActiveRecord::Associations::CollectionProxy
def pdf_files
to_a.select(&:pdf?)
end
end
有效,但是也可以从其他模型中调用此方法。
我尝试使用块来通过诸如此类的额外方法扩展您的关联。
has_many :files, do
def pdf_files
to_a.select(&:pdf?)
end
end
这对我来说有效,它选择了没有任何SQL查询的文件。
但事实是。我需要仅适用于File模型的这些方法,因为与文件模型相关联的所有其他模型都需要这些方法,并且我不想破坏 @ email.files.pdf 这样的rails约定。
还发现在Rails控制台中, 当我输入File :: ProxyCollection时,它给我ActiveRecord :: Associations :: CollectionProxy。但是不知道如何添加方法。
我希望能够调用 @ email.files.pdf?,@ email.files.doc?,以过滤结果,而无需执行任何查询,即使它正在调用CACHE SQL查询。
模型定义
class TestFile < ApplicationRecord
belongs_to :fileable, polymorphic: true, optional: true
scope :document_files, -> { all.select(&:document?) }
scope :ebook_files, -> { all.select(&:ebook?) }
enum file_type: [:document, :ebook, :paper, :article, :picture]
end
class TestEmail < ApplicationRecord
has_many :test_files, as: :fileable
end
class TestPost < ApplicationRecord
has_many :test_files, as: :fileable
end
在控制台中
email = TestEmail.includes(:test_files).first
document_files = email.test_files.document_files
但是如果我做类似的事情,
class TestFile < ApplicationRecord
belongs_to :fileable, polymorphic: true, optional: true
enum file_type: [:document, :ebook, :paper, :article, :picture]
end
class TestEmail < ApplicationRecord
has_many :test_files, as: :fileable do
def document_files
to_a.select(&:document?)
end
end
end
class TestPost < ApplicationRecord
has_many :test_files, as: :fileable do
def document_files
to_a.select(&:document?)
end
end
end
答案 0 :(得分:1)
所以您有一个多态关联,并且您希望以尽可能少的查询来获取关联的数据,对吗?
对于我来说,任务本身(“最小化查询数量”)只有在合理的限制下才有意义。摆脱N + 1总是一件好事,但是尝试在1个查询中执行所有工作或诸如此类的操作不一定是个好主意-复杂的查询可能比随后的几个琐碎的查询要慢。此外,复杂的棘手查询往往容易出错。
现在让我们回到您的代码。首先,这个想法
scope :document_files, -> { all.select(&:document?) }
对我来说看起来很糟糕,原因有两个:
所有内容都已加载到内存中,如果表很大,会很痛。有时这是必要的权衡,但是在这种情况下(不需要任何繁重的处理,只需过滤数据即可)
此“作用域”不是常规的作用域-调用它会为您提供Array
而不是AR关系,因此不能像适当的AR作用域那样链接它。它只是一个有误导性的代码,有气味。只是试一下。像TestFile.document_files.where(<some_extra_conditions>)
一样,您会得到NoMethodError
-可能不是人们从简单易懂的代码中所期望的结果...
在您的情况下,使用关联扩展也没有用-它使您的代码看起来更神秘,但并没有真正给您带来任何好处。假设它以某种特殊的方式工作是错误的-实际上,它的工作与您在关联代理上显式调用to_a.select...
完全一样。
我建议一些更简单,更惯用的东西:
class TestFile < ApplicationRecord
belongs_to :fileable, polymorphic: true, optional: true
scope :document_files, -> { where(file_type: :document) }
scope :ebook_files, -> { where(file_type: :ebook) }
enum file_type: [:document, :ebook, :paper, :article, :picture]
end
...
然后,在适当的地方使用预加载,您可以获得较少数量的查询(无N + 1)。
是否想进一步优化?嗯,有很多方法可以使用更底层的API来执行此操作:例如,您可以将connection#select_all
与任意复杂的查询一起使用,然后从ActiveRecord::Result
手动实例化必要的记录,而无需进行更多查询...但是AR是一个自以为是的ORM框架,如果您要“与之抗争”,您可能很快就会得到混乱且不可维护的代码。
如果您确实需要更灵活的东西,建议您尝试其他选择(续集,ROM)...