

# 在 DynamoDB 中使用更新表达式
<a name="Expressions.UpdateExpressions"></a>

`UpdateItem` 操作会更新现有项目，或者将新项目添加到表中（如果该新项目尚不存在）。您必须提供要更新的项目的键。您还必须提供更新表达式，指示您要修改的属性以及要分配给这些属性的值。

*更新表达式* 指定 `UpdateItem` 将如何修改项目的属性，例如，设置标量值或者删除列表或映射中的元素。

下面是更新表达式的语法摘要。

```
update-expression ::=
    [ SET action [, action] ... ]
    [ REMOVE action [, action] ...]
    [ ADD action [, action] ... ]
    [ DELETE action [, action] ...]
```

更新表达式包含一个或多个子句。每个子句以 `SET`、`REMOVE`、`ADD` 或 `DELETE` 关键字开头。您可在更新表达式中按任意顺序包含其中任意子句。但是，每个操作关键字只能出现一次。

每个子句中存在一个或多个操作，用逗号分隔。每个操作表示一个数据修改。

此部分中的示例基于`ProductCatalog`中所示的 [在 DynamoDB 中使用投影表达式](Expressions.ProjectionExpressions.md) 项目。

以下主题介绍了 `SET` 操作的一些不同使用案例。

**Topics**
+ [SET – 修改或添加项目属性](#Expressions.UpdateExpressions.SET)
+ [REMOVE – 从项目中删除属性](#Expressions.UpdateExpressions.REMOVE)
+ [ADD – 更新数值和集](#Expressions.UpdateExpressions.ADD)
+ [DELETE – 从集中删除元素](#Expressions.UpdateExpressions.DELETE)
+ [使用多个更新表达式](#Expressions.UpdateExpressions.Multiple)

## SET – 修改或添加项目属性
<a name="Expressions.UpdateExpressions.SET"></a>

在更新表达式中使用 `SET` 操作可将一个或多个属性添加到项目。如果任意这些属性已存在，则将由新值覆盖。如果您要避免覆盖现有属性，则可以将 `SET` 与 `if_not_exists` 函数结合使用。`if_not_exists` 函数特定于 `SET` 操作，只能在更新表达式中使用。

当您使用 `SET` 更新列表元素时，将使用您指定的新数据替代该元素的内容。如果元素尚不存在，`SET` 会将新元素附加到列表的末尾。

如果在单个 `SET` 操作中添加多个元素，则元素会按照元素编号的顺序排序。

您还可使用 `SET` 来加或减 `Number` 类型的属性。要执行多个 `SET` 操作，请使用逗号分隔它们。

在以下语法摘要中：
+ *path* 元素是项目的文档路径。
+ **operand** 元素可以为项目的文档路径，或者为函数。

```
set-action ::=
    path = value

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

operand ::=
    path | function

function ::=
    if_not_exists (path, value)
```

如果项目在指定路径中不包含属性，则 `if_not_exists` 的求值结果为 `value`。否则，它的求值结果为 `path`。

以下 `PutItem` 操作创建将在示例中引用的示例项目。

```
aws dynamodb put-item \
    --table-name ProductCatalog \
    --item file://item.json
```

`--item` 的参数存储在 `item.json` 文件中。（为简单起见，仅使用了几个项目属性。）

```
{
    "Id": {"N": "789"},
    "ProductCategory": {"S": "Home Improvement"},
    "Price": {"N": "52"},
    "InStock": {"BOOL": true},
    "Brand": {"S": "Acme"}
}
```

**Topics**
+ [修改属性](#Expressions.UpdateExpressions.SET.ModifyingAttributes)
+ [添加列表和映射](#Expressions.UpdateExpressions.SET.AddingListsAndMaps)
+ [将元素添加到列表](#Expressions.UpdateExpressions.SET.AddingListElements)
+ [添加嵌套映射属性](#Expressions.UpdateExpressions.SET.AddingNestedMapAttributes)
+ [对数值属性进行加减](#Expressions.UpdateExpressions.SET.IncrementAndDecrement)
+ [将元素附加到列表](#Expressions.UpdateExpressions.SET.UpdatingListElements)
+ [防止覆盖现有属性](#Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites)

### 修改属性
<a name="Expressions.UpdateExpressions.SET.ModifyingAttributes"></a>

**Example**  
更新 `ProductCategory` 和 `Price` 属性。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET ProductCategory = :c, Price = :p" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` 的参数存储在 `values.json` 文件中。  

```
{
    ":c": { "S": "Hardware" },
    ":p": { "N": "60" }
}
```

**注意**  
在 `UpdateItem` 操作中，`--return-values ALL_NEW` 将导致 DynamoDB 按更新后的情况返回项目。

### 添加列表和映射
<a name="Expressions.UpdateExpressions.SET.AddingListsAndMaps"></a>

**Example**  
添加新列表和新映射。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems = :ri, ProductReviews = :pr" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` 的参数存储在 `values.json` 文件中。  

```
{
    ":ri": {
        "L": [
            { "S": "Hammer" }
        ]
    },
    ":pr": {
        "M": {
            "FiveStar": {
                "L": [
                    { "S": "Best product ever!" }
                ]
            }
        }
    }
}
```

### 将元素添加到列表
<a name="Expressions.UpdateExpressions.SET.AddingListElements"></a>

**Example**  
将新元素添加到 `RelatedItems` 列表。（请记住，列表元素从 0 开始，因此 [0] 表示列表中的第一个元素，[1] 表示第二个元素，依此类推。）  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :ri" \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-values` 的参数存储在 `values.json` 文件中。  

```
{
    ":ri": { "S": "Nails" }
}
```

**注意**  
当您使用 `SET` 更新列表元素时，将使用您指定的新数据替代该元素的内容。如果元素尚不存在，`SET` 会将新元素附加到列表的末尾。  
如果在单个 `SET` 操作中添加多个元素，则元素会按照元素编号的顺序排序。

### 添加嵌套映射属性
<a name="Expressions.UpdateExpressions.SET.AddingNestedMapAttributes"></a>

**Example**  
添加一些嵌套映射属性。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #pr.#5star[1] = :r5, #pr.#3star = :r3" \
    --expression-attribute-names file://names.json \
    --expression-attribute-values file://values.json \
    --return-values ALL_NEW
```
`--expression-attribute-names` 的参数存储在 `names.json` 文件中。  

```
{
    "#pr": "ProductReviews",
    "#5star": "FiveStar",
    "#3star": "ThreeStar"
}
```
`--expression-attribute-values` 的参数存储在 `values.json` 文件中。  

```
{
    ":r5": { "S": "Very happy with my purchase" },
    ":r3": {
        "L": [
            { "S": "Just OK - not that great" }
        ]
    }
}
```

**重要**  
如果父映射不存在，则无法更新嵌套映射属性。如果您在父映射 (`ProductReviews`) 不存在时尝试更新嵌套属性（例如 `ProductReviews.FiveStar`），则 DynamoDB 会返回 `ValidationException`，并显示消息*“The document path provided in the update expression is invalid for update.”*  
在创建稍后将更新嵌套映射属性的项目时，请初始化父属性的空映射。例如：  

```
{
    "Id": {"N": "789"},
    "ProductReviews": {"M": {}},
    "Metadata": {"M": {}}
}
```
这使您可以像 `ProductReviews.FiveStar` 一样更新嵌套属性，而不会出现错误。

### 对数值属性进行加减
<a name="Expressions.UpdateExpressions.SET.IncrementAndDecrement"></a>

您可以对现有数值属性执行加减运算。为此，请使用 `+`（加号）和 `-`（减号）运算符。

**Example**  
降低项目的 `Price`。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```
要提高 `Price`，请在更新表达式中使用 `+` 运算符。

### 将元素附加到列表
<a name="Expressions.UpdateExpressions.SET.UpdatingListElements"></a>

您可将元素添加到列表的末尾。为此，请将 `SET` 与 `list_append` 函数结合使用。（函数名区分大小写。） `list_append` 函数特定于 `SET` 操作，只能在更新表达式中使用。语法如下所示。
+ `list_append (list1, list2)`

该函数将选取两个列表作为输入，并将所有元素从 `list2` 附加到 ` list1`。

**Example**  
在[将元素添加到列表](#Expressions.UpdateExpressions.SET.AddingListElements)中，您创建 `RelatedItems` 列表并填充两个元素：`Hammer` 和 `Nails`。现在您将另外两个元素附加到 `RelatedItems` 的末尾。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(#ri, :vals)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values file://values.json  \
    --return-values ALL_NEW
```
`--expression-attribute-values` 的参数存储在 `values.json` 文件中。  

```
{
    ":vals": {
        "L": [
            { "S": "Screwdriver" },
            {"S": "Hacksaw" }
        ]
    }
}
```
最后，您将另外一个元素添加到 `RelatedItems` 的 *beginning*。为此，请交换 `list_append` 元素的顺序。（请记住，`list_append` 将选取两个列表作为输入，并会将第二个列表附加到第一个列表。）  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET #ri = list_append(:vals, #ri)" \
    --expression-attribute-names '{"#ri": "RelatedItems"}' \
    --expression-attribute-values '{":vals": {"L": [ { "S": "Chisel" }]}}' \
    --return-values ALL_NEW
```
生成的 `RelatedItems` 属性现在包含 5 个元素，其顺序如下：`Chisel`、`Hammer`、`Nails`、`Screwdriver`、`Hacksaw`。

### 防止覆盖现有属性
<a name="Expressions.UpdateExpressions.SET.PreventingAttributeOverwrites"></a>

**Example**  
设置项目的 `Price`，但仅当项目还没有 `Price` 属性时设置。（如果 `Price` 已存在，则不执行任何操作。）  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = if_not_exists(Price, :p)" \
    --expression-attribute-values '{":p": {"N": "100"}}' \
    --return-values ALL_NEW
```

## REMOVE – 从项目中删除属性
<a name="Expressions.UpdateExpressions.REMOVE"></a>

在更新表达式中使用 `REMOVE` 操作可在 Amazon DynamoDB 中从某个项目删除一个或多个元素。要执行多个 `REMOVE` 操作，请使用逗号分隔它们。

下面是更新表达式中的 `REMOVE` 的语法摘要。唯一的操作数是您要删除的属性的文档路径。

```
remove-action ::=
    path
```

**Example**  
从项目中删除部分属性。（如果属性不存在，则不会执行任何操作。）  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE Brand, InStock, QuantityOnHand" \
    --return-values ALL_NEW
```

### 从列表中删除元素
<a name="Expressions.UpdateExpressions.REMOVE.RemovingListElements"></a>

您可使用 `REMOVE` 从列表中删除各个元素。

**Example**  
在[将元素附加到列表](#Expressions.UpdateExpressions.SET.UpdatingListElements)中，您修改了列表属性 (`RelatedItems`)，使它包含 5 个元素：  
+ `[0]`—`Chisel`
+ `[1]`—`Hammer`
+ `[2]`—`Nails`
+ `[3]`—`Screwdriver`
+ `[4]`—`Hacksaw`
以下 AWS Command Line Interface (AWS CLI) 示例从列表中删除 `Hammer` 和 `Nails`。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "REMOVE RelatedItems[1], RelatedItems[2]" \
    --return-values ALL_NEW
```
删除 `Hammer` 和 `Nails` 之后，剩下的元素将会移位。此列表现在包含以下元素：  
+ `[0]`—`Chisel`
+ `[1]`—`Screwdriver`
+ `[2]`—`Hacksaw`

## ADD – 更新数值和集
<a name="Expressions.UpdateExpressions.ADD"></a>

**注意**  
通常，我们建议使用 `SET` 而不是 `ADD` 来确保幂等运算。

在更新表达式中使用 `ADD` 操作可将新属性及其值添加到项目。

如果属性已存在，则 `ADD` 的行为取决于属性的数据类型：
+ 如果属性是数字，并且添加的值也是数字，则该值将按数学运算与现有属性相加。（如果该值为负数，则从现有属性减去该值。）
+ 如果属性是集合，并且您添加的值也是集合，则该值将附加到现有集合中。

**注意**  
`ADD` 操作仅支持数字和集合数据类型。

要执行多个 `ADD` 操作，请使用逗号分隔它们。

在以下语法摘要中：
+ *path* 元素是属性的文档路径。属性必须为 `Number` 或 set 数据类型。
+ *value* 元素是要与属性相加的值（对于 `Number` 数据类型），或者是要附加到属性中的集合（对于 set 类型）。

```
add-action ::=
    path value
```

以下主题介绍了 `ADD` 操作的一些不同使用案例。

**Topics**
+ [添加数值](#Expressions.UpdateExpressions.ADD.Number)
+ [将元素添加到集](#Expressions.UpdateExpressions.ADD.Set)

### 添加数值
<a name="Expressions.UpdateExpressions.ADD.Number"></a>

假设 `QuantityOnHand` 属性不存在。以下 AWS CLI 示例会将 `QuantityOnHand` 设置为 5。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD QuantityOnHand :q" \
    --expression-attribute-values '{":q": {"N": "5"}}' \
    --return-values ALL_NEW
```

既然 `QuantityOnHand` 存在，您可重新运行该示例以使 `QuantityOnHand` 每次增加 5。

### 将元素添加到集
<a name="Expressions.UpdateExpressions.ADD.Set"></a>

假设 `Color` 属性不存在。以下 AWS CLI 示例会将 `Color` 设置为包含两个元素的字符串集。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Orange", "Purple"]}}' \
    --return-values ALL_NEW
```

既然 `Color` 存在，您可向其添加更多元素。

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "ADD Color :c" \
    --expression-attribute-values '{":c": {"SS":["Yellow", "Green", "Blue"]}}' \
    --return-values ALL_NEW
```

## DELETE – 从集中删除元素
<a name="Expressions.UpdateExpressions.DELETE"></a>

**重要**  
`DELETE` 操作仅支持 `Set` 数据类型。

在更新表达式中使用 `DELETE` 操作可从集合中删除一个或多个元素。要执行多个 `DELETE` 操作，请使用逗号分隔它们。

在以下语法摘要中：
+ *path* 元素是属性的文档路径。该属性必须是集数据类型。
+ *子网* 是您要从 *path* 中删除的一个或多个元素。您必须指定 *subset* 作为集类型。

```
delete-action ::=
    path subset
```

**Example**  
在[将元素添加到集](#Expressions.UpdateExpressions.ADD.Set)中，您创建 `Color` 字符串集合。本示例将从该集合中删除部分元素。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "DELETE Color :p" \
    --expression-attribute-values '{":p": {"SS": ["Yellow", "Purple"]}}' \
    --return-values ALL_NEW
```

## 使用多个更新表达式
<a name="Expressions.UpdateExpressions.Multiple"></a>

您可以在单个更新表达式中使用多个操作。在应用任何操作之前，将根据项目的状态对所有属性引用进行解析。

**Example**  
给定项目 `{"id": "1", "a": 1, "b": 2, "c": 3}` 时，以下表达式删除 `a` 并交换 `b` 及 `c` 的值：  

```
aws dynamodb update-item \
    --table-name test \
    --key '{"id":{"S":"1"}}' \
    --update-expression "REMOVE a SET b = a, c = b" \
    --return-values ALL_NEW
```
结果是 `{"id": "1", "b": 1, "c": 2}`。尽管在同一个表达式中 `a` 被移除并向 `b` 重新赋值，但两个引用都将解析为其原始值。

**Example**  
如果要修改属性的值并彻底删除另一个属性，可以在单个语句中使用 SET 和 REMOVE 操作。此操作会将 `Price` 值降至 15，同时还会从项目中删除 `InStock` 属性。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET Price = Price - :p REMOVE InStock" \
    --expression-attribute-values '{":p": {"N":"15"}}' \
    --return-values ALL_NEW
```

**Example**  
如果您想在添加到列表的同时更改另一个属性的值，则可以在单个语句中使用两个 SET 操作。此操作会将 “Nails” 添加到 `RelatedItems` 列表属性中，并将 `Price` 值设置为 21。  

```
aws dynamodb update-item \
    --table-name ProductCatalog \
    --key '{"Id":{"N":"789"}}' \
    --update-expression "SET RelatedItems[1] = :newValue, Price = :newPrice" \
    --expression-attribute-values '{":newValue": {"S":"Nails"}, ":newPrice": {"N":"21"}}'  \
    --return-values ALL_NEW
```