ARel:为外连接添加其他条件

Der*_*ior 1 activerecord ruby-on-rails arel ruby-on-rails-3.1

我的Rails应用程序中有以下模型:

class Shift < ActiveRecord::Base
  has_many :schedules
  scope :active, where(:active => true)
end

class Schedule < ActiveRecord::Base
  belongs_to :shift
end
Run Code Online (Sandbox Code Playgroud)

我希望生成所有活动班次的集合,并急切加载occurs_on两个给定日期之间的任何相关计划.如果班次在这些日期之间没有时间表,则仍应在结果中返回.

本质上,我想生成相当于的SQL:

SELECT shifts.*, schedules.*
FROM shifts
  LEFT JOIN schedules ON schedules.shift_id = shifts.id
    AND schedules.occurs_on BETWEEN '01/01/2012' AND '01/31/2012'
WHERE shifts.active = 't';
Run Code Online (Sandbox Code Playgroud)

我的第一次尝试是:

Shift.active.includes(:schedules).where("schedules.occurs_on BETWEEN '01/01/2012' AND '01/31/2012')
Run Code Online (Sandbox Code Playgroud)

问题是happen_on过滤是在where子句中完成的,而不是在join中完成的.如果班次在该期间没有时间表,则根本不返回.

我的第二次尝试是使用该joins方法,但这是一个内连接.同样,这将丢弃那段没有时间表的所有班次.

我很沮丧,因为我知道我希望AREL生成的SQL,但我无法弄清楚如何使用API​​来表达它.任何人?

fir*_*ien 6

你可以尝试一些非常原始的AREL.免责声明:我没有实际ScheduleShift类,所以我无法正确测试,但我使用一些现有的表在我自己的机器上进行故障排除.

on = Arel::Nodes::On.new(
  Arel::Nodes::Equality.new(Schedule.arel_table[:shift_id], Shift.arel_table[:id]).\
  and(Arel::Nodes::Between.new(
    Schedule.arel_table[:occurs_on],
    Arel::Nodes::And.new(2.days.ago, Time.now)
  ))
)
join = Arel::Nodes::OuterJoin.new(Schedule.arel_table, on)
Shift.joins(join).where(active: true).to_sql
Run Code Online (Sandbox Code Playgroud)