ruby-on-rails ActiveRecord中自定义连接的调用加载

plicqrtu  于 2023-10-21  发布在  Ruby
关注(0)|答案(3)|浏览(118)

我有一张有很多预订的餐馆的table。现在我想查询所有的餐厅,如果他们有保留2人,它应该预载这些协会。如果没有2人的预订,它仍然应该返回餐厅,但有一个空的关联。
为此,我尝试了以下查询:

Restaurant.eager_load(:reservations).where("number_of_people = ?", 2)

然而,这并不起作用,因为它会丢弃所有有预订的餐厅,但没有一家是两个人的。
所以我想做的是把这个条件变成连接条件。例如:

Restaurant.joins('LEFT OUTER JOIN \"reservations\" ON \"reservations\".\"restaurant_id\" = \"restaurants\".\"id\" AND \"reservations\".\"number_of_people\" = ?', 2)

这将给予我需要的结果,但是这不是预加载“保留”关联,而是导致N+1问题。
eager_load似乎不接受自定义查询。我发现了这些线索:https://github.com/rails/rails/issues/12270JOIN ON ... AND conditions when eager loading association in ActiveRecord,但没有提供解决方案。

omvjsjqw

omvjsjqw1#

我觉得迪帕克是对的。试试这个:

#retaurant.rb
 has_many :reservations_for_two, ->{number_of_people: 2}, class_name: 'Reservation'

然后:

Restaurant.preload(:reservations_for_two) #(for two separate queries)

Restaurant.eager_load(:reservations_for_two) #(one join query)
2ic8powd

2ic8powd2#

试试Restaurant.joins(:reservations).includes(:reservations).where("reservations.number_of_people = ?", 2)

pxyaymoc

pxyaymoc3#

我一直在努力寻找,从Rails 7.0开始,似乎不可能用动态条件来连接它。它可以与静态条件一起工作,或者你可以加入它并使用where过滤它(正如其他答案所建议的那样),但是当这两种解决方案都不适合时,有一些用例,我不认为有一个解决方案可以使它与ActiveRecord急切加载一起工作。你只能跳过使用ActiveRecord即时加载功能,直接创建一个单独的原始SQL请求并获取数据,比如:

sql = Restaurant.joins('LEFT OUTER JOIN "reservations" ON "reservations"."restaurant_id" = "restaurants"."id" AND "reservations"."number_of_people" = ?', 2).select('restaurants.*, reservations.id as r_id').to_sql
rows = ActiveRecord::Base.connection.execute(sql)
grouped = rows.group_by {|row| row['id'] }
records = grouped.map do |id, row|
  row_hash = row[0].except('r_id')
  restaurant = Restaurant.new(row_hash)

  reservations = row.map {|tt| tt['r_id'] }.map {|id| Reservation.new(id:) }
  restaurant.association(:reservations).target = reservations
end

# access filtered reservations:
restaurant.reservations.each { ... }

这只是一个最小的例子,我只设置了id的保留,因为我不知道你还有什么其他列,但这种方法允许你添加其他字段到Reservation示例。这种方法也有它的警告,例如像这样创建的AR示例将具有persisted?等于false,除非您显式地重新加载它们(这将产生另一个SQL查询)。但只要你不关心persisted?值,你就应该没事。

相关问题