# 機能設計書 22-スクロール検索

## 概要

本ドキュメントは、OpenSearchのScroll API機能（スクロール検索）に関する機能設計書である。大量の検索結果を分割して取得するためのScroll APIの仕様を定義する。

### 本機能の処理概要

**業務上の目的・背景**：通常の検索APIでは一度に取得できる結果数に制限があり、大量データの全件取得やエクスポート処理には不向きである。スクロール検索はデータベースのカーソルに類似した仕組みを提供し、検索時点のスナップショットに対して繰り返しページングすることで大量結果の安全な取得を可能にする。

**機能の利用シーン**：大量ドキュメントのエクスポート、リインデックス処理における元データの順次取得、バッチ処理での全件スキャン、データマイグレーション処理などで利用される。

**主要な処理内容**：
1. 初回検索時にscrollパラメータを指定して検索コンテキスト（scroll_id）を作成する
2. 返却されたscroll_idを使用して後続のスクロールリクエストで次のバッチを取得する
3. 各スクロールリクエストでkeep_aliveを更新し、コンテキストの有効期限を延長する
4. 結果が空になるまでスクロールを繰り返し、完了後にClear Scroll APIでコンテキストを解放する

**関連システム・外部連携**：検索結果の取得はSearchTransportServiceを介してシャード間で協調動作する。スクロールコンテキストはシャードレベルで保持される。

**権限による制御**：`indices:data/read/scroll`アクション名で権限が制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | スクロール検索 | 主画面 | 大量の結果をスクロール検索で分割取得する主処理 |
| 33 | スクロールクリア | 主画面 | スクロールの検索コンテキストを明示的にクリアする処理 |

## 機能種別

データ検索（ページング付き逐次取得）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| scroll_id | String | Yes | スクロールコンテキストのID | null不可 |
| scroll | TimeValue | No | スクロールコンテキストの有効期間（keep_alive） | 有効なTimeValue形式 |

### 入力データソース

REST APIエンドポイント（`POST /_search/scroll`, `GET /_search/scroll`）からのリクエスト。初回検索は`POST /{index}/_search?scroll={keepAlive}`で実行。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| _scroll_id | String | 次回スクロール用のID |
| hits | SearchHits | 検索結果（次のバッチ） |
| took | long | 実行時間（ミリ秒） |
| timed_out | boolean | タイムアウト有無 |
| _shards | ShardStatistics | シャード実行統計 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. scroll_idの解析
   └─ TransportSearchHelper.parseScrollIdでscroll_idをデコード
2. スクロールタイプの判定
   └─ QUERY_THEN_FETCH_TYPE or QUERY_AND_FETCH_TYPE
3. 非同期スクロールアクションの構築・実行
   └─ SearchScrollQueryThenFetchAsyncActionまたはSearchScrollQueryAndFetchAsyncAction
4. 各シャードへのスクロールクエリの送信
   └─ SearchTransportServiceを介してシャードノードに通信
5. 結果のマージ
   └─ SearchPhaseControllerで各シャードの結果をマージ
6. SearchResponseとして返却
   └─ 新しいscroll_idを含むレスポンスを返却
