首页 > 解决方案 > DynamoDB.DocumentClient 具有动态属性和原子计数器

问题描述

我正在处理一个 dynamoDB 更新应该的用例:

  1. 动态更新插入(如果存在则更新,如果不存在则插入)项目,而不对项目的组件进行硬编码。
  2. 为简单起见,使用 DynamoDB 文档客户端
  3. 并在同一个原子操作中,更新一个简单的计数器

我从 Daniel Barrel 在https://stackoverflow.com/a/63511693/15369972上的一个出色的实用方法开始,它提供了一种用于更新动态值的通用实用方法,但没有原子计数器。

我试图通过在加载动态值后将计数器及其增量器添加到参数对象中来添加原子计数器功能,但是在更新时在计数器中得到一个静态值,而不是每次调用时递增一的值.

这哪里出错了?我用一个表名、一个动态 javascript 对象和一个包含哈希和排序键的数组来调用修改后的更新函数:

await update(tableName, jsonObjectToStore, ['myHashKey', 'mySortKey'])

修改后的更新方法不会像我想要的那样增加,是:

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();
}

标签: javascriptamazon-dynamodb

解决方案


与 AWS Support 合作寻找合理的解决方案。他们也不确定如何使用 ddb 文档客户端(与具有许多文档示例的低级客户端相反)进行原子计数器,但建议使用 ADD 命令,该命令具有对数字字段进行原子更新的副作用。

因此,在下面的示例中,我们从要存储的对象构造动态更新,然后在更新表达式中附加 ADD 语句(不带逗号!),并将实际上是数字增量器添加到 nImports 的 ExpressionAttributeValues 中。像这样,这应该是一个完整的工作 lambda 示例。有一些 console.log 语句可以显示正在发生的事情:

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;
}

推荐阅读