# 画面設計書 15-クエリによる更新

## 概要

本ドキュメントは、OpenSearchのUpdate By Query API（`_update_by_query`）エンドポイントに関する画面設計書である。検索クエリに一致するドキュメントを一括で更新するためのREST APIインターフェースを定義する。

### 本画面の処理概要

本APIは、Query DSLで指定した条件に一致するドキュメントを一括更新する機能を提供する。スクリプトによるフィールド値の変更や、マッピング変更の反映（ドキュメントの再インデックス）に使用される。内部的にはスクロール検索+バルク更新のパターンで実行される。

**業務上の目的・背景**：大量のドキュメントに対して同一の更新操作を適用する場合（例：フィールド値の一括修正、マッピング変更後のドキュメント再インデックス、スクリプトによる計算フィールドの一括更新）、個別のUpdate APIを繰り返すのは非効率である。Update By Query APIにより、条件に一致する全ドキュメントを効率的に更新でき、インジェストパイプラインの指定も可能である。

**画面へのアクセス方法**：HTTPクライアントから`POST /{index}/_update_by_query`エンドポイントにリクエストを送信する。Query DSLとオプションのスクリプトをリクエストボディに含める。

**主要な操作・処理内容**：
1. リクエストボディのQuery DSLを解析し、更新対象ドキュメントを特定するSearchRequestを構築する
2. オプションのスクリプトをパースし、UpdateByQueryRequestに設定する
3. スクロール検索により対象ドキュメントを段階的に取得する
4. 取得したドキュメントに対してバルク更新操作を実行する（スクリプトまたはマッピング再適用）
5. インジェストパイプラインが指定されている場合、更新前にパイプラインを適用する

**画面遷移**：クエリによる更新スロットル変更（No.16）と連携して実行中タスクのスロットルを変更できる。検索（No.21）と同じQuery DSLを使用する。タスク取得（No.113）やタスクキャンセル（No.114）との連携で非同期タスクの管理が可能。

**権限による表示制御**：対象インデックスに対するindex権限およびread権限が必要。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | Update By Query | 主機能 | クエリ条件に一致するドキュメントを一括更新する主処理 |
| 19 | 全文検索 | 補助機能 | 更新対象ドキュメントを特定するためのクエリ実行 |

## 画面種別

API（条件指定一括更新）

## URL/ルーティング

| メソッド | パス | 説明 |
|---------|------|------|
| POST | `/{index}/_update_by_query` | 指定インデックスのクエリ一致ドキュメントを更新 |

## 入出力項目

### URLパスパラメータ

| パラメータ | 型 | 必須 | 説明 |
|-----------|------|------|------|
| index | list | はい | 検索対象インデックス名のカンマ区切りリスト |

### クエリパラメータ

| パラメータ | 型 | 必須 | デフォルト | 説明 |
|-----------|------|------|-----------|------|
| analyzer | string | いいえ | - | クエリ文字列のアナライザ |
| analyze_wildcard | boolean | いいえ | false | ワイルドカード・プレフィクスクエリを解析するかどうか |
| default_operator | enum (AND/OR) | いいえ | OR | クエリ文字列クエリのデフォルト演算子 |
| df | string | いいえ | - | デフォルトフィールド |
| from | number | いいえ | 0 | 開始オフセット |
| ignore_unavailable | boolean | いいえ | - | 利用不可のインデックスを無視するかどうか |
| allow_no_indices | boolean | いいえ | - | ワイルドカード未解決時に無視するかどうか |
| conflicts | enum (abort/proceed) | いいえ | abort | バージョンコンフリクト時の動作 |
| expand_wildcards | enum | いいえ | open | ワイルドカードの展開方法 |
| lenient | boolean | いいえ | - | 型不一致クエリエラーを無視するかどうか |
| pipeline | string | いいえ | - | 更新時に適用するインジェストパイプラインID |
| preference | string | いいえ | random | 操作を実行するノードまたはシャードの指定 |
| q | string | いいえ | - | Luceneクエリ文字列 |
| routing | list | いいえ | - | ルーティング値のカンマ区切りリスト |
| scroll | time | いいえ | - | スクロール検索の一貫性ビュー維持時間 |
| search_type | enum | いいえ | - | 検索操作タイプ |
| search_timeout | time | いいえ | - | 各検索リクエストのタイムアウト |
| max_docs | number | いいえ | 全件 | 処理する最大ドキュメント数 |
| sort | list | いいえ | - | ソート順 |
| terminate_after | number | いいえ | - | 各シャードの最大収集ドキュメント数 |
| stats | list | いいえ | - | ログ・統計用リクエストタグ |
| version | boolean | いいえ | - | ドキュメントバージョンを返すかどうか |
| version_type | boolean | いいえ | - | ヒット時にバージョン番号をインクリメントするかどうか |
| request_cache | boolean | いいえ | - | リクエストキャッシュの使用 |
| refresh | boolean | いいえ | - | 影響インデックスのリフレッシュ |
| timeout | time | いいえ | 1m | 各バルクリクエストのタイムアウト |
| wait_for_active_shards | string | いいえ | 1 | アクティブシャードコピー数の要件 |
| scroll_size | number | いいえ | 100 | スクロール検索のバッチサイズ |
| wait_for_completion | boolean | いいえ | true | 完了までブロックするかどうか |
| requests_per_second | number | いいえ | 0 (無制限) | スロットル値（-1でスロットルなし） |
| slices | number/string | いいえ | 1 | タスク分割スライス数 |

