DynamoDBMapper Java AWS SDK中的分页功能

36

从API文档可以看出,DynamoDB支持对扫描和查询操作进行分页。关键在于将当前请求的ExclusiveStartIndex设置为上一次请求的LastEvaluatedIndex的值,以获取下一组(逻辑页面)结果。

我正在尝试实现同样的功能,但我正在使用DynamoDBMapper,它似乎有更多优点,比如与数据模型更紧密的耦合。所以,如果我想要做上述操作,我认为我会像下面这样做:

// Mapping of hashkey of the last item in previous query operation
Map<String, AttributeValue> lastHashKey = .. 
DynamoDBQueryExpression expression = new DynamoDBQueryExpression();

...
expression.setExclusiveStartKey();
List<Table> nextPageResults = mapper.query(Table.class, expression);

我希望我的关于使用DynamoDBMapper进行分页的理解是正确的。其次,我如何知道已经到达了结果的末尾。根据文档,如果我使用以下API:

QueryResult result = dynamoDBClient.query((QueryRequest) request);
boolean isEndOfResults = StringUtils.isEmpty(result.getLastEvaluatedKey());

回到使用DynamoDBMapper,我该如何知道在这种情况下是否已经到达了结果的末尾。

2个回答

64
您可以通过DynamoDBMapper的不同方法来进行操作,具体取决于您希望如何操作。以下是几种选择: 您需要理解这些方法之间的区别,以及它们返回的对象所封装的功能。
我会介绍一下 PaginatedScanListScanResultPage,但这些方法/对象基本上相互镜像。 PaginatedScanList 的描述如下(强调是我的):

实现了 List 接口,并代表了 AWS DynamoDB 中扫描操作的结果。当用户执行需要其的操作时,分页结果将按需加载。例如,某些操作需要获取整个列表,例如 size(),但在可能的情况下,结果将被惰性地分页获取。

这表示结果在您迭代列表时被加载。当您完成第一页后,第二页将自动被获取,而无需显式发出另一个请求。懒加载结果是默认方法,但如果调用重载的方法并提供带有不同 DynamoDBMapperConfig.PaginationLoadingStrategyDynamoDBMapperConfig,则可以覆盖它。
这与 ScanResultPage 不同。您将获得一组结果,并且需要自己处理分页。
下面是一个快速的示例代码,演示如何使用这两种方法,我使用 DynamoDBLocal 中的包含 5
final DynamoDBMapper mapper = new DynamoDBMapper(client);

// Using 'PaginatedScanList'
final DynamoDBScanExpression paginatedScanListExpression = new DynamoDBScanExpression()
        .withLimit(limit);
final PaginatedScanList<MyClass> paginatedList = mapper.scan(MyClass.class, paginatedScanListExpression);
paginatedList.forEach(System.out::println);

System.out.println();
// using 'ScanResultPage'
final DynamoDBScanExpression scanPageExpression = new DynamoDBScanExpression()
        .withLimit(limit);
do {
    ScanResultPage<MyClass> scanPage = mapper.scanPage(MyClass.class, scanPageExpression);
    scanPage.getResults().forEach(System.out::println);
    System.out.println("LastEvaluatedKey=" + scanPage.getLastEvaluatedKey());
    scanPageExpression.setExclusiveStartKey(scanPage.getLastEvaluatedKey());

} while (scanPageExpression.getExclusiveStartKey() != null);

输出结果:

MyClass{hash=2}
MyClass{hash=1}
MyClass{hash=3}
MyClass{hash=0}
MyClass{hash=4}

MyClass{hash=2}
MyClass{hash=1}
LastEvaluatedKey={hash={N: 1,}}
MyClass{hash=3}
MyClass{hash=0}
LastEvaluatedKey={hash={N: 0,}}
MyClass{hash=4}
LastEvaluatedKey=null

如果在使用query时将limit设置为2,它仍然返回所有记录。为什么会这样呢? - Thiyagu
2
@user7 看起来它返回了所有记录,但实际上底层实现(PaginatedQueryList)正在为您处理分页。请查看 PaginatedQueryList 的文档。它的行为方式与我上面关于 scan 的帖子相同。基本上,如果您对其进行迭代,它最终会获取所有元素,因为它自动执行分页调用。 - mkobit
那么 withLimit 对它不适用吗? - Thiyagu
根据我所了解的,在执行扫描时,限制并不是指结果数量,而是指允许的吞吐量。 - James Parker
1
PaginatedQueryList会进行多次调用以获取完整的项目集。限制用于指定每个请求要获取的项目数量。 - intoTHEwild

0
    String tableName = "tableName";
    DynamoDB dynamoDB = new DynamoDB(client);
    Table table = dynamoDB.getTable(tableName);
    ScanSpec scanSpec = new ScanSpec();
    scanSpec.withMaxResultSize(pageSize);
    long itemCount = table.describe().getItemCount();
    int count = 0;
    ItemCollection<ScanOutcome> items = table.scan(scanSpec);
    
    Map<String, AttributeValue> lastEvaluatedKey = null;

    do {
        Iterator<Page<Item, ScanOutcome>> iterator = items.pages().iterator();
        while (iterator.hasNext()) {
            Page<Item, ScanOutcome> page = iterator.next();
            count += page.size();
            for (Item item : page) {
                // do something with the item
            }
        }

        ScanResult scanResult = items.getLastLowLevelResult().getScanResult();
        lastEvaluatedKey = scanResult.getLastEvaluatedKey();
        if (lastEvaluatedKey != null) {
            KeyAttribute keyAttribute = new KeyAttribute("id", 
            lastEvaluatedKey.get("id").getS());
            scanSpec.withExclusiveStartKey(keyAttribute);

            if (count < itemCount) {
                items = table.scan(scanSpec);
            }
        }
    } while (count < itemCount);

你的回答可以通过提供其他支持信息来改进。请[编辑]以添加更多细节,例如引用或文档,以便他人确认你的答案正确无误。你可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接