DynamoDB 如何查询每个用户的一个 public 最新项目?
DynamoDB how to query by one public latest item by each user?
这是我的 DynamoDB 数据结构。
---------------------------------------------------------------------
id | author | status | content | createdAt
---------------------------------------------------------------------
id1 | user1 | PRIVATE | pcon | 2019-09-09T17:54:09.843Z
id1 | user1 | PUBLIC | hello | 2019-09-08T17:54:09.843Z
id2 | user2 | PUBLIC | world | 2019-09-07T17:54:09.843Z
id1 | user1 | PUBLIC | hello1 | 2019-09-07T17:54:09.843Z
---------------------------------------------------------------------
如何使用 DynamoDB 从每个用户查询一个最新的 PUBLIC
内容?
预期查询结果:
items[
{
id: id1,
author: user1,
status: PUBLIC,
content: hello,
createdAt: 2019-09-08T17:54:09.843Z
},
{
id: id2,
author: user2,
status: PUBLIC,
content: world,
createdAt: 2019-09-07T17:54:09.843Z
},
]
我能够使用以下代码获取所有 PUBLIC
项,但找不到从中获取最新项的方法。(放大自定义解析器映射模板)
{
"version": "2017-02-28",
"operation": "Query",
"query": {
"expression": "#privacy = :privacy",
"expressionNames": {
"#privacy": "privacy"
},
"expressionValues": {
":privacy": {
"S": "PUBLIC"
}
}
},
"scanIndexForward": #if( $context.args.sortDirection == "ASC" ) true #else false #end,
"limit": $limit,
"nextToken": #if( $context.args.nextToken ) "$context.args.nextToken" #else null #end,
"index": "privacy"
}
你需要在上面引入第二个table和一个GSI(Global secondary index),结构如下:
userId
<-- table 的分区键
category
<-- GSI 的分区键
createdAt
<-- GSI 的排序键
id
userId
属性是一个唯一标识用户的值(IIUC 它实际上可能是 post 中描述的 table 中的 author
字段)
category
属性最初可能看起来有点奇怪:它包含几个硬编码值之一。目前我只能想到一个这样的值:"public_content_page"
。尽管如此,即使未来没有新的类别出现,也需要这个属性作为GSI的分区键(所以我们无法避免)。
createdAt
、id
属性与 post 中描述的 table 中的属性相同。
要按您希望的顺序获取项目,您需要按如下方式查询 GSI:
{
"TableName": <your_table_name>,
"IndexName": <your_GSI_name>
"KeyConditionExpression": "category = :v1",
"ExpressionAttributeValues": {":v1": {"S": "public_content_page"}}
"ScanIndexForward": false,
}
因为 table 的主键是 userId
这个 table 将只为每个用户保存一个项目。
因为 table 中的所有项都具有相同的 category
值并且 GSI 的分区键是 category
属性,所以查询 GSI 就是查询其中的整个项集table.
因为 createdAt
属性是 GSI 的排序键,所以此查询返回的结果将按时间顺序排序。
当然,您需要填充这个table。基本上,每次你 put()
/update()
/delete()
第一个 table 中的一个项目(你的 post 中描述的那个)你需要做第二个 table 中的 update()
(我在回答中介绍的那个)。在那次更新中,您需要使用 ConditionExpression
来确保仅当新的 createdAt
值大于该项目的 createdAt
值时才覆盖该项目。
您需要记住,第二个 table 的 update()
有可能不会执行(因为您的进程将在更新第一个 [=86= 后终止) ] 并且就在更新第二个之前)。您可以执行计划扫描,以某个定期计划从第一个 table 重建第二个 table,或者您可以 triggers).
其他想法
此处显示的第 GSI 仅包含第一个 table 项的 id
。因此,为了获得实际项目的内容,您需要获取查询结果并使用查询返回的 id
值在第一个 table 上执行多个 get()
。您可以使用 BatchGetItem 在一个请求中执行多个 get()
操作。或者,您可以使用第二个 table 的不同结构:它不会保存第一个 table 中某个项目的 id
,而是保存 content
值。这将使您摆脱额外的 get()
。另一方面,它会使财务成本更高(第二个 table 现在将存储更多数据)并且会使对第二个 table 的更新更频繁(因为 content
字段是可能是一个经常编辑的字段,每次这样的编辑都会更新到第二个 table)。
最后,您可以使用 TransactWriteItems
更新两个 tables 在单个事务中。不过,您仍然需要在 createdAt
属性上使用条件。
这是我的 DynamoDB 数据结构。
---------------------------------------------------------------------
id | author | status | content | createdAt
---------------------------------------------------------------------
id1 | user1 | PRIVATE | pcon | 2019-09-09T17:54:09.843Z
id1 | user1 | PUBLIC | hello | 2019-09-08T17:54:09.843Z
id2 | user2 | PUBLIC | world | 2019-09-07T17:54:09.843Z
id1 | user1 | PUBLIC | hello1 | 2019-09-07T17:54:09.843Z
---------------------------------------------------------------------
如何使用 DynamoDB 从每个用户查询一个最新的 PUBLIC
内容?
预期查询结果:
items[
{
id: id1,
author: user1,
status: PUBLIC,
content: hello,
createdAt: 2019-09-08T17:54:09.843Z
},
{
id: id2,
author: user2,
status: PUBLIC,
content: world,
createdAt: 2019-09-07T17:54:09.843Z
},
]
我能够使用以下代码获取所有 PUBLIC
项,但找不到从中获取最新项的方法。(放大自定义解析器映射模板)
{
"version": "2017-02-28",
"operation": "Query",
"query": {
"expression": "#privacy = :privacy",
"expressionNames": {
"#privacy": "privacy"
},
"expressionValues": {
":privacy": {
"S": "PUBLIC"
}
}
},
"scanIndexForward": #if( $context.args.sortDirection == "ASC" ) true #else false #end,
"limit": $limit,
"nextToken": #if( $context.args.nextToken ) "$context.args.nextToken" #else null #end,
"index": "privacy"
}
你需要在上面引入第二个table和一个GSI(Global secondary index),结构如下:
userId
<-- table 的分区键category
<-- GSI 的分区键createdAt
<-- GSI 的排序键id
userId
属性是一个唯一标识用户的值(IIUC 它实际上可能是 post 中描述的 table 中的 author
字段)
category
属性最初可能看起来有点奇怪:它包含几个硬编码值之一。目前我只能想到一个这样的值:"public_content_page"
。尽管如此,即使未来没有新的类别出现,也需要这个属性作为GSI的分区键(所以我们无法避免)。
createdAt
、id
属性与 post 中描述的 table 中的属性相同。
要按您希望的顺序获取项目,您需要按如下方式查询 GSI:
{
"TableName": <your_table_name>,
"IndexName": <your_GSI_name>
"KeyConditionExpression": "category = :v1",
"ExpressionAttributeValues": {":v1": {"S": "public_content_page"}}
"ScanIndexForward": false,
}
因为 table 的主键是 userId
这个 table 将只为每个用户保存一个项目。
因为 table 中的所有项都具有相同的 category
值并且 GSI 的分区键是 category
属性,所以查询 GSI 就是查询其中的整个项集table.
因为 createdAt
属性是 GSI 的排序键,所以此查询返回的结果将按时间顺序排序。
当然,您需要填充这个table。基本上,每次你 put()
/update()
/delete()
第一个 table 中的一个项目(你的 post 中描述的那个)你需要做第二个 table 中的 update()
(我在回答中介绍的那个)。在那次更新中,您需要使用 ConditionExpression
来确保仅当新的 createdAt
值大于该项目的 createdAt
值时才覆盖该项目。
您需要记住,第二个 table 的 update()
有可能不会执行(因为您的进程将在更新第一个 [=86= 后终止) ] 并且就在更新第二个之前)。您可以执行计划扫描,以某个定期计划从第一个 table 重建第二个 table,或者您可以 triggers).
其他想法
此处显示的第 GSI 仅包含第一个 table 项的 id
。因此,为了获得实际项目的内容,您需要获取查询结果并使用查询返回的 id
值在第一个 table 上执行多个 get()
。您可以使用 BatchGetItem 在一个请求中执行多个 get()
操作。或者,您可以使用第二个 table 的不同结构:它不会保存第一个 table 中某个项目的 id
,而是保存 content
值。这将使您摆脱额外的 get()
。另一方面,它会使财务成本更高(第二个 table 现在将存储更多数据)并且会使对第二个 table 的更新更频繁(因为 content
字段是可能是一个经常编辑的字段,每次这样的编辑都会更新到第二个 table)。
最后,您可以使用 TransactWriteItems
更新两个 tables 在单个事务中。不过,您仍然需要在 createdAt
属性上使用条件。