0

如果不触发 N+1 查询,我似乎无法执行关联查询。

假设我举办派对。我有很多朋友,每次有朋友来参加聚会,他们都会创建一个 Presence。

所以:

Presence.belongs_to :party
Presence.belongs_to :friend

Friend.has_many :presences
Party.has_many :presences

到目前为止,一切都很好。

我想获得我每个朋友的列表,知道他们是否在这个聚会上,而不触发 N+1 查询。

我的数据集如下所示:

friends: [
  {name: "Dave Vlopment", presences: [{created_at: "8pm", party_id: 2012}]},
  {name: "Brett E. Hardproblem", presences: [nil]},
  {name: "Ann Plosswan-Quarry", presences: [{created_at: "10pm", party_id: 2012}]},
  ...
]

等等。

当然,我有很多朋友,也参加过很多聚会。(这当然是一个虚构的例子。)

我会做:

Friend.all.includes(:presence).map{ |them| them.parties }

# But then, `them.parties` is not filtered to tonight's party. 

Friend.all.includes(:presence).map{ |them| them.parties.where(party_id: pid) }

# And there I have an N+1.

我总是可以在 Ruby 层进行过滤:

Friend.all.includes(:presence).map{ |them| them.parties.select{ |it| it.party_id = party.id } }

但这as_json(includes: {})与等等一起工作得非常糟糕。我发现这很容易出错,因为我将对结果进行计算。

而且我举办了很多派对,你知道吗?(仍然是虚构的)

如果我在第一个查询中的位置,我会丢失左连接:

Friend.all.includes(:presence).where(party: party)

没想到今晚,布雷特和一帮一直都在的朋友缺席了。(这不保证是虚构的体验)

我只会看到在场的朋友。

如果我通过party,当然我也不会看到谁缺席。

现在我知道有一些方法可以在 SQL 中做到这一点,还有其他方法我们可以围绕一些 Ruby 来将它们组合在一起。

但是,我正在寻找一种在 Activerecord 中执行此操作的“一流”方法,而无需获得 N+1。

有没有办法只使用 Activerecord 工具来做到这一点?我还没有找到任何东西。

4

1 回答 1

0

我不确定这是否符合您对“一流”方式的期望。

但是您可以使用这种方法来避免 N+1

  # fetch all friends
  friends = Friend.all

  # fetch all presences. grouped by friend_id
  grouped_presences = Presence.all.group_by(&:friend_id)

  # arrange data
  data = []
  friends.each do |friend|
    json = friend.as_json
    json["presences"] = grouped_presences[friend.id].as_json
    data << json
  end

  puts data

它只执行 2 个查询

SELECT `friends`.* FROM `friends`
SELECT `presences`.* FROM `presences`
于 2020-06-20T01:46:07.873 回答