# 機能設計書 10-ドキュメントインデックス（登録）

## 概要

本ドキュメントは、OpenSearchにおけるドキュメントインデックス（登録）機能の設計を記述する。JSONドキュメントをインデックスに登録（作成または更新）する機能である。

### 本機能の処理概要

ドキュメントインデックス機能は、指定されたインデックスにJSONドキュメントを登録する。IDが指定されていない場合は自動生成される。OpTypeにより新規作成（CREATE）と作成/更新（INDEX）の動作を切り替える。実装上、TransportIndexActionは非推奨（Deprecated）であり、内部的にTransportBulkActionに委譲される。

**業務上の目的・背景**：OpenSearchの最も基本的なデータ書き込み操作であり、検索可能なドキュメントをインデックスに格納する。全てのデータ投入操作の基盤となる。

**機能の利用シーン**：個別ドキュメントの登録、ドキュメントの作成（PUT with _create）、ドキュメントの上書き更新、バルク操作の内部処理としての利用。

**主要な処理内容**：
1. IndexRequestの生成とバリデーション
2. TransportBulkActionへの委譲（単一アイテムのBulkRequestとしてラップ）
3. インジェストパイプラインの適用（設定されている場合）
4. プライマリシャードへの書き込みとレプリカへのレプリケーション
5. IndexResponseの返却

**関連システム・外部連携**：インジェストパイプライン機能と連携し、ドキュメント登録前にパイプライン処理を適用する。マッピング管理機能と連携し、フィールドの自動マッピングを行う。

**権限による制御**：`indices:data/write/index`アクション権限が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | ドキュメント登録・更新 | 主画面 | インデックスにドキュメントを作成または更新する主処理 |
| 4 | ドキュメント作成 | 主画面 | 新規ドキュメントを作成する処理（既存ID時は409エラー） |
| 10 | ドキュメント更新 | 主画面 | スクリプトまたは部分ドキュメントでドキュメントを更新する処理 |

## 機能種別

データ書き込み（Write）/ レプリケーション対象操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| index | String | Yes | 対象インデックス名 | - |
| id | String | No | ドキュメントID | 未指定時は自動生成。指定時はDocWriteRequest.validateDocIdLength適用 |
| routing | String | No | シャードルーティング値 | 空文字列はnullに変換 |
| source | BytesReference | Yes | ドキュメントソース（JSON） | 空でないこと |
| contentType | MediaType | Yes | ソースのコンテンツタイプ | 空でないこと |
| opType | OpType | No | 操作タイプ（INDEX/CREATE） | INDEX（デフォルト）またはCREATEのみ |
| version | long | No | バージョン値 | id未指定時はINTERNALバージョニングかつMATCH_ANY/MATCH_DELETEDのみ |
| versionType | VersionType | No | バージョニングタイプ | デフォルトINTERNAL。CREATEの場合INTERNALのみ |
| pipeline | String | No | インジェストパイプライン名 | 空文字列不可 |
| finalPipeline | String | No | 最終インジェストパイプライン名 | 空文字列不可 |
| ifSeqNo | long | No | 楽観的並行制御のシーケンス番号 | CREATEの場合指定不可 |
| ifPrimaryTerm | long | No | 楽観的並行制御のプライマリターム | CREATEの場合指定不可 |
| requireAlias | boolean | No | エイリアス経由の書き込みを必須とする | デフォルトfalse |
| timeout | TimeValue | No | タイムアウト | - |
| waitForActiveShards | ActiveShardCount | No | アクティブシャード待機数 | - |
| refresh | RefreshPolicy | No | リフレッシュポリシー | NONE/IMMEDIATE/WAIT_UNTIL |

### 入力データソース

REST API（PUT /{index}/_doc/{id}、POST /{index}/_doc、PUT /{index}/_create/{id}）。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _index | String | インデックス名 |
| _id | String | ドキュメントID |
| _version | long | ドキュメントバージョン |
| result | Result | CREATED または UPDATED |
| _seq_no | long | シーケンス番号 |
| _primary_term | long | プライマリターム |
| _shards | ShardInfo | シャード情報（total, successful, failed） |
| forced_refresh | boolean | 強制リフレッシュが発生したか |

