【问题标题】:Conditionally eager loading polymorphic associations有条件地预先加载多态关联
【发布时间】:2013-10-29 08:51:54
【问题描述】:

我正在尝试在我的网站上消除一些针对用户供稿的 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

还涉及用户模型,其中用户拥有_many Tournaments 和Events,它们都具有不同的属性。为用户加载 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)

此查询尝试检索所有类型为 Tournament 和 Event 的 FeedItem,同时预先加载 Tournament 模型的“游戏”属性。适用于锦标赛,但在迭代 FeedItems 时,事件将引发此属性不存在的错误。

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

有什么想法吗?

【问题讨论】:

  • 您仍然可以进行单独的查询,但从每个查询中提取 FeedItem id,而不是加载任何内容。创建一个包含您收集的所有 id 的数组,并将其传递到您渴望加载的单个查询中。

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


【解决方案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

【讨论】:

    猜你喜欢
    • 2011-03-26
    • 2014-07-08
    • 1970-01-01
    • 1970-01-01
    • 2012-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多