# 機能設計書 21-マルチサーチ

## 概要

本ドキュメントは、OpenSearchのMulti Search API機能（マルチサーチ）に関する機能設計書である。複数の検索リクエストを1回のAPIコールで一括実行する機能を定義する。

### 本機能の処理概要

**業務上の目的・背景**：複数の検索クエリを個別にAPIコールすると、ネットワークラウンドトリップが増加しパフォーマンスが低下する。マルチサーチ機能はこれを解決し、1回のHTTPリクエストで複数の検索を同時に実行することで、ネットワーク効率とスループットを大幅に向上させる。ダッシュボード表示等で複数パネルのデータを一括取得する場合に特に有用である。

**機能の利用シーン**：OpenSearch Dashboardsにおける複数のビジュアライゼーション同時表示、バッチ的な検索処理、異なるインデックスに対する複数クエリの一括実行、A/Bテスト的な複数クエリの比較実行などで利用される。

**主要な処理内容**：
1. NDJSON（Newline Delimited JSON）形式のリクエストボディを解析し、メタデータ行と検索ボディ行のペアを各SearchRequestに変換する
2. 並行実行数を制御しながら各検索リクエストを非同期に実行する
3. 全リクエスト完了後、入力順序を維持した結果配列をレスポンスとして返却する
4. 親タスクキャンセル時は残りの検索リクエストをキャンセル済みとしてドレインする

**関連システム・外部連携**：各個別検索はTransportSearchActionを介して実行されるため、検索パイプラインやクロスクラスタ検索の機能がそのまま適用される。

**権限による制御**：各SearchRequestに対して個別のインデックスレベルの権限チェックが行われる。`indices:data/read/msearch`アクション名で権限が制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | 複数検索 | 主画面 | 1リクエストで複数の検索を実行するMulti Search API処理 |
| 27 | 複数検索テンプレート | 参照画面 | 1リクエストで複数の検索テンプレートを実行する処理 |

## 機能種別

データ検索（複数検索の一括実行）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| requests | List<SearchRequest> | Yes | 実行する検索リクエストのリスト | 空リスト不可 |
| max_concurrent_searches | int | No | 最大同時実行数（デフォルト: データノード数 x 検索スレッドプールサイズ） | 1以上の正数 |
| index | String/String[] | No | メタデータ行で指定するインデックス名 | - |
| search_type | String | No | 検索タイプ（query_then_fetch等） | - |
| routing | String | No | ルーティング値 | - |
| preference | String | No | シャード優先指定 | - |
| request_cache | Boolean | No | リクエストキャッシュ利用有無 | - |
| allow_partial_search_results | Boolean | No | 部分結果の許可 | - |
| ccs_minimize_roundtrips | Boolean | No | クロスクラスタ検索のラウンドトリップ最小化 | - |
| cancel_after_time_interval | TimeValue | No | 指定時間後のキャンセル | - |

### 入力データソース

REST APIエンドポイント（`POST /_msearch`, `POST /{index}/_msearch`）からのNDJSON形式リクエストボディ。メタデータ行と検索ボディ行が交互に配置される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| took | long | 全検索の実行時間（ミリ秒） |
| responses | Item[] | 各検索リクエストに対応するレスポンス配列（入力順序を維持） |
| responses[].status | int | HTTPステータスコード |
| responses[].hits | SearchHits | 検索結果ヒット情報 |
| responses[].error | Object | エラー発生時のエラー情報 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. REST層でNDJSON形式のリクエストボディを解析
   └─ メタデータ行と検索ボディ行のペアをSearchRequestリストに変換
2. クラスタブロックチェック（READ）
   └─ グローバルREADブロックが存在する場合は例外を送出
3. 最大同時実行数の決定
   └─ 明示指定がない場合、データノード数 x min(searchThreadPoolSize, 10)
4. SearchRequestSlotキューの構築
   └─ 各SearchRequestにスロット番号を付与してキューに追加