### 出力先

REST APIレスポンス（JSON形式）。resultがCREATEDの場合はHTTP 201、UPDATEDの場合はHTTP 200。

## 処理フロー

### 処理シーケンス

```
1. REST APIリクエスト受信
   └─ IndexRequestの生成
2. バリデーション
   └─ source, contentType必須チェック
   └─ opType/version/versionType整合性チェック
   └─ ifSeqNo/ifPrimaryTerm整合性チェック
   └─ pipeline空文字列チェック
   └─ DocWriteRequest.validateDocIdLength
3. TransportIndexAction.doExecute()
   └─ TransportSingleItemBulkWriteAction.toSingleItemBulkRequest()
   └─ TransportBulkAction.execute()に委譲
4. （BulkAction内部）
   └─ パイプライン解決
   └─ インデックス解決（日付数式展開、エイリアス解決）
   └─ インデックスの自動作成（必要な場合）
   └─ プライマリシャードへのルーティング
   └─ シャードレベルでのドキュメント書き込み
   └─ レプリカへのレプリケーション
5. BulkResponseからIndexResponseの抽出
   └─ TransportSingleItemBulkWriteAction.wrapBulkResponse()
```

### フローチャート

```mermaid
flowchart TD
    A[REST API リクエスト受信] --> B[IndexRequest バリデーション]
    B -->|バリデーションエラー| ERR1[ActionRequestValidationException]
    B -->|OK| C[TransportIndexAction.doExecute]
    C --> D[toSingleItemBulkRequest]
    D --> E[TransportBulkAction.execute]
    E --> F[パイプライン解決]
    F --> G[インデックス解決]
    G --> H{インデックス存在?}
    H -->|存在しない & autoCreate=true| I[インデックス自動作成]
    H -->|存在する| J[プライマリシャードへルーティング]
    I --> J
    J --> K[ドキュメント書き込み]
    K --> L[レプリカレプリケーション]
    L --> M[BulkResponse]
    M --> N[wrapBulkResponse]
    N --> O[IndexResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ソース必須 | ドキュメントソースは必須 | 常時 |
| BR-02 | コンテンツタイプ必須 | contentTypeは必須 | 常時 |
| BR-03 | OpType制限 | opTypeはINDEXまたはCREATEのみ | 常時 |
| BR-04 | CREATE時バージョニング制限 | opType=CREATEの場合、versionTypeはINTERNALのみ | CREATE時 |
| BR-05 | CREATE時CAS制限 | opType=CREATEの場合、ifSeqNo/ifPrimaryTermは指定不可 | CREATE時 |
| BR-06 | CREATE時バージョン自動解決 | opType=CREATEかつversion=MATCH_ANYの場合、MATCH_DELETEDに自動変換 | CREATE時 |
| BR-07 | ID未指定時の制約 | ID未指定の場合、versionTypeはINTERNAL、versionはMATCH_ANY/MATCH_DELETEDのみ | ID未指定時 |
| BR-08 | ID自動生成 | IDが未指定の場合、process()メソッドで自動生成（RequestUtils.generateID） | ID未指定時 |
| BR-09 | ルーティング必須チェック | マッピングでroutingRequiredの場合、routing未指定はRoutingMissingException | ルーティング必須マッピング時 |
| BR-10 | パイプライン空文字列不可 | pipelineおよびfinalPipelineに空文字列は不可 | パイプライン指定時 |
| BR-11 | データストリーム対応 | includeDataStreams()がtrueを返し、データストリームへの書き込みをサポート | 常時 |
| BR-12 | BulkAction委譲 | TransportIndexActionは非推奨であり、TransportBulkActionに委譲 | 常時 |

### 計算ロジック

| ロジック名 | 内容 | ソース |
|-----------|------|--------|
| バージョン解決 | opType=CREATEかつversion=MATCH_ANY -> MATCH_DELETED | IndexRequest.java 560-566行目 |
| ルーティング解決 | metadata.resolveWriteIndexRouting(routing, index) | IndexRequest.java 651-653行目 |

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| INDEX | Luceneインデックス | INSERT/UPDATE | ドキュメントの作成または更新 |
| CREATE | Luceneインデックス | INSERT | ドキュメントの作成のみ（既存時エラー） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | source未指定 | ソースを指定 |
| 400 | ActionRequestValidationException | contentType未指定 | コンテンツタイプを指定 |
| 400 | ActionRequestValidationException | CREATE時にINTERNAL以外のversionType | versionTypeをINTERNALにするかopTypeをINDEXに変更 |
| 400 | ActionRequestValidationException | CREATE時にifSeqNo/ifPrimaryTerm指定 | CAS条件を削除するかopTypeをINDEXに変更 |
| 400 | ActionRequestValidationException | ID未指定でバージョニング指定 | IDを指定 |
| 400 | ActionRequestValidationException | pipeline/finalPipelineに空文字列 | パイプライン名を正しく指定 |
| 400 | IllegalArgumentException | opTypeがINDEX/CREATE以外 | 正しいopTypeを指定 |
| 400 | IllegalArgumentException | IDが空文字列 | IDを指定するか未指定にする |
| 400 | RoutingMissingException | routingRequired=trueのマッピングでrouting未指定 | routingを指定 |
| 409 | VersionConflictEngineException | CREATE時に既存ドキュメントが存在 | 別IDを使用するかopTypeをINDEXに変更 |
| 409 | VersionConflictEngineException | ifSeqNo/ifPrimaryTerm不一致 | 最新のseqNo/primaryTermを取得して再試行 |

### リトライ仕様

TransportBulkActionの標準リトライ機構が適用される。isRetryフラグにより、リトライ時の重複チェックが可能。

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

ドキュメント書き込みはプライマリシャードで実行後、レプリカシャードにレプリケーションされる。waitForActiveShardsにより、指定数のシャードへの書き込み完了を待機可能。

## パフォーマンス要件

- 個別ドキュメントの登録はTransportBulkActionに委譲されるため、バルク操作と同じパス
- 大量のドキュメント登録にはBulk APIの使用を推奨
- RefreshPolicy.IMMEDIATEの指定はパフォーマンスに影響するため注意

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

- 書き込み: `indices:data/write/index`
- データストリーム書き込み: `indices:data/write/index` + データストリーム対応
- requireAlias=true設定により、エイリアス経由以外の直接書き込みを防止可能

## 備考

- TransportIndexActionは`@Deprecated`であり、TransportSingleItemBulkWriteActionを介してTransportBulkActionに委譲される
- IndexAction.NAMEは`indices:data/write/index`
- IndexRequestはReplicatedWriteRequest、DocWriteRequest、CompositeIndicesRequestを実装
- IndicesOptionsはstrictSingleIndexNoExpandForbidClosed（254-256行目）
- ID自動生成時にautoGeneratedTimestampが設定される（645行目）
- SHALLOW_SIZEによるメモリ使用量推定がサポートされている（99行目）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | IndexRequest.java | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` | リクエスト構造。主要フィールド: id(110行目), routing(112行目), source(114行目), opType(116行目), version(118行目), versionType(119行目), pipeline(123行目), finalPipeline(124行目), requireAlias(129行目), ifSeqNo(140行目), ifPrimaryTerm(141行目) |
| 1-2 | IndexResponse.java | `server/src/main/java/org/opensearch/action/index/IndexResponse.java` | レスポンス構造。DocWriteResponseを継承。status()でCREATED時は201返却(82-84行目) |
| 1-3 | IndexAction.java | `server/src/main/java/org/opensearch/action/index/IndexAction.java` | アクション定義。NAME="indices:data/write/index"(45行目) |

