在 DynamoDB 中建模 Parent/Child/Subchild 关系
Modelling Parent/Child/Subchild relationships in DynamoDB
我正在开发以下休息服务:
GET /parents
- 获取所有 parents
GET /parents/{id}
- 通过 id
获取 parent
POST /parents
- 添加新的 parent
GET /parents/{pid}/children
- 获取所有 children for parent pid
GET /parents/{pid}/children/{id}
- 通过 id 获取 child for parent pid
POST /parents/{pid}/children
- 为 parent pid
添加新的 child
GET /parents/{pid}/children/{cid}/subchildren
- 获取 parent pid 和 child cid
的所有 subchildren
GET /parents/{pid}/children/{cid}/subchildren{id}
- 通过 id 获取 subchildchild for parent pid 和 child cid
POST /parents/{pid}/children/{cid}/subchildren
- 为 parent pid 和 child cid
添加新的 subchild
我必须使用 DynamoDB(或 AWS 上的其他一些无服务器且廉价的数据库选项)来执行 CRUD 操作(到目前为止,更新和删除不是优先事项)。
当调用 GET /parents
时,响应将是:
[
{
"id": 1,
"name": "parent1",
"children": [
{
"id": 1,
"name": "parent1_child1",
"subchildren": [
{
"id": 1,
"name": "parent1_child1_subchild1"
},
{
"id": 2,
"name": "parent1_child1_subchild2"
}
]
},
{
"id": 2,
"name": "parent1_child2",
"subchildren": [
{
"id": 1,
"name": "parent1_child2_subchild1"
},
{
"id": 2,
"name": "parent1_child2_subchild2"
},
{
"id": 3,
"name": "parent1_child2_subchild3"
}
]
}
]
},
{
"id": 2,
"name": "parent2",
"children": [
{
"id": 1,
"name": "parent2_child1",
"subchildren": [
{
"id": 1,
"name": "parent2_child1_subchild1"
}
]
},
{
"id": 2,
"name": "parent2_child2",
"subchildren": [
{
"id": 1,
"name": "parent2_child2_subchild1"
},
{
"id": 2,
"name": "parent2_child2_subchild2"
},
{
"id": 3,
"name": "parent2_child2_subchild3"
},
{
"id": 4,
"name": "parent2_child2_subchild4"
}
]
}
]
}
]
我应该如何为 DynamoDB 中的数据建模?我正在使用 spring-data-dynamodb 来执行存储和检索选项。
在这种情况下,创建模型的最佳设计是什么 类?
目前我已经创建了 https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SampleData.CreateTables.html#SampleData.CreateTables2
中提到的不同表格
你可以做几件事:
A:更适合 cost/performance 但需要一些额外的工作
在你的 table 中,创建一个分区键 PK
和顺序键 OK
作为两个字符串(当然你可以使用任何你想要的键名,这只是一个例子——我的意思是,键不在你的数据模型中,你必须在插入时创建它们)。创建项目时,不要将它们存储为一个大文件,而是将 parents、children 和 subchildren 分开存储。每个项目的 PK
值 parent
/child
/subchild
和 OK
您在每个项目的 name
中的值它具有正确的 parentname_childname_subchildname 继承类型,或者您可以从每个项目的 parents 属性构造它。
检索后,您可以获得给定 parent 的所有子 children,例如使用 where PK = subchild and OK begins_with parentname_
等表达式有效地 returns 所有项目subchilds 并且属于给定的 parent,无论 child 是什么。如果您还想在 child 上进行过滤,您可以执行 begins with parentname_childname_
。如果你只想要一个parent的children,你可以做where PK = child and OK begins_with parentname_
。如果你确切地知道你想要什么child,而不是使用begins_with
,你可以简单地做where PK = child and OK = parentname_childname
。等等......(我使用 SQL-like 语法来简化 writing/reading 但它很容易转换为 dynamodb 查询语言)。
这个想法过于区分你的项目的实际属性和你如何检索你的项目。如果可以帮助您轻松找到您的物品,您可以创建完全伪造的钥匙!
您可能根本不需要区分 PK 中的 child“级别”,您也可以只使用通用值来获得类似的结果。此方法的主要 objective 是在特定分区键中使用 begins_with
的顺序键。
此方法的主要问题是,一旦您拥有 /parents
示例中的所有 parents/children/subchildren,您将不得不完全用代码重建树。但就dynamodb而言,这可能是最优化的解决方案。
B:使用过滤器更简单,但(更)糟糕 cost/performance
将每个 parent 存储在一个大文档中,然后使用像 where item.children[].subchildren[].name = parentname_childname_subchildname
这样的过滤器扫描您的 table。它效率不高并且成本更高,因为过滤器应用于查询结果,因此您首先检索所有内容然后仅过滤您想要的内容,因此您首先支付所有项目然后删除那些您不想要的.它有效,但不是很好,特别是如果你想要 100000 件中的 5 件。
主要优点是,对于 /parents
调用,您可以获得所描述的整个层次结构。但对于子路径 /parents/x/children/...
,您需要重新格式化代码中的 object 或仅查询投影表达式中的子属性。
C: 使用本地二级索引
有点像解决方案 A,您在其中单独存储项目,所有项目具有相同的 PK,但具有具有 3 个不同顺序键的本地二级索引:name_parent
、name_child
、name_subchild
.然后您可以根据需要分别查询每个索引。对于此用例,使用 LSI 比 GSI 更适合成本优化,但您可以自由地使用 GSI 实现相同的想法。它也可以正常工作(关于重新格式化 parent 元素以包含 children 元素的相同限制)。
我正在开发以下休息服务:
GET /parents
- 获取所有 parents
GET /parents/{id}
- 通过 id
获取 parent
POST /parents
- 添加新的 parent
GET /parents/{pid}/children
- 获取所有 children for parent pid
GET /parents/{pid}/children/{id}
- 通过 id 获取 child for parent pid
POST /parents/{pid}/children
- 为 parent pid
GET /parents/{pid}/children/{cid}/subchildren
- 获取 parent pid 和 child cid
的所有 subchildren
GET /parents/{pid}/children/{cid}/subchildren{id}
- 通过 id 获取 subchildchild for parent pid 和 child cid
POST /parents/{pid}/children/{cid}/subchildren
- 为 parent pid 和 child cid
我必须使用 DynamoDB(或 AWS 上的其他一些无服务器且廉价的数据库选项)来执行 CRUD 操作(到目前为止,更新和删除不是优先事项)。
当调用 GET /parents
时,响应将是:
[
{
"id": 1,
"name": "parent1",
"children": [
{
"id": 1,
"name": "parent1_child1",
"subchildren": [
{
"id": 1,
"name": "parent1_child1_subchild1"
},
{
"id": 2,
"name": "parent1_child1_subchild2"
}
]
},
{
"id": 2,
"name": "parent1_child2",
"subchildren": [
{
"id": 1,
"name": "parent1_child2_subchild1"
},
{
"id": 2,
"name": "parent1_child2_subchild2"
},
{
"id": 3,
"name": "parent1_child2_subchild3"
}
]
}
]
},
{
"id": 2,
"name": "parent2",
"children": [
{
"id": 1,
"name": "parent2_child1",
"subchildren": [
{
"id": 1,
"name": "parent2_child1_subchild1"
}
]
},
{
"id": 2,
"name": "parent2_child2",
"subchildren": [
{
"id": 1,
"name": "parent2_child2_subchild1"
},
{
"id": 2,
"name": "parent2_child2_subchild2"
},
{
"id": 3,
"name": "parent2_child2_subchild3"
},
{
"id": 4,
"name": "parent2_child2_subchild4"
}
]
}
]
}
]
我应该如何为 DynamoDB 中的数据建模?我正在使用 spring-data-dynamodb 来执行存储和检索选项。
在这种情况下,创建模型的最佳设计是什么 类?
目前我已经创建了 https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SampleData.CreateTables.html#SampleData.CreateTables2
你可以做几件事:
A:更适合 cost/performance 但需要一些额外的工作
在你的 table 中,创建一个分区键 PK
和顺序键 OK
作为两个字符串(当然你可以使用任何你想要的键名,这只是一个例子——我的意思是,键不在你的数据模型中,你必须在插入时创建它们)。创建项目时,不要将它们存储为一个大文件,而是将 parents、children 和 subchildren 分开存储。每个项目的 PK
值 parent
/child
/subchild
和 OK
您在每个项目的 name
中的值它具有正确的 parentname_childname_subchildname 继承类型,或者您可以从每个项目的 parents 属性构造它。
检索后,您可以获得给定 parent 的所有子 children,例如使用 where PK = subchild and OK begins_with parentname_
等表达式有效地 returns 所有项目subchilds 并且属于给定的 parent,无论 child 是什么。如果您还想在 child 上进行过滤,您可以执行 begins with parentname_childname_
。如果你只想要一个parent的children,你可以做where PK = child and OK begins_with parentname_
。如果你确切地知道你想要什么child,而不是使用begins_with
,你可以简单地做where PK = child and OK = parentname_childname
。等等......(我使用 SQL-like 语法来简化 writing/reading 但它很容易转换为 dynamodb 查询语言)。
这个想法过于区分你的项目的实际属性和你如何检索你的项目。如果可以帮助您轻松找到您的物品,您可以创建完全伪造的钥匙!
您可能根本不需要区分 PK 中的 child“级别”,您也可以只使用通用值来获得类似的结果。此方法的主要 objective 是在特定分区键中使用 begins_with
的顺序键。
此方法的主要问题是,一旦您拥有 /parents
示例中的所有 parents/children/subchildren,您将不得不完全用代码重建树。但就dynamodb而言,这可能是最优化的解决方案。
B:使用过滤器更简单,但(更)糟糕 cost/performance
将每个 parent 存储在一个大文档中,然后使用像 where item.children[].subchildren[].name = parentname_childname_subchildname
这样的过滤器扫描您的 table。它效率不高并且成本更高,因为过滤器应用于查询结果,因此您首先检索所有内容然后仅过滤您想要的内容,因此您首先支付所有项目然后删除那些您不想要的.它有效,但不是很好,特别是如果你想要 100000 件中的 5 件。
主要优点是,对于 /parents
调用,您可以获得所描述的整个层次结构。但对于子路径 /parents/x/children/...
,您需要重新格式化代码中的 object 或仅查询投影表达式中的子属性。
C: 使用本地二级索引
有点像解决方案 A,您在其中单独存储项目,所有项目具有相同的 PK,但具有具有 3 个不同顺序键的本地二级索引:name_parent
、name_child
、name_subchild
.然后您可以根据需要分别查询每个索引。对于此用例,使用 LSI 比 GSI 更适合成本优化,但您可以自由地使用 GSI 实现相同的想法。它也可以正常工作(关于重新格式化 parent 元素以包含 children 元素的相同限制)。