5. 並行検索の開始
   └─ min(リクエスト数, maxConcurrentSearches)個のワーカーを起動
6. 各ワーカーが検索を実行
   └─ キューからリクエストを取得し、NodeClient.searchを呼び出す
7. 結果の収集
   └─ AtomicArrayにスロット順で結果を格納
8. 全検索完了後にMultiSearchResponseを構築して返却
```

### フローチャート

```mermaid
flowchart TD
    A[REST: NDJSONリクエスト受信] --> B[リクエスト解析・SearchRequestリスト構築]
    B --> C{クラスタブロック確認}
    C -->|ブロック有| D[例外送出]
    C -->|ブロック無| E[最大並行数の決定]
    E --> F[SearchRequestSlotキュー構築]
    F --> G[並行ワーカー起動]
    G --> H[各ワーカーがキューからリクエスト取得]
    H --> I[NodeClient.searchで検索実行]
    I --> J{レスポンス受信}
    J --> K[AtomicArrayに結果格納]
    K --> L{全リクエスト完了?}
    L -->|No| M{親タスクキャンセル?}
    M -->|Yes| N[残リクエストをキャンセル済みとしてドレイン]
    M -->|No| H
    L -->|Yes| O[MultiSearchResponse構築・返却]
    N --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-21-01 | レスポンス順序保証 | レスポンスはリクエストの入力順序を維持して返却される | 常時 |
| BR-21-02 | 独立性 | 各検索リクエストは独立して実行され、1つの失敗が他に影響しない | 常時 |
| BR-21-03 | 並行数デフォルト | 未指定時はデータノード数 x min(searchThreadPoolSize, 10) | max_concurrent_searches未指定時 |
| BR-21-04 | キャンセル伝播 | 親タスクキャンセル時は未実行のリクエストをTaskCancelledExceptionで処理 | 親タスクキャンセル時 |

### 計算ロジック

