# 機能設計書 15-Update By Query

## 概要

本ドキュメントは、OpenSearchの「Update By Query」機能の設計を記述する。クエリ条件に一致するドキュメントを一括更新するUpdate By Query APIの処理仕様を定義する。

### 本機能の処理概要

本機能は、検索クエリに一致するすべてのドキュメントに対して、スクリプトやパイプラインを適用して一括更新する機能である。REST APIの `POST /{index}/_update_by_query` エンドポイントを通じて呼び出される。

**業務上の目的・背景**：大量のドキュメントに対してスキーマ変更やフィールド値の一括変更が必要な場合、個別のupdate操作を繰り返すのは非効率である。Update By Queryにより、条件に一致するドキュメントを一括で更新できる。

**機能の利用シーン**：フィールドの一括リネーム、計算フィールドの一括更新、データ正規化、インジェストパイプラインの再適用による既存データの再処理で利用される。

**主要な処理内容**：
1. ソースインデックスからスクロール検索で対象ドキュメントを順次取得
2. 各ドキュメントに対するスクリプト実行またはパイプライン適用
3. 更新後のドキュメントをバルク操作で書き戻し
4. スロットリングによる負荷制御
5. タスクとしての非同期実行と進捗管理

**関連システム・外部連携**：Painlessスクリプトエンジン、インジェストパイプラインとの連携。

**権限による制御**：アクション名 `indices:data/write/update/byquery` に基づくセキュリティ権限チェック。対象インデックスへの読み取りおよび書き込み権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | クエリによる更新 | 主機能 | クエリ条件に一致するドキュメントを一括更新する主処理 |
| 16 | クエリによる更新スロットル変更 | 主機能 | Update By Query操作のリクエスト/秒スロットルを変更する処理 |

## 機能種別

CRUD操作（条件付き一括Update）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| indices | String[] | Yes | 対象インデックス名（複数可） | 1つ以上必須 |
| searchRequest | SearchRequest | Yes | 更新対象ドキュメントの検索条件 | - |
| script | Script | No | ドキュメント更新スクリプト | - |
| pipeline | String | No | 適用するインジェストパイプライン | - |
| maxDocs | int | No | 更新する最大ドキュメント数 | - |
| conflicts | String | No | コンフリクト時の動作（proceed/abort） | - |
| requestsPerSecond | float | No | スロットリング（リクエスト/秒） | - |
| slices | int | No | 並列スライス数 | - |
| scroll | TimeValue | No | スクロールのキープアライブ時間 | - |
| timeout | TimeValue | No | タイムアウト | - |
| waitForCompletion | boolean | No | 同期/非同期実行（デフォルト: true） | - |

### 入力データソース

REST APIリクエスト（HTTP POST、クエリパラメータ + JSONボディ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| took | long | 処理時間（ミリ秒） |
| timed_out | boolean | タイムアウトしたか |
| total | long | 処理対象の総ドキュメント数 |
| updated | long | 更新されたドキュメント数 |
| version_conflicts | long | バージョンコンフリクト数 |
| batches | int | バッチ処理回数 |
| noops | long | 操作なし（スクリプトがnoop返却）の数 |
| retries | Object | リトライ回数（bulk, search） |
| throttled_millis | long | スロットリング待機時間 |
| failures | List | 失敗した操作のリスト |
| task | String | 非同期実行時のタスクID |

### 出力先

REST APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信・バリデーション
   └─ 対象インデックスの存在確認、検索クエリの検証
2. タスク登録
   └─ BulkByScrollTaskとして登録
3. スクロール検索で対象ドキュメント取得
   └─ SearchRequestを使用して対象ドキュメントを順次取得
4. バッチ処理ループ
   └─ 取得したドキュメントにスクリプトを適用し、バルク操作で更新
5. スロットリング（オプション）
   └─ requestsPerSecondに基づく待機
6. 完了・レスポンス返却
   └─ BulkByScrollResponse生成