```

### フローチャート

```mermaid
flowchart TD
    A[スクロールリクエスト受信] --> B[scroll_idの解析]
    B --> C{スクロールタイプ判定}
    C -->|QUERY_THEN_FETCH| D[SearchScrollQueryThenFetchAsyncAction]
    C -->|QUERY_AND_FETCH| E[SearchScrollQueryAndFetchAsyncAction]
    C -->|不明| F[IllegalArgumentException]
    D --> G[各シャードへクエリ送信]
    E --> G
    G --> H[各シャード結果の収集]
    H --> I[SearchPhaseControllerで結果マージ]
    I --> J[SearchResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | コンテキスト維持 | スクロールコンテキストは指定されたkeep_alive期間保持される | 常時 |
| BR-22-02 | スナップショット一貫性 | スクロール開始時点のインデックス状態に基づいて結果を返す | 常時 |
| BR-22-03 | keep_alive更新 | 各スクロールリクエストでkeep_aliveが更新される | scrollパラメータ指定時 |
| BR-22-04 | 終了判定 | 返却されるhits.hitsが空の場合、スクロール完了 | 常時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 検索 | 初回検索で指定されたインデックス | SELECT | スクロールコンテキスト内のスナップショットからデータ取得 |

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

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

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | 初回検索クエリの条件に従う | スクロールコンテキストで保持された検索条件 | 読み取り専用、スナップショットベース |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | scroll_idが未指定 | scroll_idを指定する |
| 400 | IllegalArgumentException | scroll_idのタイプが不明 | 有効なscroll_idを使用する |
| 404 | SearchContextMissingException | スクロールコンテキストが期限切れまたは存在しない | 新しいスクロール検索を開始する |

### リトライ仕様

スクロールコンテキストが期限切れの場合はリトライ不可。新しいスクロール検索を最初から実行する必要がある。

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

スクロールコンテキストはインデックスのスナップショットを保持するため、スクロール中のインデックス変更は結果に反映されない。コンテキストはシャードレベルで管理され、keep_alive期間経過後に自動解放される。

## パフォーマンス要件

- スクロールコンテキストはメモリを消費するため、不要になったら明示的にClear Scroll APIで解放することを推奨
- WorkloadGroupTask対応により、ワークロードグループに基づくリソース管理が可能

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

- アクション名`indices:data/read/scroll`による権限制御
- scroll_idにはシャード情報が含まれるため、外部への漏洩に注意が必要

## 備考

- SearchTask型のタスクとして登録され、タスク管理APIから確認可能
- SearchScrollQueryAndFetchAsyncActionは将来的に廃止の可能性あり（コード内のTODOコメント）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SearchScrollRequest.java | `server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java` | scroll_idとscrollパラメータの保持構造 |
| 1-2 | ParsedScrollId.java | `server/src/main/java/org/opensearch/action/search/ParsedScrollId.java` | scroll_idのパース結果構造体 |

**読解のコツ**: SearchScrollRequestは62-63行目でscrollIdとscrollの2つのフィールドを持つシンプルな構造。バリデーション（85-91行目）でscrollIdの必須チェックを行う。

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportSearchScrollAction.java | `server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java` | スクロール実行の分岐ロジック |
| 3-2 | SearchScrollQueryThenFetchAsyncAction.java | `server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java` | QueryThenFetch型のスクロール処理 |

**主要処理フロー**:
- **75-116行目（TransportSearchScrollAction）**: doExecute - scroll_id解析、タイプ判定、非同期アクション構築・実行
- **82行目**: TransportSearchHelper.parseScrollIdでscroll_idをパース
- **84行目**: scrollId.getType()でスクロールタイプを分岐

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

```
RestSearchScrollAction (REST)
    |
    +-- TransportSearchScrollAction.doExecute()
            |
            +-- TransportSearchHelper.parseScrollId()
            |
            +-- [switch: scrollId.getType()]
                    |
                    +-- SearchScrollQueryThenFetchAsyncAction.run()
                    |       |
                    |       +-- SearchTransportService (シャード通信)
                    |       +-- SearchPhaseController (結果マージ)
                    |
                    +-- SearchScrollQueryAndFetchAsyncAction.run()
```

### データフロー図

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

scroll_id ──────> parseScrollId() ──────> ParsedScrollId
                       |
                       v
                  SearchScrollAsyncAction
                       |
            +──────────+──────────+
            |          |          |
         Shard[0]   Shard[1]   Shard[N]
            |          |          |
            +──────────+──────────+
                       |
                       v
                  SearchPhaseController.merge()
                       |
                       v
                  SearchResponse (with new _scroll_id)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SearchScrollAction.java | `server/src/main/java/org/opensearch/action/search/SearchScrollAction.java` | ソース | アクション型定義 |
| SearchScrollRequest.java | `server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java` | ソース | リクエストモデル |
| TransportSearchScrollAction.java | `server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java` | ソース | トランスポート層実行ロジック |
| SearchScrollQueryThenFetchAsyncAction.java | `server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java` | ソース | QueryThenFetch型スクロール |
| SearchScrollQueryAndFetchAsyncAction.java | `server/src/main/java/org/opensearch/action/search/SearchScrollQueryAndFetchAsyncAction.java` | ソース | QueryAndFetch型スクロール |
| SearchScrollAsyncAction.java | `server/src/main/java/org/opensearch/action/search/SearchScrollAsyncAction.java` | ソース | スクロール非同期処理基底 |
| ParsedScrollId.java | `server/src/main/java/org/opensearch/action/search/ParsedScrollId.java` | ソース | scroll_idパース結果 |
| TransportSearchHelper.java | `server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java` | ソース | scroll_id解析ユーティリティ |
| RestSearchScrollAction.java | `server/src/main/java/org/opensearch/rest/action/search/RestSearchScrollAction.java` | ソース | REST APIエンドポイント |
| ClearScrollAction.java | `server/src/main/java/org/opensearch/action/search/ClearScrollAction.java` | ソース | スクロールコンテキスト解放 |
