2016-05-24 57 views
1

我有一個Rails應用程序,用戶可以選擇他們正在看的節目以及他們已經看過的節目。 ActiveRecord的關聯看起來有點像這樣:活動記錄:查詢屬於同一家長的不同關聯

class User 
    has_many :episodes_joins, :dependent => :delete_all, class_name: 'UsersEpisodesJoin' 
    has_many :episodes, through: :episodes_joins 
    has_many :shows_joins, :dependent => :delete_all, class_name: 'UsersShowsJoin' 
    has_many :shows, through: :shows_joins 
end 

class Show 
    has_many :episodes, dependent: :destroy 
    has_many :users_shows_joins, :dependent => :delete_all 
    has_many :users, through: :users_shows_joins 
end 

class Episode 
    belongs_to :show 
    has_many :users_episodes_joins, :dependent => :delete_all 
    has_many :users, through: :users_episodes_joins 
end 

class UsersShowsJoin 
    belongs_to :user 
    belongs_to :show 
end 

class UsersEpisodesJoin 
    belongs_to :user 
    belongs_to :episode 
end 

我希望允許用戶過濾每個「特殊事件」單獨顯示(「特殊事件」有episode.special設置爲true?)。爲此,我正考慮在名爲filter_specials的UsersShowsJoins表中添加一個布爾列。我現在希望做這樣的事情:

episodes = user.episodes.where(special: false).to_a 
user.episodes.where(special: true) do |epi| 
    show_join = UsersShowsJoins.where(user_id: user.id, show_id: epi.show.id).first 
    episodes << epi if show_join.nil? || !show_join.filter_specials? 
end 
# do something with 'episodes', 
# which contains all non-special episodes, 
# as well as special episodes of shows for which the user is not filtering these 

我知道這是一個非常hackish的和緩慢的實施,會做噸的數據庫查詢,我敢肯定有一個更好的辦法做到這一點,甚至可能與一個單一的查詢。

此外,我希望能夠查詢用戶已選擇的所有節目的數據庫,並預載相應用戶具有UsersEpisodesJoins行的相應劇集。喜歡的東西:

shows = user.shows.all 
h = {} 
shows.each do |show| 
    episodes = user.episodes.where(show_id: show.id).all 
    h[show] = episodes 
end 

# do something with h, 
# which contains a user's watched episodes for each selected show 

我怎樣纔能有效地編寫這些查詢,這樣我就不會招致N + 1問題,對於複雜的查詢?

回答

2

你應該能夠編寫特殊的事件過濾這樣的:

show_joins = UsersShowsJoins.joins(:shows, :users).where(episodes: { special: true}) 

這將由他們共同的關係都加入和showsusers,並篩選出具有special設置爲true情節。

有變化,你可以寫,根據你想與爲主要對象的工作是什麼類型的對象:

shows = Show.joins(:users, :episodes).where(episodes: {special: true}) 

或:

episodes = Episode.joins(shows: :users).where(special: true) 

對於第二個查詢,可以使用這個:

這應該預先加載用戶正在觀看的節目劇集。你可以添加where條件,group -ing,甚至order條件,如果你選擇,比如:

user.shows.includes(:episodes).order("shows.title") 

訂購的節目標題的結果(假設有一個節目標題字段)。

Active Record Query Interface指南在Joining Tables部分中有一些很好的示例。值得一讀的是如何有效地進行這種複雜的查詢。

+0

謝謝!正是我所需要的,我已經通讀AR指南,但沒有注意到他們有這樣一個詳細的加入表部分,所以謝謝指出。 – rpinheiro