Mongoid将一个类嵌入为多个字段

时间:2013-08-16 15:25:18

标签: ruby-on-rails mongodb mongoid

我正在尝试跟踪用户的当前,之前和所选(期末更改)订阅状态。

在月度期间结束时,如果chosen_subscription != current_subscription,则current_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

然而,告诉Subscription user与三者中的任何一个相反,我都没有意义。

我应该如何构建它?

2 个答案:

答案 0 :(得分: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 :(得分:0)

每个mongoid关系都有一个反比关系,用于正确设置两个边。大多数情况下,它可以根据命名约定,使用的类等正确识别,但有时您需要明确定义它们。

mongoid问题在于抱怨这里User模型中定义的关系是模糊的,因为mongoid无法识别它们中的哪一个映射到subscription.user中的Subscription关系。此外,当使用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