```

### フローチャート

```mermaid
flowchart TD
    A[Update By Query リクエスト] --> B{バリデーション OK?}
    B -->|No| C[エラー返却]
    B -->|Yes| D[タスク登録]
    D --> E[スクロール検索開始]
    E --> F{ドキュメントあり?}
    F -->|No| G[完了]
    F -->|Yes| H[スクリプト/パイプライン適用]
    H --> I{noop?}
    I -->|Yes| J[noopカウント増加]
    I -->|No| K[バルク更新実行]
    J --> L{スロットリング?}
    K --> L
    L -->|Yes| M[待機]
    L -->|No| E
    M --> E
    G --> N[BulkByScrollResponse 返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | スクリプトnoop | スクリプトがctx.op='noop'を設定した場合、そのドキュメントの更新はスキップされる | スクリプト指定時 |
| BR-15-02 | バージョンコンフリクト | 更新時にバージョンが変更されていた場合のコンフリクト処理 | 常時 |
| BR-15-03 | スナップショット整合性 | スクロール検索時点のスナップショットに基づいて対象ドキュメントが決定される | 常時 |

### 計算ロジック

BulkByScrollResponseの各カウンタ（updated, noops, version_conflicts）は各バッチ処理の結果を累積して算出される。

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 対象検索 | Lucene Index | SELECT | スクロール検索によるドキュメント読み取り |
| ドキュメント更新 | Lucene Index | UPDATE | バルク操作によるドキュメント更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | 対象インデックス未指定 | インデックスを指定 |
| 409 | VersionConflictEngineException | バージョンコンフリクト | conflicts=proceedに設定 |
| 408 | タイムアウト | 処理がタイムアウト | タイムアウト値を増加 |

### リトライ仕様

バルク書き込みとスクロール検索にそれぞれリトライメカニズムが備わっている。retriesフィールドでリトライ回数を確認可能。

## トランザクション仕様

Update By Query全体はアトミックではない。バッチ単位で処理され、途中で失敗した場合は部分的に更新された状態となる。

## パフォーマンス要件

- slicesパラメータによる並列化でスループット向上が可能
- requestsPerSecondによるスロットリングで負荷制御
- スクリプト実行のオーバーヘッドを考慮する必要がある

## セキュリティ考慮事項

- 対象インデックスへのread/write両方の権限が必要
- スクリプト実行に関するセキュリティ制限（Painlessのサンドボックス）

## 備考

- TransportUpdateByQueryActionはClient, ScriptService, ClusterService, TransportSearchActionに依存
- スクリプト内でctx._source, ctx.op, ctx._routing等にアクセス可能
- IdFieldMapper, IndexFieldMapper, RoutingFieldMapperの定数を使用してフィールドマッピングを参照

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | UpdateByQueryRequest.java | `server/src/main/java/org/opensearch/index/reindex/UpdateByQueryRequest.java` | リクエスト構造。AbstractBulkIndexByScrollRequestを継承 |
| 1-2 | BulkByScrollResponse.java | `server/src/main/java/org/opensearch/index/reindex/BulkByScrollResponse.java` | 共通レスポンス構造 |
| 1-3 | AbstractBulkByScrollRequest.java | `server/src/main/java/org/opensearch/index/reindex/AbstractBulkByScrollRequest.java` | 共通パラメータ定義 |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | UpdateByQueryAction.java | `server/src/main/java/org/opensearch/index/reindex/UpdateByQueryAction.java` | アクション名 `indices:data/write/update/byquery` の定義 |
| 2-2 | TransportUpdateByQueryAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java` | トランスポート処理。Client, ScriptService, ClusterService, TransportSearchActionに依存 |

**主要処理フロー**:
1. **42-44行目（UpdateByQueryAction）**: アクション名定義
2. **61-63行目（TransportUpdateByQueryAction）**: HandledTransportActionを継承

#### Step 3: 実処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractAsyncBulkByScrollAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java` | スクロール検索→バルク書き込みの共通処理 |

### プログラム呼び出し階層図

```
REST API (POST /{index}/_update_by_query)
    |
    +-- TransportUpdateByQueryAction
           |
           +-- AbstractAsyncBulkByScrollAction
                  |
                  +-- ScrollableHitSource (スクロール検索)
                  |      └─ ClientScrollableHitSource
                  |
                  +-- ScriptService (スクリプト実行)
                  |
                  +-- BulkRequest (バルク更新)
```

### データフロー図

```
[入力]                          [処理]                              [出力]

UpdateByQueryRequest     -->  TransportUpdateByQueryAction      -->  BulkByScrollResponse
 - indices                     |                                      - took
 - searchRequest               +-> スクロール検索                      - total
 - script                      +-> スクリプト適用                     - updated
 - requestsPerSecond            +-> バルク更新                        - noops
 - slices                                                             - failures
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| UpdateByQueryAction.java | `server/src/main/java/org/opensearch/index/reindex/UpdateByQueryAction.java` | ソース | アクション型定義 |
| UpdateByQueryRequest.java | `server/src/main/java/org/opensearch/index/reindex/UpdateByQueryRequest.java` | ソース | リクエスト構造 |
| UpdateByQueryRequestBuilder.java | `server/src/main/java/org/opensearch/index/reindex/UpdateByQueryRequestBuilder.java` | ソース | リクエストビルダー |
| TransportUpdateByQueryAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java` | ソース | トランスポート処理 |
| RestUpdateByQueryAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java` | ソース | REST APIハンドラー |
| AbstractAsyncBulkByScrollAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java` | ソース | 共通処理 |
| BulkByScrollResponse.java | `server/src/main/java/org/opensearch/index/reindex/BulkByScrollResponse.java` | ソース | レスポンス |
| BulkByScrollTask.java | `server/src/main/java/org/opensearch/index/reindex/BulkByScrollTask.java` | ソース | タスク管理 |
