记录所有文档关系是否是 CouchDB 中的反模式?
Is keeping a log of all document relationships an anti-pattern in CouchDB?
当我们 return 数据库中的每个文档都将被客户端使用时,我们还必须向该文档的响应负载添加 属性 "isInUse" 以指示给定的文档是否已记录被其他文档引用。
这是必需的,因为无法删除引用的文档,因此垃圾桶按钮不应显示在客户端应用程序中的列表条目旁边。
所以基本上我们有一个文档可以引用另一个文档的关系 link 这个:
{
"_id": "factor:1I9JTM97D",
"someProp": 1,
"otherProp": 2,
"defaultBank": <id of some bank document>
}
之前我们使用视图和选择器来查询每个文档在其他文档中的引用,但这被证明是非常重要的。
我们团队中的某个人现在是这样实现的:我们在专用 "relationship" 文档中注册所有关系,如下所示,并在服务器每次 created/updated/deleted 文档时更新它们,以反映任何新的引用或取消引用:
{
"_id": "docInUse:bank",
"_rev": "7-f30ffb403549a00f63c6425376c99427",
"items": [
{
"id": "bank:1S36U3FDD",
"usedBy": [
"factor:1I9JTM97D"
]
},
{
"id": "bank:M6FXX6UA5",
"usedBy": [
"salesCharge:VDHV2M9I1",
"salesCharge:7GA3BH32K"
]
}
]
}
问题是这个解决方案是否是反模式以及潜在的缺点是什么。
我想说使用单个文档来记录所有其他文档之间的关系可能会有问题,因为
- 文档 "docInUse:bank" 最终可能会被频繁更新。 Cloudant 允许您更新文档,但是当您进行数千次修订时,文档大小变得 none 微不足道,因为所有以前的修订标记都被保留
- 如果两个进程试图同时更新文档,则更新中央文档会引起文档冲突的问题。您可以有冲突,但您的应用有责任管理它们,请参阅 here
- 如果你有很多关系,这个文档可能会变得非常大(我对你的应用了解不够,无法判断)
另一个解决方案是让您的 bank:*
、factor:*
和 salesCharge:*
文档保持相同,并为每个关系创建一个 文档 例如
{
"_id": "1251251921251251",
"type": "relationship",
"doc": "bank:1S36U3FDD",
"usedby": "factor:1I9JTM97D"
}
然后,您可以通过 doc
或 usedby
的值以及合适的索引来查询文档,从而找出 "join" 两边的文档。
我也看到了实现,其中文档的 _id
字段包含所有信息:
{
"_id": "bank:1S36U3FDD:factor:1I9JTM97D"
"added": "2018-02-28 10:24:22"
}
并且主键有助于为您排序文档 ID,允许您明智地使用 GET /db/_all_docs?startkey=x&endkey=y
来获取给定银行 ID 的关系。
如果需要撤消关系,只需删除文档即可!
通过在您当前实施的每个文档 create/update/delete 上构建关系缓存,您基本上是在数据库中手动重新创建索引。这就是为什么我倾向于将其称为反模式的原因。
改进设计的一个好方法是按照 Glynn 的建议将每个关系存储为单独的文档。
如果您关心的是一致性(我认为可能是这种情况,通过查看您提到的文档类型来判断),请尝试将有关交易的所有信息放入单个文档中。您可以在文档中的一致位置定义关系,因此无需更新视图:
{
"_id":"salesCharge:VDHV2M9I1",
"relations": [
{ "type": "bank", "id": "bank:M6FXX6UA5" },
{ "type": "whatever", "id": "whatever:xy" }
]
}
然后您可以保持观点一致,并且可以依靠 CouchDB 来保持 "relation cache" 最新。
当我们 return 数据库中的每个文档都将被客户端使用时,我们还必须向该文档的响应负载添加 属性 "isInUse" 以指示给定的文档是否已记录被其他文档引用。
这是必需的,因为无法删除引用的文档,因此垃圾桶按钮不应显示在客户端应用程序中的列表条目旁边。
所以基本上我们有一个文档可以引用另一个文档的关系 link 这个:
{
"_id": "factor:1I9JTM97D",
"someProp": 1,
"otherProp": 2,
"defaultBank": <id of some bank document>
}
之前我们使用视图和选择器来查询每个文档在其他文档中的引用,但这被证明是非常重要的。
我们团队中的某个人现在是这样实现的:我们在专用 "relationship" 文档中注册所有关系,如下所示,并在服务器每次 created/updated/deleted 文档时更新它们,以反映任何新的引用或取消引用:
{
"_id": "docInUse:bank",
"_rev": "7-f30ffb403549a00f63c6425376c99427",
"items": [
{
"id": "bank:1S36U3FDD",
"usedBy": [
"factor:1I9JTM97D"
]
},
{
"id": "bank:M6FXX6UA5",
"usedBy": [
"salesCharge:VDHV2M9I1",
"salesCharge:7GA3BH32K"
]
}
]
}
问题是这个解决方案是否是反模式以及潜在的缺点是什么。
我想说使用单个文档来记录所有其他文档之间的关系可能会有问题,因为
- 文档 "docInUse:bank" 最终可能会被频繁更新。 Cloudant 允许您更新文档,但是当您进行数千次修订时,文档大小变得 none 微不足道,因为所有以前的修订标记都被保留
- 如果两个进程试图同时更新文档,则更新中央文档会引起文档冲突的问题。您可以有冲突,但您的应用有责任管理它们,请参阅 here
- 如果你有很多关系,这个文档可能会变得非常大(我对你的应用了解不够,无法判断)
另一个解决方案是让您的 bank:*
、factor:*
和 salesCharge:*
文档保持相同,并为每个关系创建一个 文档 例如
{
"_id": "1251251921251251",
"type": "relationship",
"doc": "bank:1S36U3FDD",
"usedby": "factor:1I9JTM97D"
}
然后,您可以通过 doc
或 usedby
的值以及合适的索引来查询文档,从而找出 "join" 两边的文档。
我也看到了实现,其中文档的 _id
字段包含所有信息:
{
"_id": "bank:1S36U3FDD:factor:1I9JTM97D"
"added": "2018-02-28 10:24:22"
}
并且主键有助于为您排序文档 ID,允许您明智地使用 GET /db/_all_docs?startkey=x&endkey=y
来获取给定银行 ID 的关系。
如果需要撤消关系,只需删除文档即可!
通过在您当前实施的每个文档 create/update/delete 上构建关系缓存,您基本上是在数据库中手动重新创建索引。这就是为什么我倾向于将其称为反模式的原因。
改进设计的一个好方法是按照 Glynn 的建议将每个关系存储为单独的文档。
如果您关心的是一致性(我认为可能是这种情况,通过查看您提到的文档类型来判断),请尝试将有关交易的所有信息放入单个文档中。您可以在文档中的一致位置定义关系,因此无需更新视图:
{
"_id":"salesCharge:VDHV2M9I1",
"relations": [
{ "type": "bank", "id": "bank:M6FXX6UA5" },
{ "type": "whatever", "id": "whatever:xy" }
]
}
然后您可以保持观点一致,并且可以依靠 CouchDB 来保持 "relation cache" 最新。