@cumulus/aws-client
Version:
Utilities for working with AWS
101 lines • 4.03 kB
JavaScript
"use strict";
const services_1 = require("./services");
/**
* Class to efficiently search all of the items in a DynamoDB table, without loading them all into
* memory at once. Handles paging.
*/
class DynamoDbSearchQueue {
constructor(params, searchType = 'scan') {
this.items = [];
this.params = params;
this.dynamodbDocClient = (0, services_1.dynamodbDocClient)();
this.searchType = searchType;
}
/**
* Drain all values from the searchQueue, and return to the user.
* Warning: This can be very memory intensive.
*
* @returns {Promise<Array>} array of search results.
*/
async empty() {
let result;
const results = [];
do {
result = await this.shift(); // eslint-disable-line no-await-in-loop
if (result) {
results.push(result);
}
} while (result);
return results;
}
/**
* View the next item in the queue
*
* This does not remove the object from the queue. When there are no more
* items in the queue, returns 'null'.
*
* @returns {Promise<Object>} an item from the DynamoDB table
*/
async peek() {
if (this.items.length === 0)
await this.fetchItems();
return this.items[0];
}
/**
* Remove the next item from the queue
*
* When there are no more items in the queue, returns 'null'.
*
* @returns {Promise<Object>} an item from the DynamoDB table
*/
async shift() {
if (this.items.length === 0)
await this.fetchItems();
return this.items.shift();
}
/**
* A DynamoDbSearchQueue instance stores the list of items to be returned in
* the `this.items` array. When that list is empty, the `fetchItems()` method
* is called to repopulate `this.items`. Typically, the new items are fetched
* using the `DynamoDBDocument.scan()` method.
*
* DynamoDB scans up to 1 MB of items at a time and then filters that 1 MB to
* look for matching items. If there are more items to be search beyond that
* 1 MB limit, the scan response will include a `LastEvaluatedKey` property.
* Future calls to `scan()` should set `ExclusiveStartKey` equal to that
* `LastEvaluatedKey`, so that DynamoDB knows where to resume the search.
*
* Because DynamoDB only applies filtering to 1 MB at a time, it's possible
* that a particular 1 MB chunk may not contain any items that match the
* filter. In that case, the response from `scan()` will contain an empty list
* of items and a `LastEvaluatedKey` indicating that there are still more
* items to be scanned.
*
* The goal of `fetchItems()` is to either add more items to `this.items` or,
* if the entire Dynamo table has been scanned, push `null` onto the list of
* items. It will continue to call `scan()` until one of those two conditions
* has been satisfied.
*
* Reference: https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/Class/DynamoDBDocument/
*
* @private
*/
async fetchItems() {
let response;
do {
if (this.searchType === 'scan') {
response = await this.dynamodbDocClient.scan(this.params); // eslint-disable-line no-await-in-loop, max-len
}
else if (this.searchType === 'query') {
response = await this.dynamodbDocClient.query(this.params); // eslint-disable-line no-await-in-loop, max-len
}
if (response?.LastEvaluatedKey)
this.params.ExclusiveStartKey = response.LastEvaluatedKey;
} while ((response?.Items || []).length === 0 && response?.LastEvaluatedKey);
this.items = (response?.Items || []);
if (!response?.LastEvaluatedKey)
this.items.push(null);
}
}
module.exports = DynamoDbSearchQueue;
//# sourceMappingURL=DynamoDbSearchQueue.js.map