管理 AWS DynamoDB 中分层文件夹结构中可用的文档

Manage documents available in hierarchical folder structure in AWS DynamoDB

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

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

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

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

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

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

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

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

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

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

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

在下面的示例中,我假设您有某种 Master-Detail 视图,其中主列表包含 sub-folders 以及有关特定文件夹和详细信息视图中文档的摘要信息显示有关当前所选文档的所有信息。

架构


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

文件夹

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

文档

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

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

文档摘要

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

根据经验,DocumentSummary 模型应该是文档模型的子集,并且只包含 immutable 字段,例如date_uploaded,或者变异非常缓慢,例如titledate_modified 之类的字段可能会非常迅速地发生变化,这可能会导致问题(我们稍后会看到原因)。此外,contents 等字段不应进入我们的摘要模型。除了 contents 是一个快速静音的字段之外,"summary" 什么都没有。请记住,我们的摘要模型越接近我们的完整模型,我们的摘要模型就越没用。在某些时候,我们不妨丢弃我们的摘要模型,只使用我们的完整模型。

表格


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

文档表

Hash Key: "id"

DocumentTable 包含我们的文档,并为我们提供了通过 id

执行 CRUD 操作的能力

目录表

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

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

DirectoryTable 使我们能够查询给定文件夹中的所有 sub-folders 和文档摘要,并允许我们通过 parent_idid 更新文件夹和文档摘要。

例如,如果我们想查找 "folder-123" 内的所有 sub-folders 和文档摘要,我们可以使用以下参数进行查询。

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

注意: 对于 top-level 文件夹和文档,您需要使用虚拟 parent_id 例如 "root"

此外,我们可能想查询特定文档所在的文件夹。要回答这个问题,我们需要在 DirectoryTable

上创建一个全局二级索引 (GSI)

id-parent_id-index(目录表 GSI)

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

现在,我们可以使用带有以下参数的查询来查找 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 的项目数组。

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