### リクエストボディ

| フィールド | 型 | 必須 | 説明 |
|-----------|------|------|------|
| query | object | いいえ | Query DSLによる更新対象の検索条件 |
| script | object | いいえ | 更新スクリプト（Painless等） |
| conflicts | string | いいえ | バージョンコンフリクト時の動作 |
| max_docs | number | いいえ | 処理する最大ドキュメント数 |

## 表示項目

### レスポンスボディ

| フィールド | 型 | 説明 |
|-----------|------|------|
| took | number | 処理にかかった時間（ミリ秒） |
| timed_out | boolean | タイムアウトしたかどうか |
| total | number | 処理されたドキュメントの総数 |
| updated | number | 更新されたドキュメント数 |
| batches | number | スクロール応答のバッチ数 |
| version_conflicts | number | バージョンコンフリクト数 |
| noops | number | 操作なし（no-op）の数 |
| retries | object | リトライ回数（bulk/search別） |
| throttled_millis | number | スロットリングによる待機時間 |
| requests_per_second | number | 実際のリクエスト/秒 |
| failures | array | 失敗情報の配列 |

## イベント仕様

### 1-Update By Queryリクエスト実行

1. `RestUpdateByQueryAction.prepareRequest()`が`doPrepareRequest(request, client, false, true)`を呼び出す（行67-68）。第4引数`includeScript=true`によりスクリプトのサポートが有効化される。
2. `buildRequest()`でUpdateByQueryRequestを構築し、conflicts, script, max_docsのConsumerを設定する（行72-88）
3. `AbstractBulkByQueryRestHandler.parseInternalRequest()`がSearchRequestの構築を実行する
4. `pipeline`パラメータが指定されている場合、`internal.setPipeline()`で設定する（行87）
5. 内部的にスクロール検索で対象ドキュメントを取得し、バルク更新を実行する

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| Update By Queryリクエスト | Luceneインデックス | SELECT | クエリに一致するドキュメントの検索 |
| Update By Queryリクエスト | Luceneインデックス | UPDATE | 一致したドキュメントの更新 |

### テーブル別更新項目詳細

#### Luceneインデックス

| 操作 | 項目 | 更新値・取得条件 | 備考 |
|-----|------|-----------------|------|
| UPDATE | _source | スクリプトによる変更後の値、またはマッピング再適用後の値 | スクリプト未指定時はnoop更新（マッピング再適用のみ） |
| UPDATE | _version | 自動インクリメント | 楽観的排他制御に使用 |

## メッセージ仕様

| メッセージ種別 | 条件 | メッセージ内容 |
|--------------|------|--------------|
| 成功 | 全件更新成功 | HTTPステータス200、failures配列が空 |
| 部分成功 | 一部コンフリクト（conflicts=proceed） | HTTPステータス200、version_conflicts > 0 |
| エラー | コンフリクト（conflicts=abort） | HTTPステータス409、バージョンコンフリクトエラー |
| 非同期 | wait_for_completion=false | HTTPステータス200、タスクIDを含むレスポンス |

