# 画面設計書 3-ドキュメント登録・更新

## 概要

OpenSearchインデックスにドキュメントを作成または更新するREST APIエンドポイント（PUT/POST /{index}/_doc/{id}）の設計書である。指定したインデックスにJSON形式のドキュメントを登録し、既存のドキュメントがある場合は上書き更新を行う。

### 本画面の処理概要

本APIは、OpenSearchインデックスに対してドキュメントの作成または更新（インデックス操作）を実行する機能を提供する。

**業務上の目的・背景**：検索エンジンの基盤機能として、データの格納は最も重要な操作の一つである。アプリケーションが生成するデータ、ログ、メトリクスなどをインデックスに登録し、後から検索・分析できるようにする。ドキュメントIDが指定されている場合は冪等な操作となり、同一IDに対する再実行は更新として扱われる。

**画面へのアクセス方法**：HTTPクライアントからPUTまたはPOSTメソッドで `/{index}/_doc/{id}` にリクエストを送信する。IDなしの場合は `POST /{index}/_doc` で自動ID採番される。

**主要な操作・処理内容**：
1. クライアントからPUT/POSTリクエストとJSONドキュメントを受信する
2. インデックス名、ドキュメントID、各種パラメータを解析する
3. インジェストパイプラインが指定されている場合はドキュメントの前処理を実行する
4. ドキュメントをインデックスに書き込む（新規作成または更新）
5. 結果（作成/更新の区別、バージョン、シーケンス番号等）をJSON形式で返却する

**画面遷移**：登録後のドキュメントはドキュメント取得API（GET /{index}/_doc/{id}）で取得可能。リフレッシュ後は検索API（GET /{index}/_search）で検索可能になる。

**権限による表示制御**：インデックスへの書き込み権限が必要。セキュリティプラグイン使用時はインデックス単位のアクセス制御が適用される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | ドキュメントインデックス（登録） | 主機能 | インデックスにドキュメントを作成または更新する主処理 |
| 36 | インジェストパイプライン | 補助機能 | ドキュメント登録前のインジェストパイプライン適用処理 |
| 17 | マッピング管理 | 補助機能 | ドキュメント登録時のフィールドマッピング自動検出・適用 |

## 画面種別

登録・更新（更新系API）

## URL/ルーティング

| メソッド | パス | 説明 |
|---------|------|------|
| PUT | `/{index}/_doc/{id}` | 指定IDでドキュメントを作成または更新する |
| POST | `/{index}/_doc/{id}` | 指定IDでドキュメントを作成または更新する |
| POST | `/{index}/_doc` | 自動生成IDでドキュメントを作成する |

## 入出力項目

### パスパラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|------|------|
| index | string | はい | インデックス名 |
| id | string | いいえ | ドキュメントID（省略時は自動生成） |

### クエリパラメータ

| パラメータ名 | 型 | 必須 | デフォルト | 説明 |
|-------------|-----|------|----------|------|
| wait_for_active_shards | string | いいえ | 1 | 処理前にアクティブである必要があるシャードコピー数。`all` で全コピー |
| op_type | enum (index, create) | いいえ | index（ID指定時）/ create（ID省略時） | 操作タイプ。`create` の場合、既存IDで409エラー |
| refresh | enum (true, false, wait_for) | いいえ | false | trueでリフレッシュ実行、wait_forでリフレッシュ待機 |
| routing | string | いいえ | - | ルーティング値 |
| timeout | time | いいえ | 1m | 操作タイムアウト |
| version | number | いいえ | - | 楽観的排他制御用バージョン番号 |
| version_type | enum (internal, external, external_gte) | いいえ | internal | バージョンタイプ |
| if_seq_no | number | いいえ | - | 楽観的排他制御用シーケンス番号 |
| if_primary_term | number | いいえ | - | 楽観的排他制御用プライマリターム |
| pipeline | string | いいえ | - | 前処理パイプラインID |
| require_alias | boolean | いいえ | false | trueの場合、宛先がエイリアスであることを要求する |

