【问题标题】:How to efficiently update associated collection in rails (eager loading)如何有效地更新 Rails 中的关联集合(急切加载)
【发布时间】:2014-10-22 09:47:02
【问题描述】:

我有一个简单的关联,比如

class Slot < ActiveRecord::Base
  has_many :media_items, dependent: :destroy
end

class MediaItem < ActiveRecord::Base
  belongs_to :slot
end

MediaItemsSlot 排序,并有一个名为ordering 的字段。 并且想避免 n+1 查询,但我尝试过的没有任何效果。我已经阅读了几篇博文、railscasts 等,但是嗯..它们从不在单一模型上运行等等......

我做的是:

def update
  @slot = Slot.find(params.require(:id))

  media_items = @slot.media_items
  par = params[:ordering_media]
  # TODO: IMP remove n+1 query
  par.each do |item|
    item_id = item[:media_item_id]
    item_order = item[:ordering]
    media_items.find(item_id).update(ordering: item_order)
  end
  @slot.save
end

params[:ordering_media] 是一个 json 数组,带有 media_item_id 和一个整数 ordering 我试过像

@slot = Slot.includes(:media_items).find(params.require(:id)) # still n+1
@slot = Slot.find(params.require(:id)).includes(:media_items) # not working at all b/c is a Slot already
media_items = @slot.media_items.to_a # looks good but then in the array of MediaItems it is difficult to retrieve the right instance in my loop

这似乎是一件很常见的事情,所以我认为有一个简单的方法可以解决这个问题。很高兴了解它。

【问题讨论】:

  • 您更喜欢哪种方法:在插槽表单中为每个媒体项目填写订单字段,或在插槽显示中拖放媒体项目?

标签: ruby-on-rails postgresql activerecord ruby-on-rails-4 eager-loading


【解决方案1】:

首先,在media_items.find(item_id).update(ordering: item_order) 这一行,您没有n + 1 问题,而是2 * n 问题。因为对于每个 media_item,您进行 2 个查询:一个用于查找,一个用于更新。要解决此问题,您可以这样做:

params[:ordering_media].each do |item|
  MediaItem.update_all({ordering: item[:ordering]}, {id: item[:media_item_id]})
end

这里有n 查询。这是我们能做的最好的事情,没有办法用少于 n 个查询来更新具有 n 个不同值的 n 条记录上的列。

现在您可以删除@slot = Slot.find(params.require(:id))@slot.save 行,因为@slot 在更新操作中未被修改或使用。

通过这个重构,我们看到了一个问题:SlotsController#update 操作根本不更新槽。此代码的更好位置可能是 MediaItemsController#sortSortMediaItemsController#update(更 RESTful)。

在最后一个@slot = Slot.includes(:media_items).find(params.require(:id)) 这不是n + 1 查询,这是 2 个 SQL 语句查询,因为您检索 n 个 media_items 和 1 个插槽,只需要 2 个数据库调用。也是最好的选择。

希望对你有帮助。

【讨论】:

  • 非常有帮助和很好的解释。我必须对 update_all MediaItem.where(id: item[:media_item_id]).update_all(ordering: item[:ordering]) 使用以下语法,并且我可以在控制台中看到缺少 Load 语句。你所说的重构是有道理的,但现在与 mediaItems 交互的唯一方法是通过 slot。它只是一个 API,没有渲染页面/表单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
  • 1970-01-01
相关资源
最近更新 更多