【问题标题】:ActiveRecord - nested includesActiveRecord - 嵌套包含
【发布时间】:2017-12-29 16:22:31
【问题描述】:

我正在尝试在 Rails 5 中执行以下查询,当我访问每个 events.contact 时它不会触发 N+1 查询:

events = @company.recipients_events
                 .where(contacts: { user_id: user_id })

我尝试了一些 .includes、.references 和 .eager_loading 的组合,但都没有奏效。当我访问 events.contact 时,其中一些返回 SQL 错误,而另一些返回 nil 对象。

这是我的联想的简要版本:

class Company
   has_many :recipients
   has_many :recipients_events, through: :recipients, source: :events
end

class Recipient
   belongs_to :contact
   has_many :events, as: :eventable
end

class Event
   belongs_to :eventable, polymorphic: true
end

class Contact
   has_many :recipients
end

实现我需要的正确方法是什么?

【问题讨论】:

  • sn-p 你的看法是什么
  • 我正在使用 rails 控制台进行测试
  • 你可以尝试使用类似@company.recipients_events.includes(:contact) .where(contacts: { user_id: user_id }) 的东西。并显示错误(如果有)
  • 以下是错误:“无法将 'Event' 加入名为 'contact' 的关联;也许你拼错了?”。我相信这是因为 Event 与 Recipient 相关联,Recipient 与 Contact 相关联。
  • 我也试过了:@company.recipients_events.includes(flow_recipient: :contact).where(contacts: { user_id: user_id })。在这种情况下,没有错误,但每次我访问 event.contact 时,Rails 都会针对 FlowRecipient load 和 Contact load 执行两个新查询。

标签: ruby-on-rails ruby activerecord associations


【解决方案1】:

如果您在加载 @company 时已经知道 user_id,我会这样做:

@company = Company.where(whatever)
  .includes(recipients: [:recipients_events, :contact])
  .where(contacts: { user_id: user_id })
  .take
events = @company.recipients_events

或者,如果不是:

events = Company.where(whatever)
  .includes(recipients: [:recipients_events, :contact])
  .where(contacts: { user_id: user_id })
  .take
  .recipients_events

ActiveRecord 查询计划器将确定它认为获取该数据的最佳方式。如果没有where,每个表可能需要 1 个查询,但是当您链接 includes().where() 时,您可能会得到 2 个带有左外连接的查询。

【讨论】:

  • events = Company.includes(recipients: [:events, :contact]).where(id: 2, contacts: { user_id: 6 }).first.recipients_events。当我执行此操作时,Rails 执行 2 个查询:一个加入所有内容,一个选择所有公司事件。直到这里,包括似乎正在工作。但是,当我访问 events.first.contact 时,Rails 会执行另外两个查询:一个加载收件人,另一个加载联系人。每次我访问新事件时都会发生这种情况,例如 events.second.contact。这是预期的行为吗?我认为它还会加入或预加载所有收件人和联系人。
  • 您要确保include 您要使用的命名关联。在这种情况下,请尝试将 recipients_events 重组为您的 includes 语句。例如:events = Company.where(id: 2).includes(recipients: [:recipients_events, :contact]).where(contacts: { user_id: 6 }).take.recipients_eventsevents = Company.where(id: 2).includes(recipients: [:events, :contact]).where(contacts: { user_id: 6 }).take.recipients.map(&:events)
  • 这是难以辨认的评论,我将其添加到答案中。
猜你喜欢
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-12
  • 2014-08-29
相关资源
最近更新 更多