MongoMapper 避免在 Ruby on Rails 中导致 N+1 查询

Posted

技术标签:

【中文标题】MongoMapper 避免在 Ruby on Rails 中导致 N+1 查询【英文标题】:MongoMapper Avoiding causing N+1 queries in Ruby on Rails 【发布时间】:2021-02-17 23:24:01 【问题描述】:

所以我有两个看起来像这样的类

class Branch
  include MongoMapper::Document

  many :builds
end

class Build
  include MongoMapper::Document

  belongs_to :branch
end

如果我们想从Build 类访问Branch 数据。我可以像下面那样做

builds = Build.where(___)

builds.each do |build|
  puts "#build.branch.name build number #build.number"
end

但这会触发警报,导致 N+1 查询,因为它进行了太多独立的数据库查询。那么解决方案是使用如下所示的 Eager Load

builds = Build.where(____).includes(:branches)

builds.each do |build|
  puts "#build.branch.name build number #build.number"
end

好吧,当我查看他们的文档时,MongoMapper 中不提供急切加载或.includes()(我希望我错了)。但它在 MongoId 中可用。但是,我暂时不打算从 MongoMapper 更改为 MongoId。你知道这件事的转机吗?这也许可以减少查询。

【问题讨论】:

【参考方案1】:

根据docs Mongoid 的#includes...将根据对ids的额外查询将所有文档加载到ids匹配的身份映射中。

所以根本没有魔法——听起来它只是执行一个额外的查询来获取关联的实体并将它们保存在某种数据结构中的内存中,读取时间为 O(1)(例如 Ruby 中的哈希) .你可以自己做(免责声明:有点伪代码即将到来,作为参考,不是现成的解决方案):

builds = #...
branches = Branch.where(id: builds.map(&:id)).to_h  |br| [br.id, br] 

builds.each do |build|
  puts "#branches[build.branch_id]&.name build number #build.number"
end

但请注意:如果您必须经常进行这种记忆,这可能表明数据模型对于基于文档的数据库不是最佳的 - 嵌入式文档可能是更有效的解决方案...

【讨论】:

以上是关于MongoMapper 避免在 Ruby on Rails 中导致 N+1 查询的主要内容,如果未能解决你的问题,请参考以下文章

在Michael Hartl的Ruby on Rails教程中获取错误消息:MicropostsController中的NoMethodError #create,如何避免收到此消息?

Mongoid 还是 MongoMapper? [关闭]

Rails 3 中带有 MongoMapper 的回形针

Ruby on Rails - 未捕获的ReferenceError:$未定义

Ruby on Rails 3 - 为每个请求重新加载 lib 目录

Ruby on Rails 取消首字母大写