我想知道在其他文档中使用嵌入式文档作为参考是否可能(甚至是正确的)。
我知道我可以将嵌入式文档移动到自己的集合中,但主要目标是获得嵌入式文档的性能优势并避免重复。
例如:
用户
{
_id: ObjectId("4fed0591d17011868cf9c982"),
_class: "User"
...
addresses: [ {
_id: ObjectId("87KJbk87gjgjjygREewakj86"),
_class: "Address",
...
} ]
}
顺序
{
_id: ObjectId("gdh60591d123487658cf9c982"),
_class: "Order",
...
address: ObjectId("87KJbk87gjgjjygREewakj86")
}
答案 0 :(得分:6)
您的案例让我想起了典型的关系方法,当我开始使用面向文档的数据库时,我也是它的受害者。您所有的 引用了示例中的实体,不再存在冗余。
你应该开始习惯放开规范化并开始复制数据的想法。在许多情况下,很难确定应该引用哪些数据以及应该嵌入哪些数据。不过,你的情况往往很清楚。
在不知道整个域模型的情况下,地址似乎是值对象的完美候选者。不要维护Address
集合,将其嵌入用户对象中。在Order
中,您可以对用户进行引用,这可以隐式地为您提供地址对象,并且可能有意义,因为订单是由用户创建的。
但是......我建议您将地址完全嵌入订单中。首先,它更快,因为您不需要解析引用。其次,发货订单中的地址永远不会改变!考虑去年的订单。如果你持有对地址的引用,一旦用户更改了地址,你就会丢失它们发送到哪个地址的信息。
建议:始终拍摄地址的快照并将其嵌入Order
。将用户的MongoDB ID保存为`Order中的常规字符串(无@DBRef
)。如果用户应更改其地址,您可以查询该用户的所有未发货订单并修改该地址。
答案 1 :(得分:1)
既然你问这是否正确,我会轻轻地说,“不。”至少不是典型的。
但如果你确实想坚持使用用户的嵌入地址:
您可以在Order对象中引用用户嵌入的地址,而不是您想象的方式!如果您按顺序存储了用户的id(如果Order belongs_to User,它应该已存在),那么您只需使用user.address
而不是像您一样复制地址实例。
<强> ALTERNATIVE 强>
我希望能够说明一种更好的域建模方法......
更常见的方法是使用用户的地址作为订单的默认“送货地址”来实例化新订单对象,但如果需要,允许用户覆盖送货地址。从理论上讲,每个订单都可以有不同“发货到”地址。
仅仅因为两个类都有一个地址,并不意味着它们必须是同一个地址。
<强>评注强>
订单更像是历史文件,而不是更改的文件。因此,订单通常是 immutable 一旦放置,您的模型允许每次用户更改其地址时更改地址。这种变化会影响到订单,并且在正常订单业务逻辑发生的情况下会不正确。
假设您去年在西班牙的地址,并且您在去年运行订单报告时显示西班牙订单#1。想象一下,如果你今年的地址现在是葡萄牙,那么#1号订单现在会在同一份报告中显示葡萄牙。这实际上是不正确的。
顺便说一句:@Matt给了你一个提示,从“问题领域”的角度来看,你可能不想像你一样对它进行建模。我只是在详细说明......答案 2 :(得分:0)
由于我没有回答,我会在这里发布我是如何做到的。如果你有更好的解决方案,我很高兴在这里。
看起来没有办法在另一个集合中创建/引用集合,所以我不得不将用户集合中的地址提取到它自己的集合中,并在提及的用户和订单集合中创建一个引用{{3} }。我期待更灵活的东西,但找不到。
用户强>
{
_id: ObjectId("4fed0591d17011868cf9c982"),
_class: "User"
...
addresses: [ {
"$ref" : "addresses",
"$id" : ObjectId("87KJbk87gjgjjygREewakj86")
} ]
}
<强>地址强>
{
_id: ObjectId("87KJbk87gjgjjygREewakj86"),
...
}
<强>顺序强>
{
_id: ObjectId("gdh60591d123487658cf9c9867"),
_class: "Order",
...
address: {
"$ref" : "addresses",
"$id" : ObjectId("87KJbk87gjgjjygREewakj86")
}
}