Выберите дополнение набора - PullRequest
3 голосов
/ 20 августа 2011

Я использую Rails 3.0.У меня есть две таблицы: списки и предложения.Листинг has-many Предложения.Предложение может иметь accepted быть true или false.

. Я хочу выбрать каждый листинг, который не имеет предложения, с accepted, являющимся истинным.Я пытался

Listing.joins(:offers).where('offers.accepted' => false)

Однако, поскольку в листинге может быть много предложений, при этом выбираются все листинги, в которых есть непринятые предложения, даже если для этого листинга есть принятое предложение.В случае, если неясно, что я хочу, это дополнение набора:

Listing.joins(:offers).where('offers.accepted' => true)

Мое текущее временное решение состоит в том, чтобы захватить их все, а затем выполнить фильтрацию массива, например:*

class Listing < ActiveRecord::Base
...
    def self.open
        Listing.all.find_all {|l| l.open? }
    end

    def open?
        !offers.exists?(:accepted => true)
    end
end

Я бы предпочел, чтобы решение запускало фильтрацию на стороне базы данных.

1 Ответ

1 голос
/ 20 августа 2011

Первое, что приходит на ум, - это делать то же самое, что вы делаете сейчас, но в базе данных.

scope :accepted, lambda {
  joins(:offers).where('offers.accepted' => true)
} 

scope :open, lambda {
  # take your accepted scope, but just use it to get at the "accepted" ids
  relation = accepted.select("listings.id")

  # then use select values to get at those initial ids
  ids = connection.select_values(relation.to_sql)

  # exclude the "accepted" records, or return an unchanged scope if there are none
  ids.empty? ? scoped : where(arel_table[:id].not_in(ids))
}

Я уверен, что это можно сделать более чисто, используя внешнийприсоединяйся и группируйся, но это не сразу для меня: -)

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