[英]Active Record: Query different associations that belong to the same parent
我有一个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
我希望允许用户为每个节目单独过滤“特殊情节”(“特殊情节”的epuse.special?设置为true)。 对于这一点,我想加入的UsersShowsJoins表命名为布尔列的filter_specials
。 我现在想做这样的事情:
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
我知道这是一个非常棘手且缓慢的实现,可以执行大量的数据库查询,而且我敢肯定,有更好的方法可以做到这一点,甚至可能只有一个查询。
另外,我希望能够查询数据库以查看用户已选择的所有节目,并预加载具有针对同一用户的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问题?
您应该能够像这样编写特殊情节过滤器:
show_joins = UsersShowsJoins.joins(:shows, :users).where(episodes: { special: true})
这将通过shows
和users
的共同关系加入shows
,并过滤具有special
设置为true
的剧集。
根据您想使用哪种类型的对象作为主要对象,您可以编写一些变体:
shows = Show.joins(:users, :episodes).where(episodes: {special: true})
要么:
episodes = Episode.joins(shows: :users).where(special: true)
对于第二个查询,可以使用以下命令:
user.shows.includes(:episodes).all
这应该为用户正在观看的节目预加载剧集。 您可以添加where
的条件, group
-ing,甚至order
条件,如果你选择,就像这样:
user.shows.includes(:episodes).order("shows.title")
按显示标题对结果进行排序(假设有一个显示标题字段)。
Active Record Query Interface指南在Joining Tables部分中有一些很棒的示例。 值得通读以获取有关如何有效地执行复杂查询的想法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.