2

我是 DynamoDB(文档数据库)的新手,在设计 nosql 数据库中的表结构时需要帮助。

我需要管理不同文件夹中可用的文档。文件夹层次结构可以进入第 n 级,并且同一文档可以在多个文件夹中使用。

在关系数据库的情况下,我可以在不同的表中维护层次结构和文档,并通过在这些表上应用连接来提取所需的信息。

我需要知道将这些数据存储在 DynamoDB 表中的最佳方式,以便以最有效的方式提取信息。每个用户都将拥有一定的权限,基于这些权限他/她可以查看或编辑文档。

目前我正在尝试将其存储在以下结构中:

documents = [
{
    _id: ...,
    title: "...",
    date_uploaded: ...,
    folders: [
        folderId,
        ...
    ]
},
...
]

folders = [
{
    _id: ...,
    title: "..."
}
]

在 documentId 的帮助下,我可以从文件夹表中提取文档所在的文件夹列表以及该文件夹的详细信息,但不确定如何维护文件夹层次结构。

有人可以帮我解决这个问题吗?

4

1 回答 1

5

你在这里有一个多对多的关系。一个文件夹可以有多个文档,一个文档可以位于多个文件夹中。没有一种方法可以对这些类型的关系进行建模,因为它们往往是特定于应用程序的并且高度依赖于您的访问模式。既然是这种情况,我将需要对您的应用程序做出一些假设来回答您的问题。我会尽量说明我在哪里以及关于我的假设。

一般来说,使用 NoSQL,您可以设计模式并组织数据以支持应用程序中的特定视图。很多时候这涉及到数据的非规范化,特别是在多对多关系的情况下,这就是为什么这些类型的关系的策略往往如此特定于应用程序的原因。

在下面的示例中,我将假设您有某种主从视图,其中主列表包含子文件夹和特定文件夹中文档的摘要信息,详细视图显示有关当前选定文档的所有信息.

模式


首先,我将根据上面的模式定义模式,但稍作修改以更适合 DynamoDB。

文件夹

{
  "id": String,
  "parent_id": String,
  "name": String,
}

文档

{
  "id": String,
  "title": String,
  "contents": String,
  "date_modified": String,
  "date_uploaded": String,
}

Document 模型很容易解释。此外,我们将创建一个 DocumentSummary,其中仅包含有关文档的摘要信息。

文件摘要

{
  "id": String,
  "parent_id": String,
  "title": String,
  "date_uploaded": String,
}

根据经验,DocumentSummary 模型应该是 Document 模型的子集,并且只包含不可变的字段,例如date_uploaded,或者变化非常缓慢的字段,例如title。诸如date_modified可能会非常迅速地发生变化的字段可能会导致问题(我们稍后会看到原因)。此外,诸如此类的字段contents不应进入我们的摘要模型。除了contents是一个快速静音的领域之外,它没有任何“总结”。请记住,我们的汇总模型越接近我们的完整模型,我们的汇总模型就越没用。在某些时候,我们不妨抛弃我们的汇总模型,只使用我们的完整模型。


我们将有两个表,DocumentTable 和 DirectoryTable。

文档表

Hash Key: "id"

DocumentTable 包含我们的文档并为我们提供执行 CRUD 操作的能力id

目录表

Hash Key: "parent_id"
Sort Key: "id"

DirectoryTable 将包含文件夹和 DocumentSummaries。由于此表包含两种不同的类型,因此每种类型的 ID 不能发生冲突非常重要。我建议在您的 ID 前加上命名空间,例如“folder-123”和“document-123”。

DirectoryTable 使我们能够查询给定文件夹中的所有子文件夹和文档摘要,并允许我们通过parent_id和更新文件夹和 DocumentSummaries id

例如,如果我们想在“folder-123”中查找所有子文件夹和文档摘要,我们可以使用以下参数查询。

{
    "TableName": "DirectoryTable",
    "KeyConditionExpression": "parent_id = :parent_id",
    "ExpressionAttributeValues": {
        ":parent_id": {"S": "folder-123"},
    }
}

注意:对于顶级文件夹和文档,您需要使用parent_id“root”等虚拟对象

此外,我们可能想要查询特定文档所在的文件夹。要回答这个问题,我们需要在 DirectoryTable 上创建一个全局二级索引 (GSI)

id-parent_id-index(目录表 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,例如“folder-123”。如果你做对了,这应该会产生一个长度为 1 的 Items 数组。

最后,我们需要一种方法来更新 DocumentSummaries,当其中一个重复字段在相应的 Document 上更新时。我们可以为此使用 DynamoDB Streams。在 DocumentTable 上创建一个 DynamoDB Stream 并监听更新事件。如果更新事件指示重复字段之一已被修改,请使用查找 Document 的所有父文件夹,然后通过和id-parent_id-index更新 DocumentSummary 。此更新可能非常昂贵,因为它是扇出问题的一个示例,例如,单个 Document 更新会导致 N DocumentSummary 更新。最小化这个成本很重要,特别是在大规模时,这就是为什么我们只想在 DocumentSummary 中包含不可变或缓慢变化的字段。parent_idid

于 2018-03-21T19:19:34.873 回答