使用Node.js在DynamoDB中进行分页?

时间:2017-03-13 13:56:48

标签: node.js amazon-web-services pagination amazon-dynamodb

我已阅读AWS关于分页的文档:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html#ScanQueryLimit

正如他们的文档所述:

  

在响应中,DynamoDB返回Limit值范围内的所有匹配结果。例如,如果发出限制值为6且没有过滤器表达式的查询或扫描请求,DynamoDB将返回表中与请求中指定的键条件匹配的前六项(或仅返回前六项)没有过滤器的扫描案例

这意味着,鉴于我有一个名为Questions的表,其中包含一个名为difficulty的属性(可以采用02之间的任何数值),我可能会结束以下难题:

  • 客户提出请求,请考虑GET /questions?difficulty=0&limit=3
  • 我将3转发给DynamoDB查询,该查询可能会返回0项,因为集合中的前3个可能不是difficulty == 0
  • 然后我必须执行一个新查询以获取符合该条件的更多questions,而不知道我可能会返回重复项

如何根据查询正确分页?我得到的结果与我要求的结果一样,同时具有正确的偏移量

9 个答案:

答案 0 :(得分:5)

以下是如何迭代分页结果集的示例 Node.js中的DynamoDB scan(也可以很容易地适应query)。

您可以保存LastEvaluatedKey状态服务器端并将标识符传递回客户端,它将通过下一个请求发送,并且您的服务器会在下一个请求中将该值传递为ExclusiveStartKey到DynamoDB

const AWS = require('aws-sdk');
AWS.config.logger = console;

const dynamodb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });

let val = 'some value';

let params = {
  TableName: "MyTable",
  ExpressionAttributeValues: {
    ':val': {
      S: val,
    },
  },
  Limit: 1000,
  FilterExpression: 'MyAttribute = :val',
  // ExclusiveStartKey: thisUsersScans[someRequestParamScanID]
};

dynamodb.scan(scanParams, function scanUntilDone(err, data) {
  if (err) {
    console.log(err, err.stack);
  } else {
    // do something with data

    if (data.LastEvaluatedKey) {
      params.ExclusiveStartKey = data.LastEvaluatedKey;

      dynamodb.scan(params, scanUntilDone);
    } else {
      // all results scanned. done!
      someCallback();
    }
  }
});

答案 1 :(得分:4)

避免使用递归来防止调用堆栈溢出。扩展@Roshan Khandelwal的方法的迭代解决方案:

const getAllData = async (params) => {
  const _getAllData = async (params, startKey) => {
    if (startKey) {
      params.ExclusiveStartKey = startKey
    }
    return this.documentClient.query(params).promise()
  }
  let lastEvaluatedKey = null
  let rows = []
  do {
    const result = await _getAllData(params, lastEvaluatedKey)
    rows = rows.concat(result.Items)
    lastEvaluatedKey = result.LastEvaluatedKey
  } while (lastEvaluatedKey)
  return rows
}

答案 2 :(得分:3)

查询和扫描操作会在其回复中返回<style> <?php include 'CSS/style.css'; ?> </style> 。如果没有并发插入,只要您重复调用Query / Scan并将ExclusiveStartKey设置为上一次调用的LastEvaluatedKey,您就不会错过项目,也不会多次遇到项目。

答案 3 :(得分:1)

使用异步/等待。

const getAllData = async (params) => { 

    console.log("Querying Table");
    let data = await docClient.query(params).promise();

    if(data['Items'].length > 0) {
        allData = [...allData, ...data['Items']];
    }

    if (data.LastEvaluatedKey) {
        params.ExclusiveStartKey = data.LastEvaluatedKey;
        return await getAllData(params);

    } else {
        return data;
    }
}

我正在使用全局变量 allData 来收集所有数据。

调用此函数包含在try-catch中

try {

        await getAllData(params);
        console.log("Processing Completed");

        // console.log(allData);

    } catch(error) {
        console.log(error);
    }

我正在 Lambda 中使用它,并且效果很好。

文章here确实为我提供了指导。谢谢。

答案 4 :(得分:1)

我希望你明白了。所以以防万一其他人可能会发现它有用。 AWS 的 QueryPaginator/ScanPaginator 如下所示:

const paginator = new QueryPaginator(dynamoDb, queryInput);

for await (const page of paginator) {
    // do something with the first page of results
    break
}

https://github.com/awslabs/dynamodb-data-mapper-js/tree/master/packages/dynamodb-query-iterator

查看更多详情

答案 5 :(得分:0)

您可以按难度创建索引索引,并在查询集KeyConditionExpression上设置难度为0。

var params = {
    TableName: questions,
    IndexName: 'difficulty-index',
    KeyConditionExpression: 'difficulty = :difficulty ',
    ExpressionAttributeValues: {':difficulty':0}
}

答案 6 :(得分:0)

用于在dynamodb扫描中创建分页

var params = {  
        "TableName"                 : "abcd",
        "FilterExpression"          : "#someexperssion=:someexperssion",
        "ExpressionAttributeNames"  : {"#someexperssion":"someexperssion"},
        "ExpressionAttributeValues" : {":someexperssion" : "value"},
        "Limit"                     : 20,
        "ExclusiveStartKey"         : {"id": "9ee10f6e-ce6d-4820-9fcd-cabb0d93e8da"}
    };
DB.scan(params).promise();

此查询上次执行时间返回的ExclusiveStartKey是LastEvaluatedKey

答案 7 :(得分:0)

使用异步/等待,在等待中返回数据。 详细阐述@Roshan Khandelwal的答案。

const getAllData = async (params, allData = []) => {
  const data = await dynamodbDocClient.scan(params).promise()

  if (data['Items'].length > 0) {
    allData = [...allData, ...data['Items']]
  }

  if (data.LastEvaluatedKey) {
    params.ExclusiveStartKey = data.LastEvaluatedKey
    return await getAllData(params, allData)
  } else {
    return allData
  }
}

在try / catch中调用:

try {
        const data = await getAllData(params);
        console.log("my data: ", data);
    } catch(error) {
        console.log(error);
    }

答案 8 :(得分:0)

您还可以使用递推代替全局变量来实现此目的,例如:

<button onclick="add()">click</button><br>
<div id="values"></div>

然后您可以简单地将其命名为:

const getAllData = async (params, allData = []) => {
    let data = await db.scan(params).promise();
    return (data.LastEvaluatedKey) ?
      getAllData({...params, ExclusiveStartKey: data.LastEvaluatedKey}, [...allData, ...data['Items']]) :
      [...allData, ...data['Items']];
};