【问题标题】:DynamoDB : Pagination with "withExclusiveStartKey" on a Global Secondary IndexDynamoDB:在全局二级索引上使用“withExclusiveStartKey”进行分页
【发布时间】:2017-04-20 16:20:33
【问题描述】:

我有一个名为“product”的 DynamoDB 表,在“userId”上有一个全局二级索引。主键在“id”上。 我正在尝试在“userID”GSI 上使用“withExclusiveStartKey”实现分页查询。 但是,当我传递一个有效的 lastId 时,我得到以下异常:

独占开始键的大小必须与表的键模式相同 (服务:AmazonDynamoDBv2;状态代码:400;错误代码: 验证异常;请求编号: 822db97e-04a3-4c36-8c72-6008e2693679)

我在这里做错了什么?

public QueryResultPage<Product>  findPaged(String userId,int limit,String lastId) {
        DynamoDBMapper mapper = new DynamoDBMapper(dynamoDb);       
        Map<String, AttributeValue> vals = new HashMap<>();
        vals.put(":valUserId", new AttributeValue().withS(userId));
                DynamoDBQueryExpression<Product> queryExp = new         DynamoDBQueryExpression<Product>()
                .withKeyConditionExpression("userId = :valUserId")
                .withIndexName(ModelConsts.TBL_PRODUCT_GSI_USERID)
                .withExpressionAttributeValues(vals)
                .withScanIndexForward(false)
                .withConsistentRead(false)
                .withLimit(limit);  
           if (lastId != null) {//paging
            Map<String, AttributeValue> exclusiveStartKey = new HashMap<String, AttributeValue>();
                    exclusiveStartKey.put("id", new AttributeValue().withS(lastId));
               queryExp = queryExp.withExclusiveStartKey(exclusiveStartKey);
        }   
        QueryResultPage<Product> result = mapper.queryPage(Product.class, queryExp);
        return result;      
    }

【问题讨论】:

    标签: pagination amazon amazon-dynamodb


    【解决方案1】:

    我正在为那些尝试为 GSI 查询构建exclusiveStartKey 手动的人写这个答案。看来独占开始键由3个部分组成:

    • GSI 哈希键,
    • GSI 范围键
    • 表格键

    这似乎没有记录在任何地方,因为您应该只使用返回的 lastEvaluatedKey 通过调用:

    setLastEvaluatedKey(queryResult.getLastEvaluatedKey());
    

    接受的答案是正确的,但它给读者留下的印象是密钥只有 2 个组件,这对我的情况没有帮助。 这里描述的解决方案最早是在this GitHub issue中提到的。

    以下是使用 AWS Java SDK v2 实现上述内容的方式:

    QueryRequest.Builder queryBuilder = QueryRequest.builder();
    Map<String, AttributeValue> startKey = new HashMap<>(3);
    // HASH/PARTITION KEY
    startKey.put("gsiHashKeyAttribute", AttributeValue.builder().s(gsiHashKey).build());
    // RANGE/SORT KEY
    startKey.put("gsiRangeKeyAttribute", AttributeValue.builder().s(gsiRangeKey).build());
    // TABLE PRIMARY KEY
    startKey.put("tablePrimaryKeyAttribute", AttributeValue.builder().s(tablePrimaryKey).build());
    queryBuilder.exclusiveStartKey(startKey);
    

    【讨论】:

    • 那么QuerySpec.withExclusiveStartKey 签名具有误导性,因为只有一个重载的会起作用。这是一个接受KeyAttributes
    • QueryResultPage 的默认页面大小是多少?
    • 这正是我所需要的,谢谢。您需要包含原始表主(分区键)的事实在任何地方都没有记录。
    【解决方案2】:

    GSI原表的所有键值都应设置为开始键。如果表有分区键和排序键,那么这两个键值都应该设置为起始键值。

    在下面的例子中:-

    1) videos 表以videoid 作为分区键,category 作为排序键

    2) GSI 定义为category 作为分区键,videoid 作为排序键

    以下代码通过 category 值查询 GSI,并设置了起始键(即分区键和排序键)。

    当我不填充分区或排序键时,我可以重现您的错误。

    示例代码:-

    public QueryResultPage<VideoDynamoMappingAdapter> findVideosByCategoryUsingGSIAndMapperWithStartKey(
            String category) {
        DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
        QueryResultPage<VideoDynamoMappingAdapter> queryResult = null;
        Map<String, AttributeValue> vals = new HashMap<>();
        vals.put(":val1", new AttributeValue().withS(category));
        DynamoDBQueryExpression<VideoDynamoMappingAdapter> queryExp = new DynamoDBQueryExpression<VideoDynamoMappingAdapter>()
                .withKeyConditionExpression("category = :val1").withIndexName("VideoCategoryGsi")
                .withExpressionAttributeValues(vals).withScanIndexForward(false).withConsistentRead(false).withLimit(1);
    
        Map<String, AttributeValue> startKey = new HashMap<>();
    
        startKey.put("videoid", new AttributeValue().withS("2"));
        startKey.put("category", new AttributeValue().withS("Thriller"));
    
        queryExp.setExclusiveStartKey(startKey);
    
        queryResult = dynamoDBMapper.queryPage(VideoDynamoMappingAdapter.class, queryExp);
    
        System.out.println("Result size ===>" + queryResult.getResults().size());
        System.out.println("Last evaluated key ===>" + queryResult.getLastEvaluatedKey());
    
        for (VideoDynamoMappingAdapter videoDynamoMappingAdapter : queryResult.getResults()) {
            System.out.println("Video data ===>" + videoDynamoMappingAdapter.toString());
        }
    
        return queryResult;
    
    }
    

    【讨论】:

      【解决方案3】:

      在考虑索引时,最后评估的键由两部分组成。

      1. 表格键
      2. 索引键

      您可以简单地从 QueryResultPagelast Sysout 最后评估的键 (EvaluatedKeyMap) 并获取模式。

      在您的情况下,当您创建 ExclusiveStartKey 时,也添加最后评估的“userId”。 ExclusiveStartKey.put("userId", 最后评估的用户id属性值);

      例如

      exclusiveStartKey.put("id", new AttributeValue().withS(lastUserId));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-03
        • 2023-04-09
        • 1970-01-01
        • 2019-03-18
        相关资源
        最近更新 更多