DynamoDB如何查询每个用户的一个公共最新项目?

时间:2019-09-08 20:37:34

标签: amazon-dynamodb dynamodb-queries

这是我的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"
} 

1 个答案:

答案 0 :(得分:1)

您需要在其上引入第二个表和一个具有以下结构的GSI(全局二级索引):

  • userId <-表的分区键
  • category <-GSI的分区键
  • createdAt <-GSI的排序键
  • id

userId属性是一个唯一标识用户的值(IIUC实际上可以是您帖子中描述的表中的author字段)

category属性最初可能看起来有些奇怪:它包含几个硬编码值之一。目前,我只能考虑一个这样的值:"public_content_page"。尽管如此,即使将来不再出现任何新类别,也需要将此属性作为GSI的分区键(因此我们无法避免)。

createdAtid属性与您的帖子中描述的表中的属性相同。

要按所需顺序获取商品,您需要按以下方式查询GSI:

{ 
  "TableName": <your_table_name>,
  "IndexName": <your_GSI_name>
  "KeyConditionExpression": "category = :v1",
  "ExpressionAttributeValues": {":v1": {"S": "public_content_page"}}
   "ScanIndexForward": false,
}

因为该表的主键是userId,所以该表每位用户只能容纳一个项目。

因为该表中的所有项目都具有相同的category值,并且GSI的分区键为category属性,所以查询GSI就是查询该表中的整个项目集。

由于createdAt属性是GSI的排序键,因此该查询返回的结果将按时间顺序进行排序。

当然,您需要填充此表。基本上,每次您put() / update() / delete()到第一个表(您的帖子中描述的那个)中的一项时,都需要在第二张表(我的答案中介绍的那张)。在该更新中,仅在新的update()值大于项目中的ConditionExpression值时,才需要使用createdAt来覆盖项目。

您需要记住,第二个表的createdAt极有可能无法执行(因为您的进程将在更新第一个表之后且在更新第二个表之前终止)。您可以执行定期扫描,以一定的定期计划从第一个表重建第二个表,也可以triggers)。

其他想法

这里显示的GSI只是保留了第一个表中项目的update()。因此,为了获取实际项目的内容,您需要获取查询的结果,并使用查询返回的id值在第一个表上执行多个get()。您可以使用BatchGetItem在一个请求中执行多个id操作。或者,您可以使用第二个表的不同结构:与其保留第一个表中的项get(),不如保留第一个表中的项id。这将使您摆脱其他content的麻烦。另一方面,这将使财务成本更高(第二个表现在将存储更多数据),并使第二个表的更新更加频繁(因为get()字段很可能是已编辑的字段通常,每次进行这样的修改都会导致对第二张表的更新)。

最后,您可以使用content在单个事务中更新两个表,而不必在第二个表上执行update()(这也需要进行lambda触发器/定期扫描)。不过,您仍然需要在TransactWriteItems属性上使用条件。