【问题标题】:How to query DynamoDB GSI with compound conditions如何使用复合条件查询 DynamoDB GSI
【发布时间】:2021-06-18 14:32:51
【问题描述】:

我有一个名为“frank”的 DynamoDB 表,其中包含一个 GSI。分区键称为 PK,排序键称为 SK,GSI 分区键称为 GSI1_PK,GSI 排序键称为 GSI1_SK。我有一个存储实际数据的“数据”地图。

填充了一些测试数据,它看起来像这样:

GSI 分区键和排序键直接映射到表中同名的属性。

我可以运行 partiql 查询来获取图像中显示的结果。这是partiql代码:

select PK, SK, GSI1_PK, GSI1_SK, data from "frank"."GSI1"
where 
("GSI1_PK"='tesla')
and 
(
 (  "GSI1_SK" >= 'A_VISITOR#2021-06-01-00-00-00-000' and  "GSI1_SK" <= 'A_VISITOR#2021-06-20-23-59-59-999' )
 or
 (  "GSI1_SK" >= 'B_INTERACTION#2021-06-01-00-00-00-000'  and   "GSI1_SK" <= 'B_INTERACTION#2021-06-20-23-59-59-999' )
)

注意 partiql 代码如何多次引用“GSI1_SK”。 partiql 查询有效,并返回图像中显示的数据。到目前为止一切都很好。

但是,我现在想将其移至 Lambda 函数中。如何构建 AWS.DynamoDB.DocumentClient 查询以完全执行此 partiql 查询正在执行的操作?

我可以让它在我的 Lambda 函数中工作:

const visitorStart="A_VISITOR#2021-06-01-00-00-00-000";
        const visitorEnd="A_VISITOR#2021-06-20-23-59-59-999";
        
        var params = {
          TableName: "frank",
          IndexName: "GSI1",
          KeyConditionExpression: "#GSI1_PK=:tmn AND #GSI1_SK BETWEEN :visitorStart AND :visitorEnd",
          ExpressionAttributeNames :{  "#GSI1_PK":"GSI1_PK", "#GSI1_SK":"GSI1_SK" },
          ExpressionAttributeValues: {
            ":tmn": lowerCaseTeamName,
            ":visitorStart": visitorStart,
            ":visitorEnd": visitorEnd
          }
        };
        
        const data = await documentClient.query(params).promise();
        console.log(data); 

但一旦我尝试更复杂的复合条件,我就会收到此错误:

ValidationException: Invalid operator used in KeyConditionExpression: OR

这是更复杂的尝试:

const visitorStart="A_VISITOR#2021-06-01-00-00-00-000";
        const visitorEnd="A_VISITOR#2021-06-20-23-59-59-999";
        const interactionStart="B_INTERACTION#2021-06-01-00-00-00-000";
        const interactionEnd="B_INTERACTION#2021-06-20-23-59-59-999";
        
        var params = {
          TableName: "frank",
          IndexName: "GSI1",
          KeyConditionExpression: "#GSI1_PK=:tmn AND (#GSI1_SK BETWEEN :visitorStart AND :visitorEnd OR #GSI1_SK BETWEEN :interactionStart AND :interactionEnd) ",
          ExpressionAttributeNames :{  "#GSI1_PK":"GSI1_PK", "#GSI1_SK":"GSI1_SK" },
          ExpressionAttributeValues: {
            ":tmn": lowerCaseTeamName,
            ":visitorStart": visitorStart,
            ":visitorEnd": visitorEnd,
            ":interactionStart": interactionStart,
            ":interactionEnd": interactionEnd
          }
        };
        
        const data = await documentClient.query(params).promise();
        console.log(data);  

文档说 KeyConditionExpressions 不支持“或”。那么,如何使用 AWS.DynamoDB.DocumentClient 在 Lambda 中复制更复杂的 partiql 查询?

【问题讨论】:

    标签: amazon-web-services aws-lambda amazon-dynamodb


    【解决方案1】:

    如果您查看 PartiQL for DynamoDB 的文档,他们会警告您,PartiQL 毫不犹豫地使用全表扫描来获取您的数据:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html#ql-reference.select.syntax

    为确保 SELECT 语句不会导致全表扫描,WHERE 子句条件必须指定分区键。使用相等或 IN 运算符。

    在这些情况下,PartiQL 将运行扫描并使用FilterExpression 过滤掉数据。

    当然,在您的示例中,您提供了一个分区键,因此我假设 PartiQL 将使用分区键和 FilterExpression 运行查询以应用其余条件。

    您可以通过这种方式复制它,并且根据您的分区大小,这可能工作得很好。但是,如果分区将超过 1MB,并且大部分数据将被过滤掉,那么即使您不会获得任何数据,您也需要处理分页。

    因此,我建议您简单地将其拆分并将每个或条件作为单独的查询运行,然后合并客户端上的数据。

    【讨论】:

    • 感谢@peter - 它可以让 DynamoDB 准确地告诉您它将如何执行查询(或它是如何执行查询的)。例如SQL Server 可以为您提供一个执行配置文件,告诉您在执行查询时它会做什么?
    • 是的,我也在考虑这个问题,但我找不到任何相关信息,这就是为什么这些只是假设,基于文档中作为警告写的内容。
    【解决方案2】:

    很遗憾,DynamoDB 不支持KeyConditionExpression 中的多个布尔运算。您正在执行的 partiql 查询可能正在执行全表扫描以返回结果。

    如果您想使用 DocumentClient 复制 partiql 查询,您可以使用 scan 操作。如果您想避免使用scan,您可以执行两个单独的query 操作并将结果加入您的应用程序代码中。

    【讨论】:

    • 感谢 Seth - 我正在努力遵循 Rick Houlihan 倡导的单表模型,在同一个表中使用多个实体和一个查询,然后能够提供所需的内容。运行两个查询会使所有这些都变得多余——我也可以只使用两个表并避免所有复合键的复杂性。令人讨厌的是 DynamoDB 有这些小弱点,让他们承诺的一切都变得如此难以捉摸。
    • NoSQL 数据建模肯定很棘手。我听 Rick 说过“NoSQL 不容易,它很高效”。看起来您在数据建模方面做得很好。您已经在同一个分区中获得了所需的数据。现在您需要设计排序键以允许您获取所需的内容。我假设 A_VISITOR 和 B_INTERACTION 支持不同的访问模式,也许您可​​以将其移至不同的 GSI。这是一个迭代过程,真正迫使您跳出框框思考。继续!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多