2011-05-22 81 views
2

比方說,我有文章和標籤Rails3中 - MANY_TO_MANY關係和範圍鏈

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 

條已範圍標記之間的MANY_TO_MANY關係的船,其中 - 作爲它的名字說 - 讓我找回標註了文章一個特定的標籤

什麼煩惱我是這樣的:

$ 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

有趣的聯繫:http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/ – apneadiving 2011-05-22 14:53:09

回答

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) } 
0

試試這個

在Article.rb模型

named_scope :tagged, lambda { |ids| joins(:tags).where("tags.id in (?)", ids) } 

在控制檯

$ Article.tagged([t1.id,t2.id]) 
+0

這並不回答問題是否他想要檢索通過所有標籤的文章 – apneadiving 2011-05-22 16:12:06

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)