## 例外処理

| 例外 | 条件 | 動作 |
|------|------|------|
| VersionConflictEngineException | conflicts=abortでバージョンコンフリクト | 処理中断 |
| ScriptException | スクリプト実行エラー | 該当ドキュメントの更新が失敗 |
| IndexNotFoundException | 指定インデックスが存在しない場合 | リクエスト全体が失敗 |

## 備考

- Delete By Query（No.13）との主な違いは、スクリプトのサポートとpipelineパラメータの存在
- スクリプトを指定しない場合、ドキュメントは現在のマッピングで再インデックスされる（マッピング変更の反映に有効）
- `doPrepareRequest`の第3引数がfalse（supportsIngestPipeline=false for rest params）だが、bodyのpipelineパラメータで指定可能（行87）
- `version_type`パラメータはboolean型で、ヒット時にバージョン番号をインクリメントするかを制御する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | update_by_query.json | `rest-api-spec/src/main/resources/rest-api-spec/api/update_by_query.json` | API定義。Delete By Queryとの差分（pipeline, version_type, script等） |
| 1-2 | UpdateByQueryRequest.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/UpdateByQueryRequest.java` | リクエストデータ構造。scriptフィールドに注目 |

**読解のコツ**: UpdateByQueryRequestとDeleteByQueryRequestは共にAbstractBulkByScrollRequestを継承している。共通部分と差分を比較することで理解が深まる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestUpdateByQueryAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java` | RESTハンドラ |

**主要処理フロー**:
1. **行57-58**: routes()でPOST /{index}/_update_by_queryのルート定義
2. **行67-68**: prepareRequest()でdoPrepareRequest(request, client, false, true)を呼び出し。includeScript=true
3. **行72-88**: buildRequest()でUpdateByQueryRequestを構築。conflicts, script, max_docsのConsumer定義
4. **行82**: Script.parse(o)によるスクリプトのパース
5. **行87**: pipeline パラメータの設定

#### Step 3: 共通処理層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractBulkByQueryRestHandler.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java` | 共通リクエスト解析ロジック |

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

```
RestUpdateByQueryAction.prepareRequest()                    [行67]
    |
    +-- doPrepareRequest(request, client, false, true)
            |
            +-- buildRequest()                              [行72]
            |       +-- UpdateByQueryRequest()
            |       +-- consumers: conflicts, script, max_docs
            |       +-- parseInternalRequest()              [AbstractBulkByQueryRestHandler]
            |       +-- setPipeline()                       [行87]
            |
            +-- NodeClient.execute(UpdateByQueryAction.INSTANCE, ...)
                    |
                    +-- TransportUpdateByQueryAction
                            +-- スクロール検索 + バルク更新
```

### データフロー図

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

HTTPリクエスト              RestUpdateByQueryAction              BulkByScrollResponse
(POST /{index}/         リクエスト解析・スクリプト設定
 _update_by_query)              |
   |                            v
   v                  AbstractBulkByQueryRestHandler            レスポンスJSON
Query DSL +            SearchRequest構築                        {took, updated,
Script (optional)               |                                version_conflicts,...}
   |                            v
   v                  TransportUpdateByQueryAction
                      スクロール検索→バルク更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| update_by_query.json | `rest-api-spec/src/main/resources/rest-api-spec/api/update_by_query.json` | API定義 | REST API仕様 |
| RestUpdateByQueryAction.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java` | ソース | RESTハンドラ |
| AbstractBulkByQueryRestHandler.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java` | ソース | 共通リクエスト解析 |
| AbstractBaseReindexRestHandler.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBaseReindexRestHandler.java` | ソース | 共通実行処理 |
| UpdateByQueryRequest.java | `modules/reindex/src/main/java/org/opensearch/index/reindex/UpdateByQueryRequest.java` | ソース | リクエストデータ構造 |
| BulkByScrollResponse.java | `server/src/main/java/org/opensearch/index/reindex/BulkByScrollResponse.java` | ソース | レスポンスデータ構造 |
