направляющие: Доберитесь все объекты отметили X и Y И z

У меня есть две модели: Item и Tag. У обоих есть атрибут имени. Я хочу найти объекты отмеченными с несколькими тегами.

class Item < ActiveRecord::Base
  has_many :tags
  validates_presence_of :name
end

class Tag < ActiveRecord::Base
  belongs_to :item
  validates_presence_of :name
end

Учитывая список идентификаторов тега, я могу легко достаточно получить список объектов, отмеченных с одним тегом или другим:

# Find the items tagged with one or more of the tags on tag_ids
Item.all(:conditions => ['tags.id in (?)', tag_ids], :joins => :tags)

Если tag_ids {1,4}, затем я получаю все изображения, отмеченные с 1, или 4, или оба.

Я хочу знать теперь, как получить изображения, которые отмечены и с - 1 И С 4.

Я не могу даже вообразить SQL, который необходим здесь.

8
задан Todd A. Jacobs 4 June 2012 в 21:09
поделиться

2 ответа

Вы можете решить эту проблему, сгруппировав результаты и проверив подсчет:

Item.all(
  :conditions => ['tags.id IN (?)', tag_ids], 
  :joins      => :tags, 
  :group      => 'items.id', 
  :having     => ['COUNT(*) >= ?', tag_ids.length]
)
13
ответ дан 5 December 2019 в 09:24
поделиться

Я хочу добавить кое-что к замечательному ответу elektronaut: это не будет работать на PostgreSQL.

В моем реальном примере вызов Item.all включает другие таблицы; поэтому селект выглядит так:

SELECT items.id AS t0_f0, items.name as t0_f1 ..., table2.field1 as t1_f0 .. etc

В PostgreSQL GROUP BY требует, чтобы все поля, используемые в селекте, были включены в него. Поэтому мне пришлось включить все поля, использованные в предыдущем селекте, в предложение GROUP BY.

И все равно это не сработало; я не уверен, почему.

В итоге я решил сделать более простую и уродливую вещь. Для этого требуется два запроса к db. Один из них используется для возврата идентификаторов, которые используются в качестве условия.

class Item < ActiveRecord::Base

  # returns the ids of the items tagged with all tags
  # usage: Item.tagged_all(1,2,3)
  named_scope :tagged_all, lambda { |*args|
    { :select => "items.id",
      :joins => :tags,
      :group => "items.id",
      :having => ['COUNT(items.id) >= ?', args.length],
      :conditions => ["tags.id IN (?)", args]
    }
  }

Тогда я могу сделать следующее:

  Item.all(
    :conditions => [
      'items.id IN (?) AND ... (other conditions) ...',
      Items.tagged_all(*tag_ids).collect(&:id),
      ... (other values for conditions) ...
    ],
    :includes => [:model2, :model3] #tags isn't needed here any more
  )

Халтурно, но работает, и хакерство локализовано.

2
ответ дан 5 December 2019 в 09:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: