Arel table 关系除法

Arel table relational division

我正在使用 Rails 5.1 和 PostgreSQL 9.5,我是 Arel 表的新手。我正在尝试创建一个查询(我希望最终与其他范围查询链接),其中 returns 记录所有相关记录与给定输入匹配的位置。

给定数组 weekday_ids: [1, 2, 5, 6],只有 return ranges 具有活动 time_slots 匹配 all 给定 weekday_ids

型号

class Range < ApplicationRecord
  has_many :time_slots
end

class TimeSlot < ApplicationRecord
  belongs_to :range
  belongs_to :weekday
end

return预期结果

的工作示例
def self.qualified_time_slots(weekday_ids = nil)
  weekday_ids ||= [1, 2, 5, 6]

  qualified_ranges = Range.includes(:time_slots).all.map do |range|
    active_time_slots = range.time_slots.where(active: true)
    range if weekday_ids.all? { |day| active_time_slots.map(&:weekday_id).include? day }
  end

  # return
  qualified_ranges.compact
end

当前无法通过 Arel Query 尝试实现与上述方法相同的方法

Range.joins(
  :time_slots
).where(
  time_slots: { active: true }
).where(
  TimeSlot.arel_table[:weekday_id].in(weekday_ids)
)

预期结果

# Should return:
[
  range: {
    id: 1, 
    time_slots: [
      { weekday_id: 1 },
      { weekday_id: 2 },
      { weekday_id: 5 },
      { weekday_id: 6 },
    ]
  },
  range: {
    id: 2, 
    time_slots: [
      { weekday_id: 0 },
      { weekday_id: 1 },
      { weekday_id: 2 },
      { weekday_id: 3 },
      { weekday_id: 4 },
      { weekday_id: 5 },
      { weekday_id: 6 },
    ]
  }
]


# Should NOT return 
[
  range: {
    id: 3, 
    time_slots: [
      { weekday_id: 1 },
      { weekday_id: 2 },
      { weekday_id: 5 },
    ]
  },

  range: {
    id: 4, 
    time_slots: [
      { weekday_id: 0 },
      { weekday_id: 6 },
    ]
  }
]

EDIT - 在此过程中,我遵循了 Joe Celko article on Relational Division 中的示例并创建了此原始 SQL 查询,该查询似乎有效, 但尚未完全测试:

ActiveRecord::Base.connection.exec_query("
  SELECT 
    ts.range_id
  FROM 
    time_slots AS ts
  WHERE 
    ts.active = true
  AND 
    ts.weekday_id IN (#{weekday_ids.join(',')})
  GROUP BY 
    ts.range_id 
  HAVING COUNT(weekday_id) >= #{weekday_ids.length};
")

我仍在对此进行测试,但看起来它正在运行:

Range.joins(
  :time_slots
).where(
  TimeSlot.arel_table[:active].eq(
    true
  ).and(
    TimeSlot.arel_table[:weekday_id].in(
      weekday_ids
    )
  )
).group(
  Range.arel_table[:id]
).having(
  TimeSlot.arel_table[:weekday_id].count(true).gteq(weekday_ids.count)
)