Spring Data MongoDB - 嵌入式文档作为其他文档的引用

8
我想知道是否可以(甚至是正确的)在其他文档中使用嵌入式文档作为引用。
我知道我可以将嵌入的文档移动到它自己的集合中,但主要目标是具有嵌入式文档的性能优势并避免重复。
例如:
用户
{
    _id: ObjectId("4fed0591d17011868cf9c982"),
    _class: "User"
    ...
    addresses: [ {
        _id: ObjectId("87KJbk87gjgjjygREewakj86"),
        _class: "Address",
        ...
    } ]
}

订单

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

根据提供的信息,我想你最好使用关系型数据库... - Witold Kaczurba
3个回答

9
您的情况让我想起了典型的关系型方法,当我开始使用面向文档的数据库时,我也曾是受害者。在您的示例中,所有实体都被引用,不再有冗余。
您应该开始习惯于抛弃规范化并开始复制数据的想法。在许多情况下,很难确定哪些数据应该被引用,哪些应该被嵌入。不过在您的情况下相当明显。
在不知道您的完整领域模型的情况下,地址似乎是值对象的完美候选。不要维护Address集合,而是将其嵌入到用户对象中。在Order中,您可以向用户提供一个引用,这样就会隐含地得到地址对象,并且可能是有意义的,因为订单是由用户创建的。
但是...我建议您完全将地址嵌入订单中。首先,这样更快,因为您无需解析引用。其次,已发货订单中的地址永远不会更改!考虑一下去年的订单。如果您持有地址的引用,一旦用户更改其地址,您将失去他们所运送到的地址的信息。
建议:始终拍摄地址快照并将其嵌入Order中。将用户的MongoDB ID保存为常规字符串(无@DBRef),并将其放在`Order`中。如果用户更改地址,则可以查询该用户的所有未发货订单,并修改地址。

1
感谢您的评论! - Anderson Fernandes Silva

1

既然你问这是否正确,我会小心地说,“不是”。至少通常情况下不是。

但是如果你确实想坚持使用用户嵌入地址:

您可以在Order对象中引用用户嵌入的地址,只是不是您想象的那种方式!如果您在订单中存储了用户的ID(如果Order属于User,则应该已经存在),则仅需使用user.address而不是像您所做的那样复制地址实例。

替代方案

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

更常见的方法是实例化一个新的订单对象,使用用户的地址作为订单的默认“收货地址”,但允许用户在需要时覆盖运送地址。理论上,每个订单都可以有一个不同的“收货地址”。

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

评论

订单更多是历史文档,而不是会改变的文档。因此,一旦下单,订单通常是不可变的。您的模型允许每次用户更改地址时更改地址。这种更改会影响到订单,并且从正常订单业务逻辑的角度来看是不正确的。

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

顺便说一句:@Matt给了您提示,从“问题域”角度来看,您可能不希望按照您的方式进行建模。我只是在详细阐述...


0

由于我没有得到答案,我将在这里发布我是如何做到的。如果您有更好的解决方案,我很乐意听取。

看起来没有办法在另一个集合中创建/引用集合,因此我不得不从用户集合中提取地址到它自己的集合中,并在用户和订单集合中创建一个引用,如此处所述。我原本期望有更灵活的方法,但找不到。

用户

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接