### リクエストボディ

| フィールド | 型 | 必須 | 説明 |
|-----------|-----|------|------|
| (ドキュメント本体) | object | はい | JSON形式のドキュメント |

### レスポンスボディ

| フィールド名 | 型 | 説明 |
|-------------|-----|------|
| _index | string | インデックス名 |
| _id | string | ドキュメントID |
| _version | number | ドキュメントバージョン |
| result | string | 操作結果（"created" または "updated"） |
| _shards | object | シャード情報 |
| _shards.total | number | 総シャード数 |
| _shards.successful | number | 成功シャード数 |
| _shards.failed | number | 失敗シャード数 |
| _seq_no | number | シーケンス番号 |
| _primary_term | number | プライマリターム |

## 表示項目

レスポンスのJSON全フィールドが表示項目となる（上記レスポンスボディの項目を参照）。

## イベント仕様

### 1-ドキュメント登録（ID指定）

1. PUT/POST `/{index}/_doc/{id}` リクエストを受信する
2. `RestIndexAction.prepareRequest()` がリクエストパラメータを解析する
3. `IndexRequest` オブジェクトを構築する
4. `client.index()` でインデックス操作を実行する
5. `IndexResponse` を受け取り、HTTPステータスとともにJSON形式で返却する
   - 新規作成時：201 Created
   - 更新時：200 OK

### 2-ドキュメント登録（自動ID）

1. POST `/{index}/_doc` リクエストを受信する
2. `RestIndexAction.AutoIdHandler.prepareRequest()` がリクエストを処理する
3. op_typeがデフォルトで "create" に設定される
4. 以降はID指定時と同様の処理フロー

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ドキュメント登録（新規） | 指定インデックス | INSERT | 新しいドキュメントをインデックスに追加する |
| ドキュメント登録（更新） | 指定インデックス | UPDATE | 既存ドキュメントを上書き更新する |

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

#### 指定インデックス

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | _source | リクエストボディのJSONドキュメント | ドキュメント本体 |
| INSERT/UPDATE | _id | パスパラメータのid、または自動生成UUID | ドキュメント識別子 |
| INSERT/UPDATE | _version | インクリメントされたバージョン番号 | 楽観的排他制御用 |
| INSERT/UPDATE | _seq_no | シーケンス番号 | 操作の順序管理 |
| INSERT/UPDATE | _primary_term | プライマリターム | プライマリシャードの世代管理 |
| INSERT/UPDATE | _routing | routingパラメータの値 | シャードルーティング |

## メッセージ仕様

| メッセージ種別 | HTTPステータス | 条件 | メッセージ内容 |
|--------------|--------------|------|-------------|
| 成功（作成） | 201 Created | 新規ドキュメント作成時 | result: "created" |
| 成功（更新） | 200 OK | 既存ドキュメント更新時 | result: "updated" |
| バージョン競合 | 409 Conflict | op_type=create で既存ID指定時、またはif_seq_no/if_primary_term不一致時 | VersionConflictEngineException |
| インデックス未存在 | 404 Not Found | 存在しないインデックスへの登録（auto_create_index無効時） | index_not_found_exception |
| バリデーションエラー | 400 Bad Request | リクエストボディが不正な場合 | パース・バリデーションエラーメッセージ |

## 例外処理

| 例外 | HTTPステータス | 条件 | レスポンス |
|------|--------------|------|----------|
| VersionConflictEngineException | 409 | バージョン競合時 | バージョン競合の詳細メッセージ |
| MapperParsingException | 400 | ドキュメントのフィールドがマッピングに合致しない場合 | マッピングエラーの詳細 |
| IndexNotFoundException | 404 | インデックスが存在せず自動作成も無効の場合 | インデックス未存在メッセージ |
| ClusterBlockException | 403 | インデックスが読み取り専用の場合 | ブロック状態メッセージ |

