Одним из решений является введение counter_cache:
# add a machine_updates_count integer database column (with default 0)
# and add this to your Machine model:
counter_cache :machine_updates_count
, а затем добавьте OR machine_updates_count = 0
к условиям SQL.
Однако вы также можете решить проблему без счетчика кэша, используя LEFT JOIN:
named_scope :needs_updates,
:select => "machines.*, MAX(machine_updates.date) as last_update",
:joins => "LEFT JOIN machine_updates ON machine_updates.machine_id = machines.id",
:group => "machines.id",
:having => ["last_update IS NULL OR last_update < ?", lambda{ UPDATE_THRESHOLD.seconds.ago }]
Левое объединение необходимо, чтобы вы были уверены, что просматриваете самое последнее MachineUpdate (с MAX-датой).
Обратите внимание, что вы должны поместить свое условие в lambda
, чтобы оно оценивалось каждый раз при выполнении запроса. В противном случае он будет оцениваться только один раз (когда ваша модель загружается при загрузке приложения), и вы не сможете найти машины, которые нуждаются в обновлениях с момента запуска приложения.
UPDATE:
Это решение работает в MySQL и SQLite, но не в PostgreSQL. Postgres не позволяет именовать столбцы в предложении SELECT, которые не используются в предложении GROUP BY (см. обсуждение ). Я очень незнаком с PostgreSQL, но я все заработал, как и ожидалось:
named_scope :needs_updates, lambda{
cols = Machine.column_names.collect{ |c| "\"machines\".\"#{c}\"" }.join(",")
{
:select => cols,
:group => cols,
:joins => 'LEFT JOIN "machine_updates" ON "machine_updates"."machine_id" = "machines"."id"',
:having => ['MAX("machine_updates"."date") IS NULL OR MAX("machine_updates"."date") < ?', UPDATE_THRESHOLD.days.ago]
}
}