#### Step 2: バリデーションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | IndexRequest.java validate() | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` (195-251行目) | バリデーションロジック全体 |

**主要バリデーション**:
- **197-198行目**: source null チェック
- **200-201行目**: contentType null チェック
- **205-228行目**: OpType.CREATE時の制約（versionType=INTERNALのみ、バージョン明示指定不可、ifSeqNo/ifPrimaryTerm不可）
- **231-236行目**: ID未指定時のバージョニング制約
- **238行目**: DocWriteRequest.validateSeqNoBasedCASParams
- **240行目**: DocWriteRequest.validateDocIdLength
- **242-248行目**: pipeline/finalPipelineの空文字列チェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportIndexAction.java | `server/src/main/java/org/opensearch/action/index/TransportIndexAction.java` | 非推奨のエントリーポイント。TransportSingleItemBulkWriteActionを継承(57行目) |
| 3-2 | TransportSingleItemBulkWriteAction.java | `server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java` | 単一アイテムのBulkActionへの委譲ロジック |

**主要処理フロー（TransportSingleItemBulkWriteAction）**:
- **80-81行目**: doExecute() -- toSingleItemBulkRequestでBulkRequestに変換後、bulkAction.executeに委譲
- **104-112行目**: toSingleItemBulkRequest() -- BulkRequest構築。RefreshPolicy/timeout/waitForActiveShardsの転送
- **89-102行目**: wrapBulkResponse() -- BulkResponseから単一アイテムのレスポンスを抽出

