我是DynamoDB(文档数据库)的新手,需要帮助设计表结构 nosql数据库。
我需要管理不同文件夹中可用的文档。文件夹层次结构可以转到第n级,同一文档可以在多个文件夹中使用。
对于关系数据库,我可以在不同的表中维护层次结构和文档,并通过在这些表上应用连接来提取所需的信息。
我需要知道在DynamoDB表中存储此数据的最佳方法,以便以最有效的方式提取信息。每个用户都有一定的权限,他/她可以根据这些权限查看或编辑文档。
目前我正在尝试将其存储在以下结构中:
documents = [
{
_id: ...,
title: "...",
date_uploaded: ...,
folders: [
folderId,
...
]
},
...
]
folders = [
{
_id: ...,
title: "..."
}
]
在documentId的帮助下,我可以从文件夹表中提取文档所在的文件夹列表和该文件夹的详细信息,但不知道如何维护文件夹层次结构。
有人可以帮我解决这个问题吗?
答案 0 :(得分:3)
你这里有多对多的关系。文件夹可以包含许多文档,文档可以包含在许多文件夹中。没有一种方法可以对这些类型的关系进行建模,因为它们往往是特定于应用程序的,并且高度依赖于您的访问模式。既然如此,我将需要对您的应用程序做一些假设来回答您的问题。我会尽力说清楚我在假设的地方和地点。
通常使用NoSQL,您可以设计模式并组织数据以支持应用程序中的特定视图。很多时候涉及非规范化数据,特别是在多对多关系的情况下,这就是为什么这些类型的关系的策略往往是特定于应用程序的。
在下面的示例中,我将假设您有某种Master-Detail视图,其中主列表包含子文件夹和有关特定文件夹中文档的摘要信息,详细信息视图显示有关该文档的所有信息。目前选择的文件。
<强>架构强>
首先,我将根据您上面的模式定义模式,但稍微修改以更适合DynamoDB。
<强>文件夹强>
{
"id": String,
"parent_id": String,
"name": String,
}
<强>文档强>
{
"id": String,
"title": String,
"contents": String,
"date_modified": String,
"date_uploaded": String,
}
文档模型非常自我解释。此外,我们将创建一个DocumentSummary,它将只包含有关Document的摘要信息。
<强> DocumentSummary 强>
{
"id": String,
"parent_id": String,
"title": String,
"date_uploaded": String,
}
根据经验,DocumentSummary模型应该是Document模型的一个子集,并且只包含不可变的字段,例如: date_uploaded
,或非常缓慢地变异,例如title
。诸如date_modified
之类的字段可能会非常迅速地发生变异,这可能会导致问题(我们稍后会看到原因)。此外,contents
等字段不应进入我们的摘要模型。除了contents
是一个快速静音的领域之外,没有任何关于它的“总结”。请记住,我们的摘要模型越接近我们的完整模型,我们的摘要模型就变得越不实用。在某些时候,我们不妨丢弃我们的摘要模型,只使用我们的完整模型。
<强>表格强>
我们将有两个表,DocumentTable和DirectoryTable。
<强> DocumentTable 强>
Hash Key: "id"
DocumentTable包含我们的文档,并为我们提供了id
<强> DirectoryTable 强>
Hash Key: "parent_id"
Sort Key: "id"
DirectoryTable将包含Folders和DocumentSummaries。由于此表包含两种不同的类型,因此每种类型的ID都不会发生冲突非常重要。我建议为您的ID添加名称空间,例如: “folder-123”和“document-123”。
DirectoryTable使我们能够查询给定文件夹中的所有子文件夹和文档摘要,并允许我们按parent_id
和id
更新文件夹和DocumentSummaries。
例如,如果我们想在“folder-123”中找到所有子文件夹和文档摘要,我们可以使用以下参数进行查询。
{
"TableName": "DirectoryTable",
"KeyConditionExpression": "parent_id = :parent_id",
"ExpressionAttributeValues": {
":parent_id": {"S": "folder-123"},
}
}
注意:对于顶级文件夹和文档,您需要使用虚拟parent_id
,例如“root”
此外,我们可能想要查询特定文档所在的文件夹。为了回答这个问题,我们需要在DirectoryTable上创建全局二级索引(GSI)
id-parent_id-index(DirectoryTable GSI)
Hash Key: "id"
Sort Key: "parent_id"
现在,我们可以使用带有以下参数的Query来查找ID为“document-123”的文档的所有父文件夹ID。
{
"TableName": "DirectoryTable",
"IndexName": "id-parent_id-index",
"KeyConditionExpression": "id = :id",
"ExpressionAttributeValues": {
":id": {"S": "document-123"}
}
}
您可能想知道如何通过id
查询文件夹。您可以使用与上面相同的查询参数再次使用id-parent_id-index
,将“document-123”替换为文件夹ID,例如“文件夹123”。如果你已经正确地完成了事情,那么这应该产生一个长度为1的Items数组。
最后,当在相应的Document上更新其中一个重复字段时,我们需要一种更新DocumentSummaries的方法。我们可以使用DynamoDB Streams。在DocumentTable上创建DynamoDB流并监听更新事件。如果更新事件指示已修改其中一个重复字段,请使用id-parent_id-index
查找文档的所有父文件夹,然后按parent_id
和id
更新DocumentSummary。此更新可能非常昂贵,因为它是扇出问题的一个例子,例如,单个文档更新会导致N DocumentSummary更新。最小化此成本非常重要,特别是在大规模时,这就是为什么我们只想在DocumentSummary中包含不可变或缓慢变异的字段。