2015-04-06 84 views
1

我的一些类:Mongoid预先加载嵌入文档

class User 
    embeds_many :notifications 
    field :first_name 
    field :last_name 

    def name{ "#{first_name} #{last_name}" } 

class Notification 
    embedded_in :user 
    belongs_to :sender, class_name: "User", inverse_of: nil 

现在,在我的意见,我实现了一个较小的邮箱系统通知。但是,它目前打N +数据库1次:

<% current_user.notifications.sort{...}.each do |notif|%> 
    ... 
    <%= notif.sender.name if notif.sender %> 

这里的问题是,这将导致数据库N命中notif.sender.name。我能以某种方式预加载/急切加载吗?类似于current_user.notifications.includes(:sender)(但可以工作:D)

我目前只需要发件人姓名。

回答

3

我觉得你在这里运气一半。 Mongoid有这样的错误信息:

在Mongoid中的预加载只支持提供参数给M.includes,它是M模型中关系的名称,并且只支持一级加载。 (即,不要在M上加载关联,但不能通过另一个关系离开一步)。

注意最后括号中的一句特别是:不

预先加载关联的M,但通过其他关系一步之遥不允许

嵌入功能的关系,但你要将includes应用于嵌入关系,这对Mongoid来说太过分了。

fine manual并说:

这对于通过belongs_to引用另一个集合以及嵌入关系的工作。 。

但是这意味着你会叫上嵌入的关系includes而不是什么模型嵌入在你的情况,这意味着你可能急于负载发件人每一套嵌入式的通知:

current_user.notifications.includes(:sender).sort { ... } 

这仍然留给您N+1问题,急切的加载应该得到解决,但您的N将更小。

如果这仍然太重,那么您可以将名称非规范化到每个嵌入文档中(即复制它,而不是通过sender引用它)。当然,如果允许人们更改姓名,你需要保留副本。

+0

哈哈,其实我的问题是风马牛不相及。我有一个奇怪的错误“错误的参数数量......”:工作查询是'current_user.notifications.includes(:sender).to_a.sort'。 'to_a'在这里很重要! – 2015-04-07 23:34:23

+1

@CyrilDD它可能执行得更好,如果你想在你的查询中包含排序,而不是在本机数组上进行ruby排序 – ericraio 2015-04-30 02:54:58

1

这并不完美,但this article提出了一种可能的解决方案。

您可以加载所有发件人并使用set_relation来避免每次加载它们。

def notifications_with_senders 
    sender_ids = notifications.map(:sender_id) 
    senders = User.in(id: sender_ids).index_by(&:id) 
    notifications.each do |notification| 
    notification.set_relation(:sender, senders[notification.sender_id]) 
    end 
end 

将是巨大的有,作为一个Relation方法(如钢轨includes活动记录)