[英]rails: Get all items tagged x AND y AND z
我有两个型号: Item
和Tag
。 两者都有name属性。 我想找到标有多个标签的商品。
class Item < ActiveRecord::Base
has_many :tags
validates_presence_of :name
end
class Tag < ActiveRecord::Base
belongs_to :item
validates_presence_of :name
end
给定一个标签ID列表,我可以很容易地获得用一个标签或另一个标签标记的项目列表:
# 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}
,那么我tag_ids
所有图片标记为1或4或两者。
我现在想知道如何获得用-1 和 4标记的图片。
我甚至无法想象这里需要的SQL。
您可以通过对结果进行分组并检查计数来解决此问题:
Item.all(
:conditions => ['tags.id IN (?)', tag_ids],
:joins => :tags,
:group => 'items.id',
:having => ['COUNT(*) >= ?', tag_ids.length]
)
小更新:今天,我们可以使用(灵感来自elektronaut):
Item.joins(:tags).where("tags.label in (?)", tags).group('items.id').having("COUNT(*) >= ?", tags.size)
它并没有太大的不同,它在这里运作良好。
我有一件事要添加到elektronaut的其他很好的答案:它不适用于PostgreSQL。
在我的实例中,Item.all调用包括其他表; 所以select看起来像这样:
SELECT items.id AS t0_f0, items.name as t0_f1 ..., table2.field1 as t1_f0 .. etc
PostgreSQL的GROUP BY要求将select上使用的所有字段都包含在那里。 所以我必须在GROUP BY子句中包含上一个select中使用的所有字段。
它仍然没有用; 我不知道为什么。
我最终做了一个更简单,更丑陋的事情。 它需要两个db请求。 其中一个用于返回id,它们用作条件。
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
)
Hacky,但它有效,而hackyness是本地化的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.