2015-04-06 80 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活動記錄)