首页 > 解决方案 > 是否可以在更新表达式中添加两个以上的属性/操作数来设置 DynamoDB 中的另一个属性?

问题描述

我试图在 DynamoDB 表的列中存储一个数值,该数值是该表中其他数值列的加法。

我读到这个:https ://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html虽然似乎没有明确提到两个以上的操作数不能与“+”中的操作相结合连续,当我尝试这样做时它确实失败了。

虽然我可能不应该将聚合值存储在列中,并且只有存储粒度值的列,但是这里有解决方法吗?

编辑: 这就是我想要做的

aws dynamodb --endpoint-url http://localhost:8042 update-item --table-name custom_table --key "{\"main_part\":{\"S\":\"hash_key\"},\"sort_part\":{\"S\":\"range_key\"}}" --update-expression "SET #A = #B + #C + :val" --expression-attribute-names "{\"#A\":\"column_1\",\"#B\":\"column_2\",\"#C\":\"column_3\"}"  --expression-attribute-values "{\":val\":{\"N\":\"10\"}}"

这给出了以下错误:

An error occurred (ValidationException) when calling the UpdateItem operation: Invalid UpdateExpression: Syntax error; token: "+", near: "#C + :val"

标签: amazon-web-servicesamazon-dynamodb

解决方案


围绕UpdateItem 的 SET 操作的+文档明确表示,当使用 a或 a-作为表达式的一部分时,您只能有一个两个操作数。请注意,文档将操作描述valueSET

value ::=
    operand
    | operand '+' operand
    | operand '-' operand

如果它支持两个以上的操作数,你会在那里看到它。另外,您的示例很好地证明了它不起作用。

为了完成(我认为)您想要的,我认为您需要将临时操作数存储为项目的一部分。下面是一个示例,我正在创建和更新一个项目以保持水果计数。该项目具有以下属性:greenApplesredApplesapples和。 应该始终是绿色和红色苹果的总和。 应始终是 、 和的总和。您的问题中有趣的一点是,当您只知道要增加其中一个组件属性的数量时,如何更新此项目。bananasfruitapplesfruitgreenApplesredApplesbananas

要考虑的一种选择是在您的命令中使用多个操作,您可以在一个语句中UpdateItem同时更新组件值 ( redApples) 以及聚合值 (apples和)。fruit这是使用AWS SDK V3的完整 TypeScript 代码示例:

const tableName = getFromEnv(`TABLE_NAME`);
const client = new DynamoDBClient({});
const PK = `FRUIT_COUNT`;
const SK = `FRUIT_COUNT`;

interface FruitOnHand {
    bananas: number;
    greenApples: number;
    redApples: number;
}

interface FruitCount extends FruitOnHand {
    apples: number;
    fruit: number;
}

async function createItem({ bananas, greenApples, redApples }: FruitOnHand): Promise<FruitCount> {
    const apples = greenApples + redApples;
    const fruit = greenApples + redApples + bananas;
    const appleCount = { apples, fruit, greenApples, redApples, bananas };
    const item = { PK, SK, ...appleCount };
    const putCommand = new PutItemCommand({
        TableName: tableName,
        Item: marshall(item)
    });
    await client.send(putCommand);
    log(`Created item: ${inspect(item)}`);
    return appleCount;
}

async function addRedApples(toAdd: number): Promise<FruitCount> {
    const valueToAdd = { N: String(toAdd) };

    const updateCommand = new UpdateItemCommand({
        TableName: tableName,
        Key: marshall({ PK, SK }),
        UpdateExpression: `SET #APPLES = #APPLES + :val, #FRUIT = #FRUIT + :val, #RED = #RED + :val`,
        ExpressionAttributeNames: {
            '#APPLES': `apples`,
            '#FRUIT': `fruit`,
            '#RED': `redApples`,
        },
        ExpressionAttributeValues: {
            ':val': valueToAdd,
        },
        ReturnValues: 'ALL_NEW',
    });

    log(`Adding ${toAdd} red apples`)
    const { Attributes } = await client.send(updateCommand);
    const fruitCount = unmarshall(Attributes) as unknown as FruitCount;
    log(`Updated item:`, inspect(fruitCount));

    return fruitCount;
}

async function run() {
    const initial = await createItem({
        bananas: 2,
        greenApples: 5,
        redApples: 3,
    });

    assert(initial.redApples + initial.greenApples === initial.apples);
    assert(initial.fruit === initial.apples + initial.bananas);

    const updated = await addRedApples(8);
    assert(updated.redApples + updated.greenApples === updated.apples);
    assert(updated.fruit === updated.apples + updated.bananas);
}

很多代码只是设置示例并让 TypeScript 编译器满意;有趣的是在addRedApples(...)功能中,我将其用作UpdateExpression...

`SET #APPLES = #APPLES + :val, #FRUIT = #FRUIT + :val, #RED = #RED + :val`

...将组件属性的值以及两个聚合属性的值增加相同的数量。

执行run()函数的结果显示项目的初始和更新内容:

Created item: {
  PK: 'FRUIT_COUNT',
  SK: 'FRUIT_COUNT',
  apples: 8,
  fruit: 10,
  greenApples: 5,
  redApples: 3,
  bananas: 2
}
Adding 8 red apples
Updated item: {
  apples: 16,
  fruit: 18,
  bananas: 2,
  SK: 'FRUIT_COUNT',
  redApples: 11,
  PK: 'FRUIT_COUNT',
  greenApples: 5
}


推荐阅读