我有以下模型关系:
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"one_answers"B"的文章。
我对Rails很陌生,自从我做过任何严肃的web开发/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,但效率并不高。然而,我可以更容易地构建一个"find_by_sql"ActiveRecord查询使用这个SQL。
任何指导如何最好地做这种查询与ActiveRecord(最好不诉诸SQL)将非常感激。
我最终解决了我自己的问题。我构造了一个命名作用域,如下所示:
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'的文章。