DynamoDB:当您知道分区键但不知道排序键时如何使用 GSI 进行查询?

DynamoDB: How to query using GSI when you know the partition key but not the sort key?

我的table是这样设置的

 PK  |  SK  | CreatedAt (DateTime)(Secondary Global Index) | Random Attributes
USER | 0xFF | Today                                        | 1
USER | 0xFE | Yesterday                                    | 2

所以我的分区键是对象的通用组名。 “USER”只是告诉我它是一个用户对象。稍后我将有“ITEM”或其他随机对象,或对象之间的链接以供快速查询。排序键是随机生成的用户的实际 ID (uuid)。辅助全局索引是“CreatedAt”,它是一个 DateTime 对象。

我只想找到最新的用户(我只关心SK)

我试过了;

QueryRequest qr = new QueryRequest
{
    TableName = "mytable",
    IndexName = "CreatedAt-index",
    ScanIndexForward = true,
    KeyConditionExpression = "PK = :v_pk",
    FilterExpression = "CreatedAt BETWEEN :v_timeOldest AND :v_timeNow",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":v_pk", new AttributeValue{S = "USER" } },
        {":v_timeNow", new AttributeValue{S = DateTime.Now.ToString(AWSSDKUtils.ISO8601DateFormat)} },
        {":v_timeOldest", new AttributeValue{S = DateTime.Now.AddYears(-1).ToString(AWSSDKUtils.ISO8601DateFormat)} }
    },
    Limit = 1
};

QueryResponse res = await _client.QueryAsync(qr);

错误是;

Query key condition not supported

我认为这是因为它希望 CreatedAt 成为分区键,并且它必须是唯一的,因此不能进行 BETWEEN 比较。但这对这种情况没有用,因为许多对象的日期重复,而且它可能介于其他日期之间。

我尝试了另一种方式,其中 KeyCondition 是 CreatedAt 语句,FilterExpression 是 PK,但同样的错误。

我期望的是只收集分区键为“USER”的所有对象,然后根据 GSI 分区键对 ascending/descending 进行排序,然后选择列表顶部的对象。

我在这里错过了什么?我对 GSI 的理解在概念上有缺陷吗?

来自 KeyConditionExpression

的文档

You must specify the partition key name and value as an equality condition. You can optionally provide a second condition for the sort key (if present).

在此特定查询中,您尝试查询 CreatedAt-index 索引并在 KeyConditionExpression 中指定 PK。但是,您的索引使用 CreatedAt 作为主键,而不是名为 PK 的属性。你说

I tried it the other way where the KeyCondition is the CreatedAt statement and the FilterExpression is the PK but same error.

你没有显示查询,但听起来你这样做了

    ...
    KeyConditionExpression = "CreatedAt BETWEEN :v_timeOldest AND :v_timeNow",
    FilterExpression = "PK = :v_pk",
    ...

这将不起作用,因为(从文档中)您必须将分区键名称和值指定为 equality 条件。换句话说,您不能对分区键进行范围查询。

您没有说明您是如何定义二级索引的,但听起来您使用 createdAt 定义了一个分区键,索引上没有排序键。从逻辑上讲,这看起来像这样:

这对您帮助不大,因为您无法对分区键执行范围查询。

但是,如果您要定义分区键为 USER 且排序键为 createdAt 的二级索引。该索引看起来像这样

该索引将允许您按照您的描述执行查询操作。

顺便说一句,您描述的基础 table 听起来不是很有用。您将所有用户存储在一个分区中,并使用 UUID 作为排序键。 UUID 的排序顺序听起来对您的应用程序不是特别有用。您可能需要为您的用户 ID 考虑 KSUID。 KSUID 具有在创建时唯一(如 UUID)and 或 table 的有用特性。当您需要唯一标识符 并且 需要按创建日期对它们进行排序时,这特别有用。