Estoy trabajando en un caso de uso en el que una actualización de dynamoDB debería:
Empecé con un método de utilidad excelente de Daniel Barrel en https://stackoverflow.com/a/63511693/15369972 que proporciona un método de utilidad general para la actualización con valores dinámicos, pero sin el contador atómico.
Intenté agregar la capacidad del contador atómico agregando el contador y su incrementador en los objetos de parámetro después de que se cargan los valores dinámicos, pero obtengo un valor estático en el contador en la actualización en lugar de un valor que se incrementa en uno con cada llamada .
¿Dónde está esto yendo mal? Llamo a la función de actualización modificada con un nombre de tabla, un objeto javascript dinámico y una matriz que contiene el hash y la clave de ordenación:
await update(tableName, jsonObjectToStore, ['myHashKey', 'mySortKey'])
Y el método de actualización modificado que no se incrementa como me gustaría, es:
async function update (tableName, item, idAttributeNames) { var params = { TableName: tableName, Key: {}, ExpressionAttributeValues: {}, ExpressionAttributeNames: {}, UpdateExpression: "", ReturnValues: "UPDATED_NEW" }; for (const attname of idAttributeNames) { params["Key"][attname] = item[attname]; } let prefix = "set "; let attributes = Object.keys(item); for (let i=0; i<attributes.length; i++) { let attribute = attributes[i]; if (!idAttributeNames.includes(attribute)) { params["UpdateExpression"] += prefix + "#" + attribute + " = :" + attribute; params["ExpressionAttributeValues"][":" + attribute] = item[attribute]; params["ExpressionAttributeNames"]["#" + attribute] = attribute; prefix = ", "; } } // Add the counter params["UpdateExpression"] += ", #nImports = :nImports + :incr"; console.log(params["UpdateExpression"]) console.log(params["ExpressionAttributeValues"]) params["ExpressionAttributeValues"][":incr"] = 1; params["ExpressionAttributeValues"][":nImports"] = 0; console.log(params["ExpressionAttributeValues"]) console.log(params["ExpressionAttributeNames"]) params["ExpressionAttributeNames"]["#nImports"] = 'nImports' console.log(params["ExpressionAttributeNames"]) await docClient.update return await docClient.update(params).promise(); }
Trabajó con AWS Support para encontrar una solución razonable. Tampoco estaban seguros de cómo hacer un contador atómico usando el cliente de documentos ddb (a diferencia del cliente de bajo nivel que tiene muchos ejemplos documentados), pero sugirieron el comando ADD, que tiene el efecto secundario de una actualización atómica en un campo numérico.
Entonces, con el ejemplo a continuación, construimos nuestra actualización dinámica a partir del objeto que se almacenará, luego agregamos la declaración ADD en la expresión de actualización ( ¡sin una coma! ) y agregamos lo que es en efecto un incremento numérico a ExpressionAttributeValues para nImports. Como este, que debería ser un ejemplo completo de lambda que funcione. Hay algunas declaraciones de console.log para mostrar lo que está sucediendo:
const AWS = require('aws-sdk'); const docClient = new AWS.DynamoDB.DocumentClient(); async function update (tableName, item, idAttributeNames) { var params = { TableName: tableName, Key: {}, ExpressionAttributeValues: {}, ExpressionAttributeNames: {}, UpdateExpression: "", ReturnValues: "UPDATED_NEW" }; for (const attname of idAttributeNames) { params["Key"][attname] = item[attname]; } let prefix = "set "; let attributes = Object.keys(item); for (let i=0; i<attributes.length; i++) { let attribute = attributes[i]; if (!idAttributeNames.includes(attribute)) { params["UpdateExpression"] += prefix + "#" + attribute + " = :" + attribute; params["ExpressionAttributeValues"][":" + attribute] = item[attribute]; params["ExpressionAttributeNames"]["#" + attribute] = attribute; prefix = ", "; } } console.log('params before adding atomic counter is:', params) // Add the counter using the ADD syntax params["UpdateExpression"] += " ADD #nImports :nImports" params["ExpressionAttributeValues"][":nImports"] = 1; params["ExpressionAttributeNames"]["#nImports"] = 'nImports' console.log('params after adding atomic counter is:', params) try { const result = await docClient.update(params).promise(); console.log('after await, result is ', result); return result; } catch (err) { console.log('err is ', err) } }; exports.handler = async (event) => { const item = {title: 'sometitle', site_url: "www.amazon.com", key: "G"}; const body = await update('test_table', item, ['title', 'site_url']); const response = { statusCode: 200, body: JSON.stringify(body), }; return response; }
La amable gente de AWS investigó un poco más y también señaló un error en el código inicial que, cuando se corrige, debería incrementarse según lo deseado usando el operador SET.
Básicamente, el código original no apuntó correctamente a la variable para el incremento. Entonces, una versión corregida donde agregamos la variable incrementada debería ser:
console.log('params before adding atomic counter is:', params) // Add the counter params["UpdateExpression"] += ", #nImports = #nImports + :incr"; params["ExpressionAttributeValues"][":incr"] = 1; //params["ExpressionAttributeValues"][":nImports"] = 0; params["ExpressionAttributeNames"]["#nImports"] = 'nImports' console.log('params after adding atomic counter is:', params)``` I'm sticking with the original ADD answer because I like the differentiation it gives over the properties inserted by the SET, but both seem valid and I wanted to include the correction as well