我有以下模型关系:
class Article < ActiveRecord::Base
has_many :tags, :through => :article_tags
end
class ArticleTag < ActiveRecord::Base
belongs_to :tag
belongs_to :article
end
class Tag < ActiveRecord::Base
has_many :articles, :through => :article_tags
end
现在我要加载所有标记为“A”和标记“B”的文章。
我是Rails的新手,自从我做了任何严肃的web-dev / SQL工作已经有几年了但是对于我的生活,我无法弄清楚如何构建一个ActiveRecord查询来执行此操作
在本机SQL中执行此操作的一种方法如下:
SELECT a.* FROM articles a
INNER JOIN article_tags at ON at.article_id = a.id
INNER JOIN tags t ON at.tag_id = t.id
WHERE t.name = 'A'
intersect
SELECT a.* from articles a
INNER JOIN article_tags at ON at.article_id = a.id
INNER JOIN tags t ON at.tag_id = t.id
WHERE t.name = 'B'
现在,这可能不是最有效的SQL,但它有效。在这么晚的时候,我想不出更好的SQL解决方案。
更新:进一步的调查让我想到了这个SQL:
SELECT a.* FROM article_tags at, articles a, tags t
WHERE at.tag_id = t.id
AND (t.name = 'A' OR t.name = 'B')
AND a.id = at.vehicle_id
GROUP BY a.id HAVING COUNT(a.id) = 2
这似乎更简单(更简洁)SQL,但效率不高。但是,我可以使用此SQL更轻松地构造“find_by_sql”ActiveRecord查询。
如何最好地使用ActiveRecord进行此类查询(最好不要使用SQL)的任何指导都将非常感激。
答案 0 :(得分:0)
我最终解决了自己的问题。我构建了一个命名范围如下:
scope :tagged, lambda { |tag, *tags|
tags = tags.unshift(*tag)
joins(:tags).
where("lower(tags.name) = '" + tags.uniq.collect{ |t| t.to_s.downcase }.join("' OR lower(tags.name) = '") + "'").
group("articles.id").
having("count(articles.id) = #{tags.count}")
}
所以,现在我可以在我的控制器中执行此操作:
@tagged_articles = Article.tagged('A', 'B', 'C')
@tagged_articles
将包含所有标记为“A”,“B”和“C”的文章。