DynamoDB更新数据 #

一、UpdateItem概述 #

1.1 基本概念 #

UpdateItem 用于更新项目中的属性,可添加、修改或删除属性。

text
UpdateItem特点:
├── 部分更新属性
├── 项目不存在时创建
├── 支持原子操作
├── 支持条件表达式
└── 支持返回值

1.2 与PutItem区别 #

操作 行为 适用场景
PutItem 完全替换项目 创建或完全替换
UpdateItem 部分更新属性 修改部分属性

二、更新表达式 #

2.1 SET操作 #

SET用于添加或修改属性:

bash
aws dynamodb update-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}' \
  --update-expression 'SET Name = :name, Age = :age' \
  --expression-attribute-values \
    '{":name": {"S": "John Smith"}, ":age": {"N": "31"}}'
javascript
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Name = :name, Age = :age',
  ExpressionAttributeValues: {
    ':name': 'John Smith',
    ':age': 31
  }
}));

2.2 REMOVE操作 #

REMOVE用于删除属性:

bash
aws dynamodb update-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}' \
  --update-expression 'REMOVE TempField, OldField'
javascript
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'REMOVE TempField, OldField'
}));

2.3 ADD操作 #

ADD用于添加到集合或数字增量:

bash
# 数字增量
aws dynamodb update-item \
  --table-name Counters \
  --key '{"CounterId": {"S": "visits"}}' \
  --update-expression 'ADD Value :inc' \
  --expression-attribute-values '{":inc": {"N": "1"}}'

# 添加到集合
aws dynamodb update-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}' \
  --update-expression 'ADD Tags :tag' \
  --expression-attribute-values '{":tag": {"SS": ["new_tag"]}}'
javascript
// 数字增量
await docClient.send(new UpdateCommand({
  TableName: 'Counters',
  Key: { CounterId: 'visits' },
  UpdateExpression: 'ADD Value :inc',
  ExpressionAttributeValues: { ':inc': 1 }
}));

// 添加到集合
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'ADD Tags :tag',
  ExpressionAttributeValues: {
    ':tag': new Set(['new_tag'])
  }
}));

2.4 DELETE操作 #

DELETE用于从集合中删除元素:

bash
aws dynamodb update-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}' \
  --update-expression 'DELETE Tags :tag' \
  --expression-attribute-values '{":tag": {"SS": ["old_tag"]}}'
javascript
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'DELETE Tags :tag',
  ExpressionAttributeValues: {
    ':tag': new Set(['old_tag'])
  }
}));

三、高级更新操作 #

3.1 原子计数器 #

javascript
// 增加计数器
const response = await docClient.send(new UpdateCommand({
  TableName: 'Counters',
  Key: { CounterId: 'page_views' },
  UpdateExpression: 'SET #v = #v + :inc',
  ExpressionAttributeNames: {
    '#v': 'Value'
  },
  ExpressionAttributeValues: {
    ':inc': 1
  },
  ReturnValues: 'UPDATED_NEW'
}));

console.log('New value:', response.Attributes.Value);

3.2 条件更新 #

javascript
// 仅当版本匹配时更新
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Name = :name, Version = Version + :inc',
  ConditionExpression: 'Version = :currentVersion',
  ExpressionAttributeValues: {
    ':name': 'John Smith',
    ':inc': 1,
    ':currentVersion': 5
  }
}));

3.3 列表操作 #

更新列表元素:

javascript
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Tags[0] = :tag',
  ExpressionAttributeValues: {
    ':tag': 'updated_tag'
  }
}));

追加到列表:

javascript
// 追加到末尾
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Tags = list_append(Tags, :newTags)',
  ExpressionAttributeValues: {
    ':newTags': ['tag4', 'tag5']
  }
}));

// 追加到开头
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Tags = list_append(:newTags, Tags)',
  ExpressionAttributeValues: {
    ':newTags': ['tag0']
  }
}));

从列表中删除元素:

javascript
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'REMOVE Tags[1], Tags[2]'
}));

3.4 嵌套属性更新 #

