2013-08-16 40 views
2

我試圖跟蹤用戶的當前,先前和選擇(期末更改)訂閱狀態。Mongoid嵌入一個類作爲多個字段

在月結束時,如果chosen_subscription != current_subscriptioncurrent_subscription被更改。

class User 
    include Mongoid::Document 

    embeds_one :previous_subscription, class_name: "Subscription" 
    embeds_one :current_subscription, class_name: "Subscription" 
    embeds_one :chosen_subscription, class_name: "Subscription" 
end 

class Subscription 
    include Mongoid::Document 

    embedded_in :user 

    field :plan, type: String 
    field :credits, type: Integer 
    field :price_per_credit, type: BigDecimal 

    field :start, type: Date 
    field :end, type: Date 
end 

Mongoid要我來指定這個更多的方式,沒有任何意義對我說:

Mongoid::Errors::AmbiguousRelationship: 

Problem: 
    Ambiguous relations :previous_subscription, :current_subscription, :chosen_subscription defined on User. 
Summary: 
    When Mongoid attempts to set an inverse document of a relation in memory, it needs to know which relation it belongs to. When setting :user, Mongoid looked on the class Subscription for a matching relation, but multiples were found that could potentially match: :previous_subscription, :current_subscription, :chosen_subscription. 
Resolution: 
    On the :user relation on Subscription you must add an :inverse_of option to specify the exact relationship on User that is the opposite of :user. 

這發生在我覆蓋現有current_subscription。我想,在這個時候,Mongoid想要取消註冊舊訂閱用戶的訂閱。

當然,每個認購對象只屬於一個用戶和user == user.previous_subscription.user == user.current_subscription.user == user.chosen_subscription.user

然而,它並不意義,我要告訴Subscriptionuser是三個中的任何一個的倒數。

我應該如何構建它?

+0

我不太清楚你將如何構建它沒錯,但你可以試試看單身谷歌組。 mongoid告訴你的問題是如果你執行'subscription.user = some_user',它不知道你是從用戶的角度來談論你正在談論的。如果你不打算在你的代碼中使用'subscription.user =',你也可以嘗試設置'inverse_of:nil'。 – rubish

+0

我從來沒有直接設置subscription.user。我認爲當我設置用戶。* _訂閱時,它會隱式設置。 inverse_of:nil對我來說工作得很好。如果你想爲它創建一個答案,我會標記它是正確的。 – Jan

+0

我已將評論擴展到答案中,並提供了一些更多信息和一些建議。我不相信這是理想的解決方案,但在大多數情況下適用於我。 – rubish

回答

5

正如它所說:您必須添加:inverse_of選項。試試這個:

class User 
    include Mongoid::Document 

    embeds_one :previous_subscription, class_name: "Subscription", inverse_of: :previous_subscription 
    embeds_one :current_subscription, class_name: "Subscription", inverse_of: :current_subscription 
    embeds_one :chosen_subscription, class_name: "Subscription", inverse_of: :chosen_subscription 
end 

class Subscription 
    include Mongoid::Document 

    embedded_in :user, inverse_of: :previous_subscription 
    embedded_in :user, inverse_of: :current_subscription 
    embedded_in :user, inverse_of: :chosen_subscription 

    field :plan, type: String 
    field :credits, type: Integer 
    field :price_per_credit, type: BigDecimal 

    field :start, type: Date 
    field :end, type: Date 
end 
+1

我原以爲embedding_in不能多次使用,特別是對於同一個父級。但是,這工作。對於embeds_one,我使用inverse_of :: user。 – Jan

0

每個mongoid關係都有一個反比關係,用來正確設置兩邊。大多數情況下,它可以根據命名約定,使用的類等正確識別,但有時您需要明確定義它們。

問題mongoid抱怨這裏的是,在User模型中定義的關係是模糊的,因爲mongoid不能確定它們中的哪映射到Subscriptionsubscription.user關係。此外,當使用subscription.user = some_user時,mongoid無法識別您是否需要在訂閱對象的用戶中設置previous_subscriptioncurrent_subscriptionchosen_subscription。即使你沒有明確地調用這個方法,當你設置一個關係時,你可以面對這些問題,因爲mongoid試圖設置反向。

我不確定這是否是構建系統的正確方法,但您可以在關係中定義inverse_of: nil以告訴mongoid在設置關係時不設置反向關係。只有在您的代碼中不需要使用subscription.user=時才應該使用此方法。 subscription.user也可能有問題,但如果您確實需要它們,則可以使用未公開的方法doc._parentdoc._root。這種方法雖然解決了問題,但有其自身的缺點。我試圖將它們與下面的代碼顯示在這裏:

class Tester 
    include Mongoid::Document 

    field :name, type: String 

    embeds_one :first_module, class_name: 'TestModule', inverse_of: nil 
    embeds_one :last_module, class_name: 'TestModule', inverse_of: nil 

end 

class TestModule 
    include Mongoid::Document 

    field :name, type: String 

    # not needed as such, but required to tell mongoid that this is a embedded document 
    embedded_in :tester, inverse_of: nil 
end 

t1 = Tester.new(name: 't1') 
m1 = t1.build_first_module(name: 't1m1') 
m2 = t1.build_last_module(name: 't1m2') 

t1.first_module == m1 #=> true 
t1.last_module == m2 #=> true 

m1.tester    #=> nil 
m2.tester    #=> nil 

t1.save 
t1 = Tester.last 
m1 = t1.first_module 
m2 = t1.last_module 

t1.first_module == m1 #=> true 
t1.last_module == m2 #=> true 

m1.tester    #=> nil 
m2.tester    #=> nil 

已經有幾對這樣的事情上mongoid問題跟蹤過一個討論:#1677 & #3086