MongoMapper避免在Ruby on Rails中引起N + 1查询

问题描述

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

class Branch
  include MongoMapper::Document

  many :builds
end

class Build
  include MongoMapper::Document

  belongs_to :branch
end

并且如果我们要访问Branch类中的Build数据。我可以像下面这样

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。你知道这个转身吗?也许可以减少查询

解决方法

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

所以根本没有魔术-听起来它只是执行一个附加查询以获取关联的实体,并通过O(1)读取以某种数据结构将它们保存在内存中(例如,Ruby中的Hash) 。您可以自己进行操作(免责声明: kinda伪代码即将发布,作为参考,但尚未提供解决方案)

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

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