## 備考

- IDを指定しない場合（POST /{index}/_doc）はAutoIdHandlerが処理し、op_typeがデフォルトで "create" になる。
- インジェストパイプラインが設定されている場合、ドキュメントはインデックスに書き込まれる前にパイプラインで前処理される。
- `require_alias` を true にすると、宛先がエイリアスでない場合にエラーとなる。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | IndexRequest.java | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` | リクエストのフィールド（index, id, routing, source, pipeline等）を理解する |
| 1-2 | IndexResponse.java | `server/src/main/java/org/opensearch/action/index/IndexResponse.java` | レスポンス構造（ShardId, id, seqNo, primaryTerm, version, result）を理解する |

**読解のコツ**: IndexRequestはDocWriteRequestを継承しており、共通的な書き込みパラメータ（routing, version, versionType等）は親クラスに定義されている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestIndexAction.java | `server/src/main/java/org/opensearch/rest/action/document/RestIndexAction.java` | ルーティング定義（65行目）、パラメータ解析（139-159行目）、クライアント実行（161-164行目）を確認する |
| 2-2 | RestIndexAction.CreateHandler | 同上ファイル内 (78-102行目) | _create パスの処理。op_typeを "create" に強制する |
| 2-3 | RestIndexAction.AutoIdHandler | 同上ファイル内 (109-136行目) | 自動ID生成パスの処理 |

**主要処理フロー**:
1. **140行目**: IndexRequestをインデックス名で初期化
2. **141-151行目**: パスパラメータ・クエリパラメータの解析とセット
3. **152-159行目**: op_typeとwait_for_active_shardsの設定
4. **161-164行目**: `client.index()` で非同期にインデックス操作を実行

#### Step 3: トランスポート層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportIndexAction.java | `server/src/main/java/org/opensearch/action/index/TransportIndexAction.java` | インデックス操作のトランスポート層処理を確認する |

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

```
RestIndexAction.prepareRequest()
    |
    +-- IndexRequest 構築（パラメータ解析）
    |
    +-- client.index(indexRequest, listener)
            |
            +-- TransportIndexAction
                    |
                    +-- TransportBulkAction（単一操作もBulk経由）
                            |
                            +-- IngestService（パイプライン適用）
                            |
                            +-- TransportShardBulkAction
                                    |
                                    +-- InternalEngine.index()
                                            |
                                            +-- Lucene IndexWriter
```

### データフロー図

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

PUT /{index}/_doc/{id}  --> RestIndexAction              --> HTTP 201/200
+ JSON Body                   |                              JSON Response:
                              v                              {
                        IndexRequest構築                       "_index": "...",
                              |                                "_id": "...",
                              v                                "_version": N,
                        IngestService                          "result": "created/updated",
                        (パイプライン)                           "_shards": {...},
                              |                                "_seq_no": N,
                              v                                "_primary_term": N
                        TransportBulkAction                  }
                              |
                              v
                        Lucene IndexWriter
                        (インデックス書込)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestIndexAction.java | `server/src/main/java/org/opensearch/rest/action/document/RestIndexAction.java` | ソース | RESTハンドラ（エントリーポイント） |
| IndexRequest.java | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` | ソース | リクエストオブジェクト |
| IndexResponse.java | `server/src/main/java/org/opensearch/action/index/IndexResponse.java` | ソース | レスポンスオブジェクト |
| TransportIndexAction.java | `server/src/main/java/org/opensearch/action/index/TransportIndexAction.java` | ソース | トランスポートアクション |
| TransportBulkAction.java | `server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java` | ソース | バルク操作トランスポート（単一操作もBulk経由） |
| index.json | `rest-api-spec/src/main/resources/rest-api-spec/api/index.json` | 設定 | REST API仕様定義 |
| RestIndexActionTests.java | `server/src/test/java/org/opensearch/rest/action/document/RestIndexActionTests.java` | テスト | ユニットテスト |
