Арель стол реляционного деления - PullRequest
0 голосов
/ 26 апреля 2018

Я использую Rails 5.1 с PostgreSQL 9.5 и новичок в таблицах Arel. Я пытаюсь создать запрос (который, я надеюсь, в конечном итоге объединить с другими запросами областей), который возвращает записи, в которых все связанные записи соответствуют заданному входу.

Учитывая массив weekday_ids: [1, 2, 5, 6], вернуть только ranges с активными time_slots соответствующими всеми указанными weekday_ids

Модель

class Range < ApplicationRecord
  has_many :time_slots
end

class TimeSlot < ApplicationRecord
  belongs_to :range
  belongs_to :weekday
end

Рабочий пример, который возвращает ожидаемые результаты

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

Текущая неработающая попытка запроса Ареля для достижения того же метода, что и выше

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 - Работая таким образом, я следовал примерам из статьи Джо Селько о реляционном делении и создал этот необработанный 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};
")

1 Ответ

0 голосов
/ 27 апреля 2018

Я все еще тестирую это, но похоже, что это работает:

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)
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...