Skip to content

Instantly share code, notes, and snippets.

@danibrear
Last active April 7, 2021 21:49
Show Gist options
  • Save danibrear/364b05da796a3c9f8ed956d10ebf3bf9 to your computer and use it in GitHub Desktop.
Save danibrear/364b05da796a3c9f8ed956d10ebf3bf9 to your computer and use it in GitHub Desktop.
This is an example migration for adding a status attribute to a hypothetical Todo table.
/* eslint-disable @typescript-eslint/no-var-requires */
/**
* How to run:
* - export AWS_PROFILE
* - export AWS_REGION=us-east-1
*/
const DynamoDB = require("aws-sdk/clients/dynamodb");
const dynamodb = new DynamoDB();
const DocumentClient = new DynamoDB.DocumentClient();
const IS_DEV = true;
// CHANGE THIS TO THE CORRECT TABLE NAME
const TABLE_PREFIX = "Todo";
const PROD_EXT = "prod";
const DEV_EXT = "develop";
/**
* Gets the table name for the environment specified above. This makes it
* so we dont have to hard-code the amplify random id added to the table name.
* @returns a string of the table name or null
*/
const getTable = async () => {
const tables = await dynamodb.listTables().promise();
if (tables.TableNames) {
const postfix = IS_DEV ? DEV_EXT : PROD_EXT;
const filtered = tables.TableNames.filter(
(tn) => tn.startsWith(`${TABLE_PREFIX}-`) && tn.endsWith(`-${postfix}`),
);
if (filtered && filtered.length > 0) {
return filtered[0];
}
return null;
} else {
return null;
}
};
// THIS IS WHERE THE PROCESSING HAPPENS
const processItems = async (tableName, records) => {
try {
return await Promise.allSettled(
records.map(async (todo) => {
// Put processing logic here...
await DocumentClient.update({
TableName: tableName,
Key: { id: todo.id },
UpdateExpression: "set status = :status",
ExpressionAttributeValues: {
":status": "complete",
},
ReturnValues: "UPDATED_NEW",
}).promise();
console.log("Updated todo", todo.id);
}),
);
} catch (e) {
console.log("[ERROR] error updating record:", e);
return [];
}
};
const main = async () => {
const tableName = await getTable();
console.log("[INFO] changing", tableName);
let nextToken = null;
let changed = 0;
do {
const results = await DocumentClient.scan({
TableName: tableName,
Limit: 500,
ExclusiveStartKey: nextToken,
}).promise();
if (results.Items) {
changed += (await processItems(tableName, results.Items)).length;
console.log("Done with", changed, "items");
} else {
break;
}
if (results.LastEvaluatedKey) {
nextToken = results.LastEvaluatedKey;
} else {
break;
}
} while (nextToken);
console.log("UPDATED", changed, "records.");
};
main();
@MiladNazeri
Copy link

Does nextToken ever get set to null if there is more than 500 items?

@danibrear
Copy link
Author

Does nextToken ever get set to null if there is more than 500 items?

Yep. If there are more than 500 records, nextToken will get set to the last evaluated key. If there are less than 500 records, when nextToken is set to null on line 70 it will never get overridden Therefore it will run on the less than 500 items and the do...while loop will exit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment