3

在這樣一個領域:如何篩選用於association_ids ActiveRecord模型?

class User 
    has_many :posts 
    has_many :topics, :through => :posts 
end 
class Post 
    belongs_to :user 
    belongs_to :topic 
end 
class Topic 
    has_many :posts 
end 

我可以通讀user.topic_ids所有的主題ID,但我看不到的方式來過濾條件適用於這種方法,因爲它返回一個Array而不是ActiveRecord::Relation的。

問題是,給定一個用戶和一個現有的主題,標記用戶有一個帖子。我目前正在做這樣的事情:

def mark_topics_with_post(user, topics) 
    # only returns the ids of the topics for which this user has a post 
    topic_ids = user.topic_ids 
    topics.each {|t| t[:has_post]=topic_ids.include(t.id)} 
end 

但是,這加載所有主題id,無論輸入集。理想情況下,我想這樣做

def mark_topics_with_post(user, topics) 
    # only returns the topics where user has a post within the subset of interest 
    topic_ids = user.topic_ids.where(:id=>topics.map(&:id)) 
    topics.each {|t| t[:has_post]=topic_ids.include(t.id)} 
end 

但我可以具體做的唯一事情就是

def mark_topics_with_post(user, topics) 
    # needlessly create Post objects only to unwrap them later 
    topic_ids = user.posts.where(:topic_id=>topics.map(&:id)).select(:topic_id).map(&:topic_id) 
    topics.each {|t| t[:has_post]=topic_ids.include(t.id)} 
end 

有沒有更好的辦法? 是否有可能有一些像select_values在協會或範圍是什麼? FWIW,我在軌道上3.0.x的,但我很好奇3.1了。

我爲什麼這樣做呢?

基本上,我有一個半複雜搜索的結果頁面(它僅基於主題數據發生),並且我想將結果(主題)標記爲用戶已經交互的東西(寫入了帖子)。

所以是的,還有另一個選項可以做一個連接[主題,發佈],以便結果從搜索中標記出來,但是這會破壞我緩存Topic查詢的能力(查詢,即使沒有加入,也比僅爲用戶獲取ID更昂貴)

注意上面概述的方法做的工作,他們只是覺得不理想。

+2

也許你可以描述爲什麼你在做什麼你在做。有時候抽象化會揭示需求的簡化(士力架vs比利時巧克力)。 – clyfe 2011-12-22 09:36:32

+0

true,添加了一個關於它的塊 – riffraff 2011-12-22 09:44:25

回答

2

假設你的主題模式有一個名爲id,你可以做這樣的事情

Topic.select(:id).join(:posts).where("posts.user_id = ?", user_id) 

本專欄將針對您的數據庫只運行一個查詢,並給你所有具有一個給定的user_id帖子的主題IDS

+0

但這不是我所需要的:我已經有了一個Topic對象列表,我只想標記那些與用戶有共同關係的帖子。 – riffraff 2011-12-22 10:56:29

3

我認爲你的第二個解決方案几乎是最優的(從視圖涉及到的查詢點),至少相對於你想使用的一個。

user.topic_ids生成查詢:

SELECT `topics`.id FROM `topics` 
INNER JOIN `posts` ON `topics`.`id` = `posts`.`topic_id` 
WHERE `posts`.`user_id` = 1 

如果user.topic_ids.where(:id=>topics.map(&:id))有可能它會產生這樣的:

SELECT topics.id FROM `topics` 
INNER JOIN `posts` ON `topics`.`id` = `posts`.`topic_id` 
WHERE `posts`.`user_id` = 1 AND `topics`.`id` IN (...) 

這正是產生在做同樣的查詢:user.topics.select("topics.id").where(:id=>topics.map(&:id))

user.posts.select(:topic_id).where(:topic_id=>topics.map(&:id))生成以下查詢:

SELECT topic_id FROM `posts` 
WHERE `posts`.`user_id` = 1 AND `posts`.`topic_id` IN (...) 

哪一個更高效取決於定義的實際表和索引(以及使用哪個數據庫)中的數據。

如果主題ID列表中的用戶是漫長的,有主題反覆多次,它可以在查詢級別意義的按主題ID:

user.posts.select(:topic_id).group(:topic_id).where(:topic_id=>topics.map(&:id)) 
+0

有趣的是,我沒有考慮過連接是必要的,如果沒有參照完整性和重複值,這是有意義的。 posts/select/map解決方案的問題在於,它會在Ruby域(至少有8個字段,4個哈希,兩個字符串的對象)中創建更多對象,只是爲了給我一個Fixnum,加上一個無用的循環,是不必要的。這顯然不是一個大問題,但它只是感覺不對。 – riffraff 2011-12-22 15:50:57

+0

是的,你是正確的,它確實感覺不對,我的觀點是,topic_ids方法本身完全遵循主題/選擇/地圖路線。避免實例化活動記錄的唯一(醜陋的)方法我能想到涉及直接執行sql查詢... – LucaM 2011-12-22 17:33:46