包括一个左联接的has_many关联条件

时间:2020-06-20 00:45:31

标签: ruby-on-rails activerecord has-many select-n-plus-1

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

假设我主办缔约方。我有很多朋友,每当一个朋友参加聚会时,他们都会创建一个状态。

等等:

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}]},
  ...
]

以此类推。

我当然有一个 lot 个朋友,并且参加一个 lot 个聚会。 (这当然是一个虚构的例子。)

我会做的:

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)

我不知道今晚布雷特(Brett)和一堆永远在场的朋友不在。 (不保证这是一种虚构的经历)

我只会看到在场的朋友。

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

现在,我知道有一些方法可以在SQL中执行此操作,还有其他方法可以纠缠一些Ruby以将其组合在一起。

但是,我正在ActiveRecord中寻找一种“一流”的方法,而又不会获得N + 1。

是否可以仅使用Activerecord工具来执行此操作?我还没有发现任何东西。

1 个答案:

答案 0 :(得分: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`