ruby-on-rails 缓存ActiveRecord中的优先级包含

6za6bjd0  于 2023-07-01  发布在  Ruby
关注(0)|答案(1)|浏览(151)

我有三个集合CompanyPersonEmail一个公司有很多人,一个人有很多电子邮件
如果我做一些

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-谢谢!

lh80um4z

lh80um4z1#

在Rails4.2中,没有内置的机制来检查关联是否已经被预加载,并自动避免额外的查询。
在这种情况下,您可以使用ActiveRecord提供的loaded?方法手动检查是否加载了关联。以下是getAllCompanyEmails方法的更新版本,其中包含以下检查:

def getAllCompanyEmails(company)
  if company.people.loaded?
    company.people.flat_map(&:emails)
  else
    company.people.includes(:emails).flat_map(&:emails)
  end
end

在此更新的实现中,如果已经加载了公司的人员关联,则它直接从预加载的数据中检索电子邮件。否则,它执行包含查询以加载关联的电子邮件,然后检索它们。
通过使用flat_map(&:emails),我们避免了在结果数组上调用flatten。
请注意,这种方法是Rails 4.2特有的,因为Rails的新版本提供了更好的预加载关联处理。

相关问题