我有一个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问题?

===============>>#1 票数:2 已采纳

您应该能够像这样编写特殊情节过滤器:

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

这将通过showsusers的共同关系加入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部分中有一些很棒的示例。 值得通读以获取有关如何有效地执行复杂查询的想法。

  ask by rpinheiro translate from so

未解决问题?本站智能推荐: