有条件地渴望加载多态关联

时间:2013-10-21 00:58:51

标签: ruby-on-rails activerecord ruby-on-rails-4

我正在尝试为我的网站上的用户Feed删除一些N + 1查询。有各种模型与FeedItem模型具有多态关联

class Event < ActiveRecord::Base
  has_many :feed_items, as: :feedable
end

class Tournament < ActiveRecord::Base
  has_many :feed_items, as: :feedable
end

class FeedItem < ActiveRecord::Base
  belongs_to :feedable, :polymorphic => true
end

用户模型也涉及用户拥有多个具有不同属性的锦标赛和事件。为用户加载FeedItems时,如何根据FeedItem类型有条件地急切加载嵌套属性?

我的意思(使用squeel DSL)的一个例子:

FeedItem.where{ (feedable_id.in(@user.tournaments.ids}) & feedable_type.eq('Tournament')) |    
                (feedable_id.in(@user.events.ids) & feedable_type.eq('Event')) }.includes(:feedable => :game)

此查询尝试在急切加载锦标赛模型的“游戏”属性时检索锦标赛和赛事类型的所有FeedItem。适用于锦标赛,但在迭代FeedItems时,事件将抛出此属性不存在的错误。

我当前的hack是对每个FeedItem类型执行单独查询并将记录添加到一起。但是,这会将关联转换为我不想做的数组。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

可以为多态关联预加载嵌套关联。您需要向FeedItem添加两个关联:

class FeedItem < ActiveRecord::Base
  belongs_to :feedable, :polymorphic => true

  belongs_to :event, -> { where(feedable_type: 'Event' ) }, foreign_key: :feedable_id
  belongs_to :tournament, -> { where(feedable_type: 'Tournament' ) }, foreign_key: :feedable_id
end

之后,您只能为tournament预加载嵌套关联:

feeds =
  FeedItem.where("
    (feedable_id IN (?) AND feedable_type = 'Tournament') OR 
    (feedable_id IN (?) AND feedable_type = 'Event')", @user.tournament_ids, @user.event_ids).
    includes(:feedable, tournament: :game)

现在您可以在没有其他查询的情况下获得相关的锦标赛游戏:

feeds.each do |feed|
  puts feed.feedable.inspect
  puts feed.tournament.game.inspect if feed.feedable_type == "Tournament"
end