我有三个集合Company
、Person
和Email
一个公司有很多人,一个人有很多电子邮件
如果我做一些
company_with_emails_preloaded = Company.includes(people: [:emails]).where(...)
我可以执行company.people[0].emails
,而无需额外的查询/避免n+1查询问题
但是,假设我有一个名为getAllCompanyEmails
的函数,它执行以下操作
def getAllCompanyEmails(company)
company.people.includes(:emails).map { |person| person.emails } .flatten
end
在company
上调用.includes
,如果它还没有被.includes
处理过。
当我调用getAllCompanyEmails(company_with_emails_preloaded)
时,它运行另一个连接查询,忽略了我之前运行的.includes
!
我的问题是:有没有一种方法可以修改includes
,以便在触发另一个查询之前检查数据是否已经包含在内?我认为最坏的情况是,我可以编写某种.includes
帮助程序来检查.association(:key_name).loaded
的所有内容,但我觉得Rails中应该已经存在一些东西。
我在使用rails 4.2.11
和ruby 2.6.8
-谢谢!
1条答案
按热度按时间lh80um4z1#
在Rails4.2中,没有内置的机制来检查关联是否已经被预加载,并自动避免额外的查询。
在这种情况下,您可以使用ActiveRecord提供的
loaded?
方法手动检查是否加载了关联。以下是getAllCompanyEmails
方法的更新版本,其中包含以下检查:在此更新的实现中,如果已经加载了公司的人员关联,则它直接从预加载的数据中检索电子邮件。否则,它执行包含查询以加载关联的电子邮件,然后检索它们。
通过使用
flat_map(&:emails)
,我们避免了在结果数组上调用flatten。请注意,这种方法是Rails 4.2特有的,因为Rails的新版本提供了更好的预加载关联处理。