# 機能設計書 11-ドキュメント取得

## 概要

本ドキュメントは、OpenSearchの「ドキュメント取得」機能の設計を記述する。IDを指定してインデックスからドキュメント（JSONデータ）を取得するGet API及びMulti Get APIの処理仕様を定義する。

### 本機能の処理概要

本機能は、インデックスに格納されたドキュメントをドキュメントIDを指定して取得する機能である。単一ドキュメント取得（Get API）と複数ドキュメント一括取得（Multi Get API）の2つのAPIを提供する。

**業務上の目的・背景**：検索エンジンとしてだけでなく、ドキュメントストアとしてのOpenSearchにおいて、特定のドキュメントを高速にIDベースで取得する需要がある。検索クエリを発行せずに既知のIDでデータを直接参照することで、レイテンシを最小化する。

**機能の利用シーン**：アプリケーションから特定のドキュメントの詳細を表示する場面、ドキュメントの存在確認を行う場面、複数の既知IDのドキュメントを一括で取得する場面で利用される。REST APIの `GET /{index}/_doc/{id}` エンドポイントや `GET /{index}/_mget` エンドポイントを通じて呼び出される。

**主要な処理内容**：
1. リクエストのバリデーション（インデックス名・ドキュメントIDの必須チェック、バージョン検証）
2. インデックスのルーティング解決（エイリアス解決、ルーティング値によるシャード特定）
3. 対象シャードへのリクエスト転送（セグメントレプリケーション有効時はプライマリシャードへルーティング）
4. シャード上でのドキュメント取得（Luceneインデックスからのリアルタイム取得またはリフレッシュ後取得）
5. レスポンス構築（ドキュメントソース、メタデータ、ストアドフィールドの返却）

**関連システム・外部連携**：本機能はOpenSearch内部のLuceneインデックスに直接アクセスする。外部システムとの連携はREST APIを通じて行われる。

**権限による制御**：アクション名 `indices:data/read/get` に基づくセキュリティプラグインによるアクセス制御が適用される。システムインデックスへのアクセスは `SYSTEM_READ` スレッドプールで実行される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | ドキュメント取得 | 主機能 | IDを指定してドキュメントを取得する主処理 |
| 6 | ドキュメントソース取得 | 主機能 | ドキュメントのソース（_source）のみを返す処理 |
| 7 | ドキュメント存在確認 | 主機能 | ドキュメントがインデックスに存在するかをHEADリクエストで確認する処理 |
| 8 | ドキュメントソース存在確認 | 主機能 | ドキュメントソースが存在するかを確認する処理 |
| 11 | 複数ドキュメント取得 | 主機能 | 1リクエストで複数ドキュメントを一括取得する処理 |

## 機能種別

CRUD操作（Read）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| index | String | Yes | 取得対象のインデックス名 | null不可（validateNonNullIndex） |
| id | String | Yes | 取得対象のドキュメントID | 空文字不可 |
| routing | String | No | シャードルーティング値 | ルーティング必須インデックスでは必須 |
| preference | String | No | シャード選択の優先度（_local, _primary等） | - |
| storedFields | String[] | No | 返却するストアドフィールド名の配列 | - |
| fetchSourceContext | FetchSourceContext | No | _sourceフィールドのフィルタリング設定 | - |
| refresh | boolean | No | 取得前にリフレッシュを実行するか（デフォルト: false） | - |
| realtime | boolean | No | リアルタイム取得を行うか（デフォルト: true） | - |
| version | long | No | 楽観的並行制御のためのバージョン番号 | versionTypeに応じた値検証 |
| versionType | VersionType | No | バージョンの種別（デフォルト: INTERNAL） | - |

### 入力データソース

REST APIリクエスト（HTTP GET/HEAD）、Java Transport Client

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _index | String | ドキュメントが格納されているインデックス名 |
| _id | String | ドキュメントID |
| _version | long | ドキュメントのバージョン番号 |
| _seq_no | long | 最後の操作に割り当てられたシーケンス番号 |
| _primary_term | long | 最後の変更を行ったプライマリのターム |
| found | boolean | ドキュメントが存在するかどうか |
| _source | Map<String, Object> | ドキュメントのソースJSON |
| fields | Map<String, DocumentField> | リクエストされたストアドフィールド |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信・バリデーション
   └─ インデックス名とドキュメントIDの必須チェック、バージョン検証
2. インデックス解決
   └─ エイリアスの解決、具体的なインデックス名の特定
3. ルーティング解決
   └─ メタデータからルーティング値を解決、ルーティング必須チェック
4. シャード選択
   └─ セグメントレプリケーション有効時はプライマリへ、それ以外はpreferenceに従う
