В Rails, как я могу вернуть набор записей на основе количества элементов в отношении ИЛИ критериев для отношения? - PullRequest
0 голосов
/ 23 декабря 2009

Я пишу приложение Rails, в котором у меня есть две модели: модель Machine и модель MachineUpdate. Модель Machine имеет много обновлений Machine. MachineUpdate имеет поле даты / времени. Я пытаюсь получить все записи компьютеров, которые соответствуют следующим критериям:

  1. Модель машины не обновлялась за последние 2 недели, ИЛИ
  2. Модель Машины никогда не имела обновлений .

В настоящее время я выполняю # 1 с именованной областью действия:

named_scope :needs_updates,
            :include => :machine_updates,
            :conditions => ['machine_updates.date < ?', UPDATE_THRESHOLD.days.ago]

Однако при этом получаются только модели Машин, у которых было хотя бы одно обновление. Я также хотел получить модели машин, которые имели нет обновлений. Как я могу изменить needs_updates, чтобы элементы, которые он возвращает, также соответствовали этим критериям?

Ответы [ 3 ]

3 голосов
/ 23 декабря 2009

Одним из решений является введение 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]
  }
}
0 голосов
/ 15 июля 2010

Я только что столкнулся с подобной проблемой. На самом деле все довольно просто:

Machine.all(
  :include => :machine_updates,
  :conditions => "machine_updates.machine_id IS NULL OR machine_update.date < ?", UPDATE_THRESHOLD.days.ago])

Если вы делали именованную область, просто используйте лямбда-выражения, чтобы дата пересчитывалась при каждом вызове именованной области

named_scope :needs_updates, lambda { {
  :include => :machine_updates,
  :conditions => "machine_updates.machine_id IS NULL OR machine_update.date < ?", UPDATE_THRESHOLD.days.ago]
} }

Если вы не хотите возвращать все записи MachineUpdate в своем запросе, вам нужно использовать опцию :select, чтобы возвращать только те столбцы, которые вам нужны

named_scope :needs_updates, lambda { {
  :select => "machines.*",
  :conditions => "machine_updates.machine_id IS NULL OR machine_update.date < ?", UPDATE_THRESHOLD.days.ago]
} }
0 голосов
/ 23 декабря 2009

Если вы можете вносить изменения в таблицу, то вы можете использовать метод: touch для ассоциации own_to.

Например, добавьте столбец datetime к компьютеру с именем last_machine_update. После этого в принадлежащем MachineUpdate принадлежащем добавьте :touch => :last_machine_update. Это приведет к тому, что это поле будет обновляться с момента последнего добавления или изменения MachineUpdate, подключенного к этому компьютеру, что устраняет необходимость в именованной области действия.

В противном случае я бы сделал это так, как предлагает Алекс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...