Spring 数据 MongoDB - 嵌入文档作为其他文档中的参考

Spring Data MongoDB - Embedded Document as Reference in Other Document

我想知道是否可以(甚至正确)使用嵌入文档作为其他文档中的参考。

我知道我可以将嵌入文档移动到它自己的集合中,但主要目标是获得嵌入文档的性能优势并避免重复。

例如:

用户

{
    _id: ObjectId("4fed0591d17011868cf9c982"),
    _class: "User"
    ...
    addresses: [ {
        _id: ObjectId("87KJbk87gjgjjygREewakj86"),
        _class: "Address",
        ...
    } ]
}

订单

{
    _id: ObjectId("gdh60591d123487658cf9c982"),
    _class: "Order",
    ...
    address: ObjectId("87KJbk87gjgjjygREewakj86")
}

因为我没有得到答案,所以我会post在这里我是如何做到的。如果您有更好的解决方案,我很乐意提供给您。

看起来没有办法 create/reference 一个集合在另一个集合中,所以我不得不从用户集合中提取地址到它自己的集合中,并在 User 和 Order 集合中创建一个引用,如前所述here。我期待更灵活的东西,但找不到。

用户

{
    _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")
    }
}

你的案例让我想起了典型的关系方法,当我开始使用面向文档的数据库时,我也是这种方法的受害者。您所有的 示例中的实体均已引用,不再赘述

您应该开始习惯放弃规范化并开始复制数据的想法。在许多情况下,很难确定哪些数据应该被引用,哪些应该被嵌入。不过,您的情况往往很清楚。

在不了解整个域模型的情况下,该地址似乎是值对象的完美候选者。不要维护 Address 集合,将其嵌入到用户对象中。在 Order 中,您可以引用用户,这会隐式地为您提供地址对象并且可能有意义,因为订单是由用户发出的。

但是...我建议您将地址完全嵌入订单中。首先,它更快,因为您不需要解析引用。其次,发货订单中的地址永远不能更改!考虑去年的订单。如果您保留对地址的引用,一旦用户更改地址,您将丢失他们被运送到哪个地址的信息。

建议:始终对地址进行快照并将其嵌入 Order。将用户的 MongoDB ID 保存为 `Order.如果用户要更改地址,您可以查询该用户所有未发货的订单并修改地址。

既然你问这是否正确,我会温和地说,"No."至少通常不会。

但如果您确实想坚持使用来自用户的嵌入地址:

您可以在订单对象中引用用户嵌入地址,只是不是您想象的那样!如果您将用户的 ID 存储在订单中(如果订单 belongs_to 用户,它应该已经存在),那么您只需使用 user.address 而不是像您所做的那样复制地址实例。

备选方案

我希望说明一种更好的域建模方法...

一种更常见的方法是实例化一个新的订单对象,使用用户地址作为订单的默认 "ship to" 地址,但允许用户根据需要覆盖送货地址。理论上,每个订单可以有一个 不同的 "ship to" 地址。

仅仅因为两个 类 有一个地址,并不意味着它们一定是同一个地址。

评论

订单更像是一份历史文件,而不是不断变化的文件。因此,订单通常 不可变 一旦下达,您的模型允许地址在每次用户更改地址时更改。这种变化会波及订单,并且在正常订单业务逻辑的范围内是不正确的。

假设您去年的地址在西班牙,并且当您 运行 去年的订单报告时,订单 #1 显示西班牙。想象一下,如果您今年的地址现在是葡萄牙,而订单 #1 现在在同一份报告中显示葡萄牙。这在事实上是不正确的。

顺便说一句:@Matt 给了你提示,从 "problem domain" 的角度来看,你可能不想像现在这样建模。我只是在详细说明...