我正在使用Mongoid在Rails中使用MongoDB.
我正在寻找的是积极的记录include
.目前我没有在mongoid orm中找到这样的方法.
任何人都知道如何在mongoid或mongomapper中解决这个问题,这是另一个很好的选择.
现在已经过了一段时间,Mongoid确实增加了对此的支持.请参阅此处的"预先加载"部分:http:
//docs.mongodb.org/ecosystem/tutorial/ruby-mongoid-tutorial/#eager-loading
Band.includes(:albums).each do |band| p band.albums.first.name # Does not hit the database again. end
我想指出:
Rails' :include
没有加入
SQL和Mongo都需要急切加载.
N + 1问题发生在这种类型的场景中(在循环内部生成的查询):
.
<% @posts.each do |post| %> <% post.comments.each do |comment| %> <%= comment.title %> <% end %> <% end %>
看起来@amrnt发布的链接已合并到Mongoid中.
更新:我发布这个答案已经两年了,情况发生了变化.有关详细信息,请参阅tybro0103的答案.
基于两个驱动程序的文档,它们都不支持您正在寻找的内容.可能是因为它无法解决任何问题.
:include
ActiveRecord 的功能解决了SQL数据库的N + 1问题.通过告诉ActiveRecord要包含哪些相关表,它可以通过使用JOIN
语句构建单个SQL查询.无论您要查询的表数量多少,这都将导致单个数据库调用.
MongoDB只允许您一次查询单个集合.它不支持任何类似的东西JOIN
.因此,即使您可以告诉Mongoid它必须包含哪些其他集合,它仍然必须为每个其他集合执行单独的查询.
虽然其他答案是正确的,但在当前版本的Mongoid中,包含方法是获得所需结果的最佳方法.在之前的版本中,包含不可用,我找到了摆脱n + 1问题的方法,并认为值得一提.
就我而言,这是一个n + 2问题.
class Judge include Mongoid::Document belongs_to :user belongs_to :photo def as_json(options={}) { id: _id, photo: photo, user: user } end end class User include Mongoid::Document has_one :judge end class Photo include Mongoid::Document has_one :judge end
控制器动作:
def index @judges = Judge.where(:user_id.exists => true) respond_with @judges end
此as_json响应导致来自Judge记录的n + 2查询问题.在我的情况下,给开发服务器一个响应时间:
816毫秒完成200 OK(浏览次数:785.2ms)
解决此问题的关键是在单个查询中加载用户和照片,而不是每个Judge 1个1.
您可以使用Mongoids IdentityMap Mongoid 2和Mongoid 3支持此功能.
首先打开mongoid.yml配置文件中的身份映射:
development: host: localhost database: awesome_app identity_map_enabled: true
现在更改控制器操作以手动加载用户和照片.注意:Mongoid :: Relation记录将懒惰地评估查询,因此您必须调用to_a来实际查询记录并将它们存储在IdentityMap中.
def index @judges ||= Awards::Api::Judge.where(:user_id.exists => true) @users = User.where(:_id.in => @judges.map(&:user_id)).to_a @photos = Awards::Api::Judges::Photo.where(:_id.in => @judges.map(&:photo_id)).to_a respond_with @judges end
这导致总共只有3个查询.1为法官,1为用户,1为照片.
在559ms完成200 OK(浏览次数:87.7ms)
这是如何运作的?什么是IdentityMap?
IdentityMap有助于跟踪已加载的对象或记录.因此,如果您获取第一个用户记录,IdentityMap将存储它.然后,如果您再次尝试获取相同的用户,Mongoid会在再次查询数据库之前查询用户的IdentityMap.这将在数据库上保存1个查询.
因此,通过加载我们知道在手动查询中我们想要Judges json的所有用户和照片,我们一次性将数据预加载到IdentityMap中.然后,当Judge要求它的用户和照片时,它会检查IdentityMap,而不需要查询数据库.
ActiveRecord :include
通常不会执行完全连接来填充Ruby对象.它做了两个电话.首先获取父对象(比如一个帖子),然后第二次调用拉相关对象(属于Post的注释).
Mongoid对引用的关联的工作方式基本相同.
def Post references_many :comments end def Comment referenced_in :post end
在控制器中你得到帖子:
@post = Post.find(params[:id])
在您的视图中,您将迭代注释:
<%- @post.comments.each do |comment| -%> VIEW CODE <%- end -%>
Mongoid将在该集合中找到该帖子.当您点击注释迭代器时,它会执行单个查询来获取注释.Mongoid将查询包装在游标中,因此它是一个真正的迭代器,并不会使内存过载.
Mongoid延迟加载所有查询以默认允许此行为.该:include
标签是不必要的.