假设我在文章和标签之间有很多关系
class ArticleTag < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :article_tags
has_many :articles, :through => :article_tags
end
class Article < ActiveRecord::Base
has_many :article_tags
has_many :tags, :through => :article_tags
named_scope :tagged, lambda { |id| joins(:tags).where("tags.id = ?", id) }
end
文章的范围已标记,正如名称所示,允许我检索标记有特定标记的文章
让我感到困扰的是:
$ a = Article.create
=> #<Article id: 3, created_at: "2011-05-22 13:54:02", updated_at: "2011-05-22 13:54:02">
$ t1 = Tag.create
=> #<Tag id: 4, created_at: "2011-05-22 13:54:07", updated_at: "2011-05-22 13:54:07">
$ t2 = Tag.create
=> #<Tag id: 5, created_at: "2011-05-22 13:54:11", updated_at: "2011-05-22 13:54:11">
$ a.tags << t1
=> [#<Tag id: 4, created_at: "2011-05-22 13:54:07", updated_at: "2011-05-22 13:54:07">]
$ a.tags << t2
=> [#<Tag id: 4, created_at: "2011-05-22 13:54:07", updated_at: "2011-05-22 13:54:07">, #<Tag id: 5, created_at: "2011-05-22 13:54:11", updated_at: "2011-05-22 13:54:11">]
$ Article.tagged(t1.id)
=> [#<Article id: 3, created_at: "2011-05-22 13:54:02", updated_at: "2011-05-22 13:54:02">]
$ Article.tagged(t2.id)
=> [#<Article id: 3, created_at: "2011-05-22 13:54:02", updated_at: "2011-05-22 13:54:02">]
$ Article.tagged(t1.id).tagged(t2.id)
=> []
如果文章标记有两个标记,则链接相应的范围不允许检索。这是假定的行为吗?如果是,我应该如何更改我的代码,以便最后一行不返回空数组?
PS:这是生成的SQL。
SELECT \"articles\".* FROM \"articles\" INNER JOIN \"article_tags\" ON \"articles\".\"id\" = \"article_tags\".\"article_id\" INNER JOIN \"tags\" ON \"tags\".\"id\" = \"article_tags\".\"tag_id\" WHERE (tags.id = 4) AND (tags.id = 5)
答案 0 :(得分:1)
虽然我不知道为什么内置链失败,但这是一个解决方案:
在Article
模型中
def self.tagged_by_one_in(*ids)
return [] if ids.nil?
self.joins(:tags).where("tags.id in (?)", ids).group(:article_id)
end
def self.tagged_by_all_in(*ids)
return [] if ids.nil?
#sorry raw sql there, found no Rails way.
self.find_by_sql("select * from articles where id in (select article_id from article_tags where tag_id in (" + ids * ", " + ") group by article_id having count(*) = " + ids.size.to_s + ")" )
end
在控制台中,您可以调用以下方法:
Article.tagged_by_one_in(1,2)
Article.tagged_by_all_in(1,2)
答案 1 :(得分:0)
试试这个
在Article.rb模型中
named_scope :tagged, lambda { |ids| joins(:tags).where("tags.id in (?)", ids) }
在控制台
中$ Article.tagged([t1.id,t2.id])
答案 2 :(得分:0)
好的,我找到了解决方案。诀窍是手动创建连接,并在某些位置添加唯一ID以避免名称冲突。
named_scope :tagged, lambda { |id| uid = rand(36**8).to_s(36); joins("INNER JOIN article_tags AS at_#{uid} ON (articles.id = at_#{uid}.article_id) INNER JOIN tags AS tags_#{uid} ON (tags_#{uid}.id = at_#{uid}.tag_id)").where("tags_#{uid}.id = ?",id) }