如何使用 DynamoDB 扫描对筛选结果进行分页?

问题描述 投票:0回答:1

我有一个 DynamoDB 表,其中包含 PK id 和属性状态(布尔值)和类型(具有两个值的字符串)。我需要对状态和类型的结果过滤实现分页。我当前的方法使用 FilterExpression 进行扫描,并将 Limit 设置为 2 *desiredResultSize。我累积过滤结果直到达到desiredResultSize,然后根据最后过滤的项目生成一个nextToken供客户端在下一个请求中使用。 但是,我面临一些潜在的问题:

  1. 扫描+过滤可能找不到任何合格的项目。
  2. 不确定最后找到的合格项目是否确实是表中的最后一个。理想情况下,如果剩余表中没有合格的项目,我应该将 null 作为 nextToken 返回给客户。
  3. 会有很多扫描操作,可能会很慢。那时我的桌子大小约为 2k。
    这是我当前的实现
public PaginatedResult<List<ItemRecord>> getFilteredItems(String nextToken, int resultSize) {
    Map<String, AttributeValue> exclusiveStartKey = decodeNextToken(nextToken);

    Expression filterExpression = Expression.builder()
            .expression("status = :status AND type IN (:type1, :type2)")
            .expressionValues(Collections.unmodifiableMap(new HashMap<String, AttributeValue>() {{
                put(":status", AttributeValue.fromBool(true));
                put(":type1", AttributeValue.fromString("typeValue1"));
                put(":type2", AttributeValue.fromString("typeValue2"));
            }}))
            .build();

    ScanEnhancedRequest.Builder scanEnhancedRequestBuilder = ScanEnhancedRequest.builder()
            .filterExpression(filterExpression)
            .limit(resultSize);

    // Accumulating results until desired resultSize is reached
    // Create nextToken based on the last item in filteredItemsList
    // Return the lastEvaluatedKey of the last found qualified item as nextToken
    List<ItemRecord> filteredItemsList = new ArrayList<>();
    Map<String, AttributeValue> currentExclusiveStartKey = exclusiveStartKey;
    boolean findAllQualifiedItems = false;
    while (!findAllQualifiedItems && filteredItemsList.size() < resultSize) {
        try {
            ScanEnhancedRequest scanEnhancedRequest = scanEnhancedRequestBuilder
                    .exclusiveStartKey(currentExclusiveStartKey).build();
            Page<ItemRecord> itemRecordsList = table.scan(scanEnhancedRequest)
                    .stream().findFirst().orElse(null);

            if (itemRecordsList.items().isEmpty()) {
                break;
            }

            for (ItemRecord itemRecord : itemRecordsList.items()) {
                if (filteredItemsList.size() < resultSize) {
                    filteredItemsList.add(itemRecord);
                }
            }

            currentExclusiveStartKey = itemRecordsList.lastEvaluatedKey();
            if (currentExclusiveStartKey == null) {
                // We have found all qualified items in the table
                findAllQualifiedItems = true;
            }
        } catch (Exception e) {
            // handle it
        }
    }

    if (filteredItemsList.isEmpty()) {
        if (nextToken == null) {
            throw new NotFoundException("Qualified items not found");
        }
        return new PaginatedResult<>(filteredItemsList, Optional.ofNullable(null), resultSize);
    }

    Map<String, AttributeValue> lastEvaluatedKey;
    if (findAllQualifiedItems) {
        lastEvaluatedKey = null;
    } else {
        lastEvaluatedKey = ConversionHelper.itemToAttributeValueMap(
                filteredItemsList.get(filteredItemsList.size() - 1));
    }

    String newNextToken = ConversionHelper.encodeNextToken(lastEvaluatedKey);
    return new PaginatedResult<>(filteredItemsList, Optional.ofNullable(newNextToken), resultSize);
}

感谢您的任何建议!

java pagination amazon-dynamodb nosql dynamodb-queries
1个回答
0
投票

您的应用程序应确保您获得足够的项目来返回给用户,您返回的最后一个项目将是 LastEvaluatedKey。

使用扫描时,您永远不会知道是否有更多物品可供提取。

我建议更改您的数据模型以使用索引和查询而不是扫描,它更具可预测性,更不用说更高的性能和成本效益。

© www.soinside.com 2019 - 2024. All rights reserved.