Rails3 ActiveRecord—获取所有具有A和B的记录(通过has_many关系)



我有以下模型关系:

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'的文章。

相关内容

  • 没有找到相关文章

最新更新