代码之家  ›  专栏  ›  技术社区  ›  Journeyman

如何使用复合条件查询DynamoDB GSI

  •  0
  • Journeyman  · 技术社区  · 5 年前

    我有一张名为“frank”的DynamoDB表,只有一个GSI。分区密钥称为PK,排序密钥称为SK,GSI分区密钥名为GSI1_PK,GSI排序密钥名为GSM1_SK。我有一个存储实际数据的“数据”映射。

    填充了一些测试数据,看起来是这样的: enter image description here

    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不支持“OR”。那么,我如何使用AWS在Lambda中复制我更复杂的partiql查询呢。DynamoDB。DocumentClient?

    0 回复  |  直到 5 年前
        1
  •  1
  •   peter    5 年前

    如果你看一下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将使用分区键和 筛选表达式 以应用条件的其余部分。

    你可以用这种方式复制它,根据分区的大小,这可能会很好地工作。然而,如果分区将增长到1MB以上,并且大部分数据将被过滤掉,那么即使没有得到任何数据,也需要处理分页。

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

        2
  •  0
  •   Seth Geoghegan    5 年前

    不幸的是,DynamoDB不支持 KeyConditionExpression 。您正在执行的partiql查询可能正在执行完整的表扫描以返回结果。

    如果要使用DocumentClient复制partiql查询,可以使用 scan 活动如果您想避免使用 扫描 ,你可以表演两个单独的 query 操作,并将结果连接到应用程序代码中。