#### Step 4: ドキュメント処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | IndexRequest.java process() | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` (628-648行目) | ドキュメント前処理。ルーティング必須チェック、ID自動生成 |
| 4-2 | IndexRequest.java resolveRouting() | `server/src/main/java/org/opensearch/action/index/IndexRequest.java` (651-653行目) | ルーティング解決 |

**主要処理**:
- **631-633行目**: routingRequired()かつrouting==nullでRoutingMissingException
- **636-637行目**: IDが空文字列の場合IllegalArgumentException
- **641-647行目**: ID未指定時の自動生成。autoGeneratedTimestamp設定、RequestUtils.generateID()

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

```
REST API (PUT /{index}/_doc/{id})
    |
    +-- IndexRequest生成 + validate()
          |
          +-- TransportIndexAction (Deprecated)
                |
                +-- TransportSingleItemBulkWriteAction.doExecute() (80行目)
                      |
                      +-- toSingleItemBulkRequest() (104行目)
                      |     +-- BulkRequest構築
                      |     +-- RefreshPolicy/timeout/waitForActiveShards転送
                      |
                      +-- TransportBulkAction.execute()
                      |     +-- パイプライン解決
                      |     +-- インデックス解決（日付数式、エイリアス）
                      |     +-- インデックス自動作成（必要時）
                      |     +-- プライマリシャード書き込み
                      |     +-- レプリカレプリケーション
                      |
                      +-- wrapBulkResponse() (89行目)
                            +-- BulkResponse -> IndexResponse抽出
```

### データフロー図

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

IndexRequest -----------> TransportIndexAction               IndexResponse
 (index, id,               |                                  (_index, _id,
  source, opType,           +-> TransportSingleItemBulk         _version,
  version, routing,              WriteAction                     result,
  pipeline, ...)                 |                               _seq_no,
                                 +-> toSingleItemBulkRequest()   _primary_term,
                                 |    (BulkRequest構築)           _shards)
                                 +-> TransportBulkAction
                                 |    +-> パイプライン適用
                                 |    +-> シャード書き込み
                                 |    +-> レプリケーション
                                 +-> wrapBulkResponse()
                                      (BulkResponse -> IndexResponse)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| IndexAction.java | `server/src/main/java/org/opensearch/action/index/IndexAction.java` | ソース | アクション定義 |
| 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` | ソース | トランスポートアクション（非推奨） |
| TransportSingleItemBulkWriteAction.java | `server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java` | ソース | 単一アイテムBulk委譲 |
| TransportBulkAction.java | `server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java` | ソース | 実際の書き込み処理 |
| DocWriteRequest.java | `server/src/main/java/org/opensearch/action/DocWriteRequest.java` | ソース | ドキュメント書き込みリクエストインターフェース |
| DocWriteResponse.java | `server/src/main/java/org/opensearch/action/DocWriteResponse.java` | ソース | ドキュメント書き込みレスポンス基底クラス |
