0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DynamoDBのupdate条件式でハマるポイント|AI実務ノート 編集部

Last updated at Posted at 2026-01-03

1. 結論(この記事で得られること)

DynamoDBのUpdateItemで条件式を書くとき、「なぜかエラーになる」「思った条件で更新されない」という経験、ありませんか?

私も3年前、本番環境で条件式のミスによる予期しない更新を起こしかけて、コードレビューで先輩に止められた経験があります。

この記事では以下が得られます:

  • 条件式(ConditionExpression)の正しい書き方と頻出エラーパターン
  • UpdateExpressionとConditionExpressionの使い分け
  • 属性が存在しない場合の安全な扱い方
  • AIを使った高速デバッグ手法(有料部分)
  • 本番運用での失敗パターンと対策(有料部分)

10分で読めて、明日から実務で使える内容に絞りました。

2. 前提(環境・読者層)

想定読者

  • DynamoDBを触り始めて数ヶ月〜1年程度
  • UpdateItemの基本は知っているが、条件式で詰まったことがある
  • 本番環境でのミスを未然に防ぎたいエンジニア

動作環境

  • AWS SDK for JavaScript v3(コード例はTypeScript)
  • DynamoDB(リージョン・容量モードは問わず)
  • Node.js 18以上推奨

boto3やJavaのSDKでも考え方は共通です。

3. Before:よくあるつまずきポイント

3-1. 「attribute_exists」と「attribute_not_exists」の罠

// ❌ NG:よくある間違い
await docClient.update({
  TableName: 'Users',
  Key: { userId: 'user123' },
  UpdateExpression: 'SET #status = :newStatus',
  ConditionExpression: 'attribute_exists(#status)', // 既存属性がある場合のみ更新
  ExpressionAttributeNames: {
    '#status': 'status'
  },
  ExpressionAttributeValues: {
    ':newStatus': 'active'
  }
});

何がダメか?

  • 「attribute_exists」は「その属性がitemに存在するか」をチェックする
  • 初回登録時やステータス未設定のユーザーでは更新が失敗する
  • 「ConditionalCheckFailedException」が投げられるが、原因がログからわかりにくい

私も昔、これで「特定ユーザーだけ更新できない」というバグを半日追いかけました。

3-2. 比較演算子での型不一致

// ❌ NG:数値比較のつもりが文字列比較になっている
ConditionExpression: '#version = :expectedVersion',
ExpressionAttributeValues: {
  ':expectedVersion': '5' // ← DynamoDB上はNumber型なのに文字列で指定
}
  • エラーにはならないが、条件が常にfalseになり更新が通らない
  • 型の不一致は実行時まで分からない(TypeScriptでも防げない)

3-3. 論理演算の優先順位ミス

//  NG意図しない評価順序
ConditionExpression: '#status = :active OR #status = :pending AND #retryCount < :maxRetry'
//  ANDが先に評価されるため意図と異なる動作になる

これも実際にレビューで指摘したケースです。括弧を使わないと事故ります。

4. After:基本的な解決パターン

4-1. 属性の存在チェックを正しく行う

//  OK属性がない場合はデフォルト値で作成ある場合は条件付き更新
await docClient.update({
  TableName: 'Users',
  Key: { userId: 'user123' },
  UpdateExpression: 'SET #status = :newStatus, #updatedAt = :now',
  ConditionExpression: 'attribute_not_exists(#status) OR #status = :oldStatus',
  ExpressionAttributeNames: {
    '#status': 'status',
    '#updatedAt': 'updatedAt'
  },
  ExpressionAttributeValues: {
    ':newStatus': 'active',
    ':oldStatus': 'pending',
    ':now': Date.now()
  }
});

ポイント

  • 「OR」で「新規作成」と「既存更新」の両方に対応
  • 楽観ロック的な使い方(期待する古い値をチェック)

4-2. 型を明示的に統一する

// ✅ OK:数値型として明示
ExpressionAttributeValues: {
  ':expectedVersion': 5,  // Numberとして扱われる
  ':maxRetry': 3
}

AWS SDK v3では型推論が効くが、念のため確認

import { marshall } from '@aws-sdk/util-dynamodb';
 
// 型が不安なら明示的に変換
const values = marshall({
  ':expectedVersion': 5
}, { removeUndefinedValues: true });

4-3. 論理演算は括弧で明示

//  OK意図通りの評価順序
ConditionExpression: '(#status = :active OR #status = :pending) AND #retryCount < :maxRetry'

ここは必ずレビューでチェックします。自分も見落としやすいので。

4-4. よく使う安全パターン集

// パターン1:楽観ロック(バージョン番号チェック)
ConditionExpression: '#version = :currentVersion',
UpdateExpression: 'SET #data = :newData, #version = #version + :inc',
ExpressionAttributeValues: {
  ':currentVersion': 5,
  ':newData': { /* ... */ },
  ':inc': 1
}
 
// パターン2:在庫減算(負にならないチェック)
ConditionExpression: '#stock >= :quantity',
UpdateExpression: 'SET #stock = #stock - :quantity'
 
// パターン3:初回のみ作成(既存なら何もしない)
ConditionExpression: 'attribute_not_exists(PK)'

特に在庫管理などの数値操作では、条件式が必須です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?