我的问题:我打算在数据库中存储一个"项目",其中一个项目由多个项目组成,例如文档,每个文档都有多个项目,例如段落.段落可以交叉引用其他文件中的段落.存在许多团队,每个团队可能有很多项目.团队成员编辑,更新和优化交叉引用文档的位置,直到他们满意为止,然后审阅文档或项目.
在审核后发布文档时,将保留已颁发的状态,并且对新的暂存/当前状态进行任何更改.
项目发布时,将对其进行全面审核和发布.然后在该问题中保留文档集,内容和交叉引用,以便在整个项目中捕获状态.然后,将以下任何编辑应用于新的临时/当前版本,使发布的集合可以在问题点处进行读取.
我考虑过使用典型的数据库设置,但我担心a)存储的行数会很快增长; 和b)找到"当前"集合,或任何特定项目的集合将太复杂/脆弱,无法可靠地使用.
作为一种变体,将例如JSON中的文档存储在单个文档行中会阻止碎片段落的传播,而不会导致大的性能损失吗?
另一种想法是为每个项目分配一个Git存储库; 但后来担心存储和性能,并且必须存储"文档",例如JSON文档或类似文件,以使系统高效工作.然后,您最终会为每个登录的用户管理每个会话的暂存区域,这会很慢 - 对吧?除GitHub外,GitLab等允许快速访问回购历史......
最初将通过Web应用程序访问数据库(或等效的).最终,它可以通过API提供给本地客户端,甚至允许本地客户端使用git repos,如果这是遵循的路径.理想情况下,将使用PHP或NodeJS易于访问的技术.
具体问题 - 如何/应该存储和访问需要配置控制的多个相关人工制品?
1> Félix Gagnon..:
TL; DR
您在此处公开的模型是关系数据库的一个非常明确的用例.在数据库中存储文本时没有明显的性能问题,如果真的遇到了数据库问题,那么服务器资源现在非常便宜,即使任何瓶颈几乎肯定会出现在您的代码而不是数据库操作中.
大纲
我将首先揭示为什么我认为事件采购和nosql不合适的想法,然后喋喋不休地谈论一种可能的方式来模拟你所说的域,并完成一些如何进行任何行动的例子.
不是事件采购
我首先考虑事件采购,因为它易于管理......事件的演变......例如编辑段落,审查问题等.
然而,在尝试对其进行模型化时,似乎这是不切实际的,因为为了减少每次查询问题时必须重放整个事件链,您必须在数据库/缓存系统中保存快照.支持问题和事件采购的定时快照并不像是赢得净胜利.
不是Nosql数据库
我相信nosql数据库实际上是一个明显的损失.你所说的一切都是绝对的,绝对的,关系的.域的结构似乎没有变化,因此您无法从nosql灵活性中获益.
是关系数据库
因此,我认为传统的关系SQL数据库设置应该完全是首选.
我有处理相对大量文本的第一手经验(非常类似于具有多个任意长段落的文档)并且对于乘法行没有任何问题.它们会成倍增加,特别是如果你跟踪历史记录,但这就是关系数据库的用途,处理大量数据行.
数据库的初步模型
我认为这里提到的大多数实体都可以直接模型化.通过直截了当,我的意思是有几个标准字段:
- id
- parent_id (when the entity is a child, such as paragraph)
- time_created: timestamp
- time_updated: timestamp
some dbms offer automatic updating of timestamp fields on update of the row
基于此,这可以是一个段落结构.请注意,状态在这里,它很可能在paragraph_revision
表中,例如支持在第一次发布段落后检查段的进一步修订.或者甚至可能两者,因为我们可以看到以后使用它.
paragraph
- paragraph_id
- document_id
- created_at
- status (to_be_reviewed, in_review, reviewed, rejected, varia)
- order (maybe, if a specific order within a document is desired)
但是请小心,因为这种情况有点棘手.由于我们希望获得所有更改及其作者的详细历史记录,因此段落的实际内容位于另一个表中,链接到原始(单个)段实体.请注意,只有一个created_at
时间戳值.在这个范例中,段落修订永远不会更新,新的创建.可以使用数据库触发器或其他技巧来实现.
paragraph_revision
- paragraph_id
- created_at
- text
- title (if needed)
- revision_number
possibly a simple integer based revision number can be used
- author_id
段落引用可以保证另一个多对多关系表.要获取所有一个段落引用,您select referenced_paragraph_id from paragraph_reference where paragraph_id = :id
可以通过切换列来选择引用特定段落的所有段落.在这里,您可以引用一般段落或段落的特定修订,以免丢失历史记录.
paragraph_reference
- paragraph_id
- referenced_paragraph_id
问题似乎是一个项目,一个或多个文档,每个文档由一个或多个段落组成.基本上,一个issue
表,issue_document
表中,链接到单个的问题,和一个issue_paragraph
表,包含特定paragraph_revision
的ID,链接到这些issue_document
表.文档表可以删除,因为所有段落都是文档的子项,但我更喜欢能够直接选择内容而不是从子项中选择它们.
issue
- issue_id
- timestamp
issue_document
- issue_id
- document_id
issue_paragraph
- document_id
- paragraph_content_id
UUID可以是相关的
这可能是将uuids用于实体而不是数字ID的有效情况,特别是如果增长使得必须复制数据库或能够在将数据发送到数据库之前创建有效实体.
外键不是可选的
虽然不是太复杂,但这是一个有点花哨的架构.外键不是一个选项.每个父ID必须具有外键,数据库完整性必须由数据库引擎强制执行,否则这将变得脆弱.使用外键,这样一个有点复杂的系统可以并且将随着时间的推移保持一致.
管理域操作
希望我给出的关于如何创建表的几个例子可以给出一般结构的概念,现在给出一些具体的例子,说明它如何适用于你的不同用例.
创建项目
相当简单:在project
表格中添加一行,不需要任何其他操作.
创建段落
需要添加两行:一行将唯一标识段落paragraph
,一行包含其内容paragraph_revision
.
更新段落
paragraph_revision
表中添加了一行.如果人们每5秒钟保存一次,可能会使用和更新一个条目,直到用户做出"我对这个版本没问题"的操作.(使这成为"不更新修订版"规则的唯一例外.可以使用其他技巧,例如temp_paragraph_revision
表格.)
回顾段落
选择a的编号最高的版本paragraph_revision
可以让您查看要审阅的当前版本.可以通过选择特定的所有内容来制作所有修订的列表paragraph_id
.设置审核后,状态将在paragraph
和paragraph_revision
表中更新,可能会阻止进行任何修订,或者在添加修订时重置审核状态.
发布段落或项目
在问题表中创建一行,将文档链接到issue_document表中的该问题,链接到表中paragraph_revision
的这些文档issue_paragraph
.可以在此处进行选择,即在此issue_paragraph
表中包含修订的实际内容,以确保在发布问题后无法更改段落内容,但是如果永远不更新修订的规则使用,这是不必要的.
结论
虽然这可能看起来像预先创建的很多表,但是大多数表都相当小,而且一个好的UML图表会让人对这些表格犹豫不决.在此使用联接,确保在这些联接中使用的列上保留索引.所有这些都可以在大多数sql发行版中实现,如果你熟悉它就是MariaDB和MySQL,如果你知道的话,可以在postgres中实现.