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" 的角度来看,你可能不想像现在这样建模。我只是在详细说明...
我想知道是否可以(甚至正确)使用嵌入文档作为其他文档中的参考。
我知道我可以将嵌入文档移动到它自己的集合中,但主要目标是获得嵌入文档的性能优势并避免重复。
例如:
用户
{
_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" 的角度来看,你可能不想像现在这样建模。我只是在详细说明...