5. シャード上での取得処理
   └─ realtime=falseの場合はリフレッシュ待機、refresh=trueの場合はリフレッシュ実行
6. Luceneからのドキュメント読み取り
   └─ ShardGetService.get()によるドキュメント取得
7. レスポンス構築・返却
   └─ GetResult → GetResponse への変換、XContent形式でシリアライズ
```

### フローチャート

```mermaid
flowchart TD
    A[REST API リクエスト受信] --> B{バリデーション OK?}
    B -->|No| C[バリデーションエラー返却]
    B -->|Yes| D[インデックス名解決]
    D --> E[ルーティング解決]
    E --> F{ルーティング必須で未指定?}
    F -->|Yes| G[RoutingMissingException]
    F -->|No| H{セグメントレプリケーション有効?}
    H -->|Yes| I[プライマリシャードへルーティング]
    H -->|No| J[preference に従いシャード選択]
    I --> K[シャードへリクエスト転送]
    J --> K
    K --> L{realtime?}
    L -->|Yes| M[即座にドキュメント取得]
    L -->|No| N[リフレッシュ待機後に取得]
    M --> O[GetResponse 構築]
    N --> O
    O --> P[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-11-01 | リアルタイム取得 | デフォルトでリアルタイム取得が有効であり、リフレッシュを待たずにトランスログからも最新データを取得可能 | realtime=true（デフォルト） |
| BR-11-02 | セグメントレプリケーション時のプライマリルーティング | セグメントレプリケーション有効時、realtime取得でpreferenceが未指定の場合はプライマリシャードへ強制ルーティング | セグレプ有効 AND realtime AND preference==null |
| BR-11-03 | バージョン整合性チェック | 指定バージョンとドキュメントの現在バージョンが一致しない場合はエラー | versionパラメータ指定時 |
| BR-11-04 | ルーティング必須チェック | マッピングでルーティングが必須と設定されたインデックスではrouting未指定時にエラー | routingRequired=true |

### 計算ロジック

特に複雑な計算ロジックはない。シャードの特定はドキュメントIDまたはroutingパラメータのハッシュ値に基づいて行われる。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ドキュメント取得 | Lucene Index（シャード） | SELECT | IDによるドキュメントの読み取り |
| リアルタイム取得 | Translog | SELECT | 未フラッシュのドキュメントをトランスログから取得 |

### テーブル別操作詳細

#### Lucene Index

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | _source, stored fields | _id = リクエストID | バージョン条件付きの場合あり |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | インデックス名またはIDが未指定 | リクエストパラメータを修正 |
| 404 | ドキュメント未検出 | 指定IDのドキュメントが存在しない | found=falseのレスポンスが返却される |
| 400 | RoutingMissingException | ルーティング必須インデックスでrouting未指定 | routingパラメータを指定 |
| 409 | VersionConflictEngineException | バージョン不一致 | 最新バージョンを再取得して再試行 |

### リトライ仕様

リードオペレーションのためリトライは基本的にクライアント側の責務。シャード障害時はレプリカシャードへフェイルオーバーする。

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

読み取り専用操作のためトランザクション制御は不要。リアルタイム取得時はトランスログからの読み取りにより最新の未コミットデータも取得可能。

## パフォーマンス要件

- 単一ドキュメント取得は通常1ms未満のレイテンシを目標とする
- システムインデックスへのアクセスはSYSTEM_READスレッドプールで実行
- 検索スロットルが有効なインデックスはSEARCH_THROTTLEDスレッドプールで実行
- Multi Get APIはシャード単位でリクエストをグループ化し効率的に処理

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

- アクション名 `indices:data/read/get`（単一取得）、`indices:data/read/mget`（複数取得）に対するセキュリティ権限チェックが適用される
- FetchSourceContextによる_sourceフィルタリングでレスポンスに含まれるフィールドを制限可能
- storedFieldsで返却フィールドを制限可能

## 備考

- TransportGetActionはTransportSingleShardActionを継承しており、単一シャードへのルーティングを担当する
- Multi Get APIはTransportMultiGetActionにより処理され、内部でシャード単位にグループ化してTransportShardMultiGetActionを呼び出す
- `@PublicApi(since = "1.0.0")` アノテーションにより公開APIとして安定性が保証されている

---

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

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

### 推奨読解順序

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

まず、リクエスト・レスポンスのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | GetRequest.java | `server/src/main/java/org/opensearch/action/get/GetRequest.java` | 取得リクエストのフィールド定義（id, routing, preference, storedFields, fetchSourceContext, realtime, version, versionType） |
| 1-2 | GetResponse.java | `server/src/main/java/org/opensearch/action/get/GetResponse.java` | レスポンス構造。GetResultをラップし、isExists(), getSource(), getFields()等を提供 |
| 1-3 | MultiGetRequest.java | `server/src/main/java/org/opensearch/action/get/MultiGetRequest.java` | 複数取得リクエスト。内部クラスItemが個別のドキュメント取得設定を保持 |

**読解のコツ**: GetRequestはSingleShardRequest<GetRequest>を継承しており、単一シャードへのルーティングの仕組みを理解する際は親クラスも参照すること。RealtimeRequestインターフェースの実装に注目。

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

処理の起点となるアクション定義とトランスポートアクション。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GetAction.java | `server/src/main/java/org/opensearch/action/get/GetAction.java` | アクション名 `indices:data/read/get` の定義。シングルトンINSTANCE |
| 2-2 | TransportGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportGetAction.java` | Get操作の実際の処理ロジック |

**主要処理フロー**:
1. **89-91行目**: resolveIndex()はtrueを返し、インデックス名の解決を有効にする
2. **96-98行目**: shouldForcePrimaryRouting()でセグメントレプリケーション時のプライマリ強制ルーティング判定
3. **101-117行目**: shards()メソッドでリクエストの転送先シャードを決定
4. **120-127行目**: resolveRequest()でルーティング解決とルーティング必須チェック
5. **130-144行目**: asyncShardOperation()でrealtimeかどうかに応じた非同期処理
6. **147-165行目**: shardOperation()がシャード上での実際の取得処理。refresh処理後、indexShard.getService().get()を呼び出し

#### Step 3: Multi Get処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportMultiGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java` | Multi Get APIの処理。リクエスト内のアイテムをシャード単位にグループ化 |
| 3-2 | TransportShardMultiGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java` | シャード単位の複数ドキュメント取得処理 |

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

```
REST API (GET /{index}/_doc/{id})
    |
    +-- TransportGetAction (extends TransportSingleShardAction)
           |
           +-- resolveRequest()  ... ルーティング解決
           +-- shards()          ... シャード選択（セグレプ考慮）
           +-- asyncShardOperation() / shardOperation()
                  |
                  +-- IndexShard.getService().get()
                         |
                         +-- ShardGetService.get()
                                |
                                +-- Lucene Index / Translog 読み取り

REST API (GET /{index}/_mget)
    |
    +-- TransportMultiGetAction
           |
           +-- シャード単位にアイテムをグループ化
           +-- TransportShardMultiGetAction (シャードごとの処理)
                  |
                  +-- IndexShard.getService().get() (各アイテム)
```

### データフロー図

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

GetRequest          -->  TransportGetAction              -->  GetResponse
 - index                  - resolveRequest()                   - _index
 - id                     - shards()                           - _id
 - routing                - shardOperation()                   - _version
 - preference              |                                   - _seq_no
 - storedFields            +-> IndexShard                      - _primary_term
 - fetchSourceContext          .getService()                   - found
 - realtime                    .get()                          - _source
 - version                                                     - fields
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| GetAction.java | `server/src/main/java/org/opensearch/action/get/GetAction.java` | ソース | アクション型定義 |
| GetRequest.java | `server/src/main/java/org/opensearch/action/get/GetRequest.java` | ソース | リクエストデータ構造 |
| GetResponse.java | `server/src/main/java/org/opensearch/action/get/GetResponse.java` | ソース | レスポンスデータ構造 |
| GetRequestBuilder.java | `server/src/main/java/org/opensearch/action/get/GetRequestBuilder.java` | ソース | リクエストビルダー |
| TransportGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportGetAction.java` | ソース | Get操作のトランスポート処理 |
| MultiGetAction.java | `server/src/main/java/org/opensearch/action/get/MultiGetAction.java` | ソース | Multi Getアクション型定義 |
| MultiGetRequest.java | `server/src/main/java/org/opensearch/action/get/MultiGetRequest.java` | ソース | Multi Getリクエストデータ構造 |
| MultiGetResponse.java | `server/src/main/java/org/opensearch/action/get/MultiGetResponse.java` | ソース | Multi Getレスポンスデータ構造 |
| TransportMultiGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java` | ソース | Multi Get処理のオーケストレーション |
| TransportShardMultiGetAction.java | `server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java` | ソース | シャード単位のMulti Get処理 |
| MultiGetShardRequest.java | `server/src/main/java/org/opensearch/action/get/MultiGetShardRequest.java` | ソース | シャード単位のMulti Getリクエスト |
| MultiGetShardResponse.java | `server/src/main/java/org/opensearch/action/get/MultiGetShardResponse.java` | ソース | シャード単位のMulti Getレスポンス |
| MultiGetItemResponse.java | `server/src/main/java/org/opensearch/action/get/MultiGetItemResponse.java` | ソース | Multi Getの個別アイテムレスポンス |