デフォルト最大並行数の算出:
```
defaultMaxConcurrentSearches = max(1, numDataNodes * min(searchThreadPoolSize(allocatedProcessors), 10))
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 検索 | 対象インデックス（各SearchRequest指定） | SELECT | 各検索リクエストに従い、Luceneインデックスに対して検索を実行 |

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

#### 対象インデックス

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 各SearchRequestのクエリ条件に従う | 個別のSearchRequestが定義する検索条件 | 読み取り専用操作 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | リクエストリストが空 | 少なくとも1つの検索リクエストを含める |
| 400 | IllegalArgumentException | NDJSON形式が不正（末尾の改行なし等） | リクエスト形式を修正 |
| 400 | IllegalArgumentException | maxConcurrentSearchRequestsが0以下 | 1以上の正数を指定 |
| 403 | SecurityException | インデックスへのアクセス権限なし | 適切な権限を付与 |
| 500 | ClusterBlockException | クラスタがREADブロック状態 | クラスタの状態を確認・復旧 |

### リトライ仕様

マルチサーチ自体にはリトライ機構はない。個別の検索リクエストレベルでのシャード失敗時のリトライはTransportSearchActionの仕様に従う。

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

各検索リクエストは独立した読み取り専用操作であり、トランザクション管理は不要である。各リクエストは個別にスナップショットを取得して実行される。

## パフォーマンス要件

- 並行実行数のデフォルト値はクラスタリソースに応じて動的に算出される
- スタックオーバーフロー防止のため、同一スレッドでの再帰を検出した場合はgenericスレッドプールにフォークする
- 各検索結果はAtomicArrayで管理され、ロック競合を最小化する

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

- アクション名`indices:data/read/msearch`による権限制御
- 各個別検索リクエストに対するインデックスレベルの権限チェック
- 明示的インデックス指定の禁止設定（allowExplicitIndex）のサポート

## 備考

- CancellableTaskとして実装されており、タスク管理APIからキャンセル可能
- 子タスクのキャンセルが自動的に伝播する（shouldCancelChildrenOnCancellation = true）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MultiSearchRequest.java | `server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java` | リクエストの構造。List<SearchRequest>の保持、NDJSON解析ロジック（readMultiLineFormat） |
| 1-2 | MultiSearchResponse.java | `server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java` | レスポンスの構造。Item配列（成功/失敗）、tookInMillis |

**読解のコツ**: MultiSearchRequest.readMultiLineFormat()（193-320行目）がNDJSONの解析処理であり、メタデータ行と検索ボディ行の交互パースを理解することが重要。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MultiSearchAction.java | `server/src/main/java/org/opensearch/action/search/MultiSearchAction.java` | アクション定義。NAME = "indices:data/read/msearch" |
| 2-2 | RestMultiSearchAction.java | `server/src/main/java/org/opensearch/rest/action/search/RestMultiSearchAction.java` | REST APIエンドポイント定義 |

**主要処理フロー**:
1. **44-45行目（MultiSearchAction）**: INSTANCEシングルトンとNAME定数の定義
2. **47-49行目（MultiSearchAction）**: コンストラクタでレスポンスリーダーを指定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportMultiSearchAction.java | `server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java` | 並行検索実行の中核ロジック |

**主要処理フロー**:
- **108-133行目**: doExecute - クラスタブロックチェック、並行数決定、キュー構築、ワーカー起動
- **140-145行目**: defaultMaxConcurrentSearches - デフォルト並行数の算出
- **156-237行目**: executeSearch - キューからの取得、検索実行、結果収集、再帰/フォーク制御
- **239-245行目**: isCancelled - 親タスクキャンセル判定

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

```
RestMultiSearchAction (REST)
    |
    +-- TransportMultiSearchAction.doExecute()
            |
            +-- clusterState.blocks().globalBlockedRaiseException(READ)
            |
            +-- defaultMaxConcurrentSearches() [並行数算出]
            |
            +-- executeSearch() [並行ワーカー x N]
                    |
                    +-- NodeClient.search()
                    |       |
                    |       +-- TransportSearchAction.doExecute()
                    |
                    +-- handleResponse()
                            |
                            +-- responses.set(slot, item)
                            |
                            +-- finish() or executeSearch() [次のリクエスト]
```

### データフロー図

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

NDJSON Body ──────> readMultiLineFormat() ──────> List<SearchRequest>
                         |
                         v
                    TransportMultiSearchAction
                         |
          +──────────────+──────────────+
          |              |              |
   SearchRequest[0] SearchRequest[1] SearchRequest[N]
          |              |              |
   NodeClient.search  NodeClient.search  ...
          |              |              |
   SearchResponse[0] SearchResponse[1] SearchResponse[N]
          |              |              |
          +──────────────+──────────────+
                         |
                         v
                    AtomicArray<Item>
                         |
                         v
                    MultiSearchResponse ──────> JSON Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MultiSearchAction.java | `server/src/main/java/org/opensearch/action/search/MultiSearchAction.java` | ソース | アクション型定義 |
| MultiSearchRequest.java | `server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java` | ソース | リクエストモデル・NDJSON解析 |
| MultiSearchResponse.java | `server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java` | ソース | レスポンスモデル |
| TransportMultiSearchAction.java | `server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java` | ソース | トランスポート層の実行ロジック |
| RestMultiSearchAction.java | `server/src/main/java/org/opensearch/rest/action/search/RestMultiSearchAction.java` | ソース | REST APIエンドポイント |
| SearchRequest.java | `server/src/main/java/org/opensearch/action/search/SearchRequest.java` | ソース | 個別検索リクエスト |
| SearchResponse.java | `server/src/main/java/org/opensearch/action/search/SearchResponse.java` | ソース | 個別検索レスポンス |
| TransportSearchAction.java | `server/src/main/java/org/opensearch/action/search/TransportSearchAction.java` | ソース | 個別検索の実行処理 |
