DynamoDB:在全球二级索引

时间:2016-12-06 05:26:29

标签: pagination amazon amazon-dynamodb

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

  

Exclusive Start Key必须与表的键架构具有相同的大小   (服务:AmazonDynamoDBv2;状态代码:400;错误代码:   ValidationException;请求ID:   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;      
    }

3 个答案:

答案 0 :(得分:8)

我正在为那些试图为GSI查询构建exclusiveStartKey 手动的人写这个答案。专用启动键似乎由3个组件组成:

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

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

setLastEvaluatedKey(queryResult.getLastEvaluatedKey());

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

答案 1 :(得分: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;

}

答案 2 :(得分:1)

在考虑索引时,上一次评估的键由两部分组成。

  1. 表键
  2. 索引键

您只需从QueryResultPagelast Sysout上次评估的键(EvaluatedKeyMap)并获取模式。

在您的情况下,当您创建ExclusiveStartKey时,还要添加上次评估的“ userId”。 ExclusiveStartKey.put(“ userId”,上次评估的用户ID值);

例如。

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