javascript
// 更新嵌套Map属性
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Address.City = :city, Address.Country = :country',
  ExpressionAttributeValues: {
    ':city': 'Shanghai',
    ':country': 'China'
  }
}));

// 更新深层嵌套属性
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Profile.Contacts.Email = :email',
  ExpressionAttributeValues: {
    ':email': 'newemail@example.com'
  }
}));

3.5 条件创建属性 #

javascript
// 仅当属性不存在时设置
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET CreatedAt = if_not_exists(CreatedAt, :now)',
  ExpressionAttributeValues: {
    ':now': new Date().toISOString()
  }
}));

四、表达式属性名 #

4.1 处理保留字 #

DynamoDB有保留字,使用ExpressionAttributeNames处理:

javascript
// "Name"是保留字
await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET #n = :name, #s = :status',
  ExpressionAttributeNames: {
    '#n': 'Name',
    '#s': 'Status'
  },
  ExpressionAttributeValues: {
    ':name': 'John Smith',
    ':status': 'active'
  }
}));

4.2 常见保留字 #

text
常见保留字:
├── Name, Names
├── Status
├── Date
├── Year, Month, Day
├── User
├── Order
├── Key, Value
├── Data
└── Timestamp

五、返回值配置 #

5.1 返回选项 #

选项 说明
NONE 不返回任何值(默认)
ALL_OLD 返回更新前的完整项目
UPDATED_OLD 返回更新前被修改的属性
ALL_NEW 返回更新后的完整项目
UPDATED_NEW 返回更新后被修改的属性

5.2 使用示例 #

javascript
// 返回更新后的属性
const response = await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Name = :name, Age = :age',
  ExpressionAttributeValues: {
    ':name': 'John Smith',
    ':age': 31
  },
  ReturnValues: 'UPDATED_NEW'
}));

console.log('Updated attributes:', response.Attributes);
// { Name: 'John Smith', Age: 31 }
javascript
// 返回更新前的值
const response = await docClient.send(new UpdateCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  UpdateExpression: 'SET Name = :name',
  ExpressionAttributeValues: { ':name': 'John Smith' },
  ReturnValues: 'ALL_OLD'
}));

console.log('Old item:', response.Attributes);
// { UserId: 'user123', Name: 'John Doe', Age: 30, ... }

六、条件表达式 #

6.1 常用条件 #

javascript
// 属性存在检查
ConditionExpression: 'attribute_exists(Email)'

// 属性不存在检查
ConditionExpression: 'attribute_not_exists(DeletedAt)'

// 值比较
ConditionExpression: 'Age > :minAge'
ConditionExpression: 'Status = :status'
ConditionExpression: 'Version = :expectedVersion'

// 集合包含
ConditionExpression: 'contains(Tags, :tag)'

// 大小检查
ConditionExpression: 'size(Description) <= :maxSize'

// 组合条件
ConditionExpression: 'Status = :status AND Age > :minAge'

6.2 乐观锁实现 #

javascript
async function updateUserWithOptimisticLock(userId, updates, currentVersion) {
  try {
    const response = await docClient.send(new UpdateCommand({
      TableName: 'Users',
      Key: { UserId: userId },
      UpdateExpression: 'SET #n = :name, Version = Version + :inc',
      ConditionExpression: 'Version = :currentVersion',
      ExpressionAttributeNames: {
        '#n': 'Name'
      },
      ExpressionAttributeValues: {
        ':name': updates.name,
        ':inc': 1,
        ':currentVersion': currentVersion
      },
      ReturnValues: 'ALL_NEW'
    }));
    
    return { success: true, item: response.Attributes };
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      return { success: false, error: 'Version conflict - item was modified by another process' };
    }
    throw error;
  }
}

七、实用示例 #

7.1 用户状态更新 #

