原来在 mongoid3 上做的 belongs_to eager_load 不能再用了 那我们看看 mongoid4.0.0 改如何修改
class Face
include Mongoid::Document
field :name, type: String
has_many :left_eyes, class_name: "Eye", as: :eyeable
has_many :right_eyes, class_name: "Eye", as: :eyeable
end
class Eye
include Mongoid::Document
field :name, type: String
belongs_to :eyeable, polymorphic: true
end
faces = Face.all.to_a
faces.first.left_eyes
MOPED: 127.0.0.1:27017 QUERY runtime: 0.6560ms
[ #, # ]
faces = Face.includes(:left_eyes).to_a
faces.first.left_eyes
[ #, # ]
eg:
eyes = Eye.includes(:eyeable).to_a
抛出异常 Mongoid::Errors::EagerLoad: Problem: Eager loading :eyeable is not supported since it is a polymorphic belongs_to relation.
查看原代码,发现当 belongs_to 后面接 polymorphic 参数的时候,会抛出异常
module Mongoid
module Relations
module Eager
class BelongsTo < Base
def preload
raise Errors::EagerLoad.new(@metadata.name) if @metadata.polymorphic?
@docs.each do |d|
set_relation(d, nil)
end
each_loaded_document do |doc|
id = doc.send(key)
set_on_parent(id, doc)
end
...
end
end
end
end
end
那我们注意掉这句,看一下会发生什么? NameError: uninitialized constant Eyeable 问题出在这里 each_loaded_document, @metadata.klass => Eyeable 这个 Eyeable 是从哪里来的呢?就是从我们的模型 Eye 中:belongs_to :eyeable each_loaded_document 这个方法就是 load 依赖的模型数据了
eyes = Eye.includes(:eyeable).to_a
module Mongoid
module Relations
module Eager
class Base
def each_loaded_document
@metadata.klass.any_in(key => keys_from_docs).each do |doc|
yield doc
end
end
end
end
end
end
找到需要预加载的 Face,并通过 set_on_parent 与 eye 建立对应关系 @metadata.klass.any_in(key => keys_from_docs) => Face.any_in(_id => ['id1','id2'])
each_loaded_document do |doc|
id = doc.send(key)
set_on_parent(id, doc)
end
建立 eye 对象和 face 对象的对应关系 #Eye:0x007f837b55b670, #Face:0x007f837b4f2800 #Eye:0x007f837b55b5f8, #Face:0x007f837b4f2800
def set_on_parent(id, element)
grouped_docs[id].each do |d|
set_relation(d, element)
end
end
生成实例变量 @_eyealbe,当调用 eyes.first.eyealbe 的时候,则直接返回 face 对象 而不需要再查询数据库 @_eyeable, #Face:0x007f837b4f2800
def set_relation(name, relation)
instance_variable_set("@_#{name}", relation)
end
重写 preload 方法,去掉 raise lib/mongoid/eager_load.rb
module Mongoid
module Relations
module Eager
class BelongsTo < Base
def preload
@docs.each do |d|
set_relation(d, nil)
end
each_loaded_document do |doc|
id = doc.send(key)
set_on_parent(id, doc)
end
...
end
end
end
end
end
加载 initializers/mongoid.rb
require "mongoid/eager_load"
在 belongs_to 一端,声明 class_name
class Eye
include Mongoid::Document
field :name, type: String
belongs_to :eyeable, class_name: "Face", polymorphic: true
end
2.1.1 :004 > eyes = Eye.all.to_a 2.1.1 :005 > eyes.first.eyeable MOPED: 127.0.0.1:27017 QUERY runtime: 0.6910ms => #
2.1.1 :002 > eyes = Eye.includes(:eyeable).to_a 2.1.1 :003 > eyes.first.eyeable => #
2.1.1 :006 > faces = Face.includes(:left_eyes).to_a 2.1.1 :009 > faces.first.left_eyes MOPED: 127.0.0.1:27017 QUERY runtime: 0.7480ms => [ #, # ]
2.1.1 :006 > faces = Face.includes(:left_eyes).to_a 2.1.1 :007 > faces.first.left_eyes => [ #, # ]
博客:http://michael-roshen.iteye.com/blog/2152764 微信:ruby 程序员