javascript
async function updateUserStatus(userId, newStatus) {
  const timestamp = new Date().toISOString();
  
  await docClient.send(new UpdateCommand({
    TableName: 'Users',
    Key: { UserId: userId },
    UpdateExpression: `
      SET #s = :status, 
          UpdatedAt = :timestamp,
          StatusHistory = list_append(if_not_exists(StatusHistory, :emptyList), :newEntry)
    `,
    ExpressionAttributeNames: {
      '#s': 'Status'
    },
    ExpressionAttributeValues: {
      ':status': newStatus,
      ':timestamp': timestamp,
      ':emptyList': [],
      ':newEntry': [{ status: newStatus, timestamp }]
    }
  }));
}

7.2 购物车更新 #

javascript
async function addToCart(userId, product) {
  const { productId, name, price, quantity } = product;
  
  await docClient.send(new UpdateCommand({
    TableName: 'Carts',
    Key: { UserId: userId },
    UpdateExpression: `
      SET Items = if_not_exists(Items, :emptyList),
          TotalPrice = if_not_exists(TotalPrice, :zero) + :priceIncrease,
          UpdatedAt = :timestamp
      ADD Items[${getInsertIndex()}] :newItem
    `,
    ExpressionAttributeValues: {
      ':emptyList': [],
      ':zero': 0,
      ':priceIncrease': price * quantity,
      ':timestamp': new Date().toISOString(),
      ':newItem': { productId, name, price, quantity }
    }
  }));
}

7.3 点赞功能 #

javascript
async function toggleLike(userId, postId) {
  const response = await docClient.send(new UpdateCommand({
    TableName: 'Posts',
    Key: { PostId: postId },
    UpdateExpression: `
      ADD LikeCount :inc
      ADD LikedByUsers :userSet
    `,
    ConditionExpression: 'NOT contains(LikedByUsers, :userId)',
    ExpressionAttributeValues: {
      ':inc': 1,
      ':userSet': new Set([userId]),
      ':userId': userId
    },
    ReturnValues: 'UPDATED_NEW'
  }));
  
  return response.Attributes.LikeCount;
}

7.4 库存扣减 #

javascript
async function decreaseStock(productId, quantity) {
  try {
    const response = await docClient.send(new UpdateCommand({
      TableName: 'Products',
      Key: { ProductId: productId },
      UpdateExpression: 'SET Stock = Stock - :quantity',
      ConditionExpression: 'Stock >= :quantity',
      ExpressionAttributeValues: {
        ':quantity': quantity
      },
      ReturnValues: 'UPDATED_NEW'
    }));
    
    return { success: true, remainingStock: response.Attributes.Stock };
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      return { success: false, error: 'Insufficient stock' };
    }
    throw error;
  }
}

八、错误处理 #

8.1 常见错误 #

javascript
try {
  await docClient.send(new UpdateCommand({
    TableName: 'Users',
    Key: { UserId: 'user123' },
    UpdateExpression: 'SET Name = :name',
    ExpressionAttributeValues: { ':name': 'John Smith' }
  }));
} catch (error) {
  switch (error.name) {
    case 'ConditionalCheckFailedException':
      console.error('条件检查失败');
      break;
    case 'ProvisionedThroughputExceededException':
      console.error('超过预置吞吐量');
      break;
    case 'ResourceNotFoundException':
      console.error('表不存在');
      break;
    case 'ValidationException':
      console.error('参数验证失败:', error.message);
      break;
    default:
      console.error('未知错误:', error);
  }
}

九、最佳实践 #

9.1 更新表达式优化 #

text
优化建议:
├── 合并多个SET操作
├── 使用if_not_exists避免覆盖
├── 使用ExpressionAttributeNames处理保留字
└── 合理使用ReturnValues

9.2 条件更新使用场景 #

text
适用场景:
├── 乐观锁
├── 唯一性约束
├── 业务规则验证
├── 防止并发冲突
└── 数据完整性保护

十、总结 #

UpdateItem要点:

特性 说明
SET 添加或修改属性
REMOVE 删除属性
ADD 数字增量或集合添加
DELETE 从集合中删除元素
条件表达式 支持条件更新
返回值 支持多种返回选项

下一步,让我们学习删除数据!

最后更新:2026-03-27