# 機能設計書 111-ランク評価API

## 概要

本ドキュメントは、OpenSearchのRank Eval API（ランク評価API）モジュールの機能設計書である。検索結果のランキング品質を定量的に評価するためのAPIを提供する。

### 本機能の処理概要

**業務上の目的・背景**：検索エンジンの品質向上において、検索結果のランキング精度を客観的に計測・評価することは不可欠である。Rank Eval APIは、事前にアノテーション（人手による評価）が付与されたドキュメントセットに対して検索クエリを実行し、Precision@K、Recall@K、Mean Reciprocal Rank（MRR）、Discounted Cumulative Gain（DCG）、Expected Reciprocal Rank（ERR）などの標準的な情報検索評価メトリクスを算出する機能を提供する。

**機能の利用シーン**：検索チューニングの効果測定、A/Bテストにおける検索品質比較、検索アルゴリズム変更前後の品質検証、定期的な検索品質モニタリングなどの場面で利用される。

**主要な処理内容**：
1. RESTリクエストを受け付け、RankEvalSpecに基づいて評価仕様を解析する
2. 評価対象の各クエリ（RatedRequest）に対してMultiSearchRequestを構築する
3. MultiSearch APIを使用して複数の検索クエリを一括実行する
4. 各検索結果を評価済みドキュメント（RatedDocument）と照合し、指定メトリクスでスコアを算出する
5. 個別クエリのスコアを統合（デフォルトは平均）し、全体の評価スコアとして返却する

**関連システム・外部連携**：Search API（MultiSearch）を内部的に呼び出す。テンプレート検索が使用される場合はScriptServiceを利用してMustacheテンプレートを展開する。

**権限による制御**：対象インデックスへの読み取り権限が必要。評価対象インデックスへのアクセス制御はIndicesOptionsに基づく。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 29 | ランク評価 | 主画面 | ランク付き検索結果の品質を評価する処理 |

## 機能種別

計算処理 / データ分析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| index | String | No | 評価対象のインデックス名（カンマ区切り） | 有効なインデックス名であること |
| search_type | String | No | 検索タイプ（query_then_fetch等） | SearchType列挙値であること |
| requests | Array[RatedRequest] | Yes | 評価対象のクエリとレーティング情報のリスト | 1件以上必須 |
| metric | Object | Yes | 評価メトリクスの定義（precision, recall, mrr, dcg, err） | サポートされたメトリクス名であること |
| templates | Array[ScriptWithId] | No | テンプレートベースのクエリ定義 | 各テンプレートにidとtemplateが必要 |
| max_concurrent_searches | int | No | 並列実行する最大検索数（デフォルト: 10） | 正の整数であること |

### 入力データソース

REST API経由のJSONリクエストボディ。GET/POSTの `/_rank_eval` または `/{index}/_rank_eval` エンドポイント。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| metric_score | double | 全体の評価メトリクススコア（各クエリスコアの平均） |
| details | Map[String, EvalQueryQuality] | クエリIDごとの詳細評価結果 |
| failures | Map[String, Exception] | クエリIDごとのエラー情報 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. RESTリクエスト受付（RestRankEvalAction）
   └─ パラメータ解析、RankEvalRequestの生成
2. トランスポートアクション実行（TransportRankEvalAction.doExecute）
   └─ RankEvalSpecからメトリクスと評価リクエストリストを取得
3. テンプレート展開
   └─ テンプレートが指定されている場合、ScriptServiceでMustacheテンプレートを展開
4. MultiSearchRequest構築
   └─ 各RatedRequestに対してSearchRequestを構築し、MSRに追加
5. MultiSearch実行
   └─ client.multiSearch()で一括検索実行
6. 評価スコア算出（RankEvalActionListener.onResponse）
   └─ 各検索結果のヒットと評価済みドキュメントを照合し、メトリクスで評価
7. レスポンス生成
   └─ metric.combine()で個別スコアを集約し、RankEvalResponseを生成
```

### フローチャート

```mermaid
flowchart TD
    A[RESTリクエスト受信] --> B[RankEvalSpec解析]
    B --> C{テンプレート使用?}
    C -->|Yes| D[ScriptServiceでテンプレート展開]
    C -->|No| E[evaluationRequestをそのまま使用]
    D --> F[MultiSearchRequest構築]
    E --> F
    F --> G{metricにforcedSearchSize?}
    G -->|Yes| H[検索サイズを上書き]
    G -->|No| I[デフォルトサイズ]
    H --> J[client.multiSearch実行]
    I --> J
    J --> K[各レスポンスを評価]
    K --> L{検索成功?}
    L -->|Yes| M[metric.evaluate実行]
    L -->|No| N[エラーマップに追加]
    M --> O[metric.combineでスコア統合]
    N --> O
    O --> P[RankEvalResponse返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-111-01 | デフォルト並列数 | 最大並列検索数のデフォルトは10 | max_concurrent_searches未指定時 |
| BR-111-02 | メトリクス必須 | 評価メトリクスの指定は必須 | 常時 |
| BR-111-03 | スコア集約 | 各クエリスコアの平均値が全体スコアとなる | デフォルト動作 |
| BR-111-04 | 未評価ドキュメント | 未評価（unlabeled）ドキュメントの扱いはメトリクスごとに異なる | PrecisionAtKではignore_unlabeledで制御 |
| BR-111-05 | 検索ウィンドウ | PrecisionAtKのkデフォルト値は10 | k未指定時 |

### 計算ロジック

- **Precision@K**: relevantRetrieved / retrieved（kはデフォルト10、relevant_rating_thresholdはデフォルト1）
- **Recall@K**: relevantRetrieved / totalRelevant
- **Mean Reciprocal Rank (MRR)**: 最初の関連ドキュメントの逆順位
- **Discounted Cumulative Gain (DCG)**: sum(rating / log2(position + 1))
- **Expected Reciprocal Rank (ERR)**: ユーザが各位置で満足する確率ベースの評価

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 検索実行 | 対象インデックス | SELECT | MultiSearchによる検索クエリ実行（読み取り専用） |

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

本機能はデータの変更を行わず、読み取り専用の操作のみを実行する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | ActionRequestValidationException | RankEvalSpecが未指定 | リクエストボディにrequestsとmetricを指定する |
| 400 | IllegalArgumentException | ratedRequestsが空 | 1件以上の評価リクエストを指定する |
| 400 | IllegalStateException | テンプレートも評価リクエストも未指定 | templateまたはrequestフィールドを指定する |
| 400 | IOException | テンプレート展開時のパースエラー | テンプレート構文を確認する |
| 404 | IndexNotFoundException | 指定インデックスが存在しない | インデックス名を確認する |

### リトライ仕様

本機能は読み取り専用操作であり、リトライはクライアント側で制御する。内部的にはMultiSearchの一部クエリが失敗してもerrorsマップに記録され、他のクエリは正常に評価が続行される。

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

読み取り専用操作のため、トランザクション管理は不要。

## パフォーマンス要件

- max_concurrent_searchesパラメータにより並列検索数を制御可能（デフォルト: 10）
- 各メトリクスのforcedSearchSize()により検索結果取得数を最適化

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

- 対象インデックスへの読み取り権限が必要
- IndicesOptionsに基づくインデックスアクセス制御が適用される
- テンプレート使用時はScriptServiceのセキュリティ制御に従う

## 備考

- モジュール名: rank-eval
- プラグインクラス: RankEvalModulePlugin
- サポートされるメトリクス: PrecisionAtK, RecallAtK, MeanReciprocalRank, DiscountedCumulativeGain, ExpectedReciprocalRank

---

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

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

### 推奨読解順序

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

まず、評価リクエストと評価メトリクスのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RankEvalSpec.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java` | 評価仕様全体の構造：ratedRequests、metric、templates、maxConcurrentSearches |
| 1-2 | RatedRequest.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java` | 個別の評価リクエスト：クエリ定義とRatedDocumentのリスト |
| 1-3 | RatedDocument.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java` | 評価済みドキュメント：index、id、ratingの組み合わせ |
| 1-4 | EvaluationMetric.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java` | 評価メトリクスのインターフェース：evaluate、combine、joinHitsWithRatings |

**読解のコツ**: RankEvalSpecが全体の入力を表し、EvaluationMetricの実装クラス（PrecisionAtK等）が具体的な評価ロジックを持つ。各メトリクスはNamedWriteableとして登録され、JSON上のキー名で特定される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestRankEvalAction.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java` | RESTエンドポイント定義と入力パース |

**主要処理フロー**:
1. **109-117行目**: ルート定義 `GET/POST /_rank_eval` と `GET/POST /{index}/_rank_eval`
2. **121-131行目**: `prepareRequest`でRankEvalRequestを生成し、RankEvalActionを実行
3. **133-141行目**: `parseRankEvalRequest`でインデックス、IndicesOptions、SearchType、RankEvalSpecを設定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TransportRankEvalAction.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java` | 核心となるビジネスロジック |

**主要処理フロー**:
- **102-163行目**: `doExecute`メソッドが処理の核心。RankEvalSpecからメトリクスとratedRequestsを取得し、テンプレート展開後にMultiSearchRequestを構築して実行
- **110-112行目**: テンプレートをScriptServiceでコンパイル
- **142-144行目**: メトリクスにforcedSearchSizeがある場合、検索サイズを上書き
- **159-162行目**: client.multiSearchで一括検索を実行
- **186-200行目**: `RankEvalActionListener.onResponse`で各検索結果をメトリクスで評価し、最終スコアを算出

#### Step 4: 評価メトリクスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PrecisionAtK.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java` | Precision@Kの実装例として最も代表的 |
| 4-2 | DiscountedCumulativeGain.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java` | DCGの実装 |
| 4-3 | MeanReciprocalRank.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java` | MRRの実装 |

**主要処理フロー**（PrecisionAtK）:
- **205-233行目**: `evaluate`メソッド。joinHitsWithRatingsで検索ヒットと評価済みドキュメントを照合し、relevantRetrieved / retrievedでPrecisionを算出
- **181-183行目**: `forcedSearchSize`でkを返す（検索結果をk件に制限）

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

```
RestRankEvalAction.prepareRequest()
    |
    +-- RankEvalSpec.parse()
    |
    +-- client.executeLocally(RankEvalAction.INSTANCE, request)
            |
            +-- TransportRankEvalAction.doExecute()
                    |
                    +-- ScriptService.compile() [テンプレート展開]
                    |
                    +-- MultiSearchRequest構築
                    |
                    +-- client.multiSearch()
                    |       |
                    |       +-- RankEvalActionListener.onResponse()
                    |               |
                    |               +-- EvaluationMetric.evaluate()
                    |               |       |
                    |               |       +-- joinHitsWithRatings()
                    |               |
                    |               +-- EvaluationMetric.combine()
                    |
                    +-- RankEvalResponse生成
```

### データフロー図

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

REST Request       --> RestRankEvalAction              --> RankEvalRequest
  (JSON Body)            (パース)

RankEvalRequest    --> TransportRankEvalAction          --> MultiSearchRequest
  (RankEvalSpec)         (テンプレート展開・検索構築)

MultiSearchRequest --> Search Engine                    --> MultiSearchResponse
                        (一括検索実行)

MultiSearchResponse --> RankEvalActionListener          --> RankEvalResponse
  + RatedDocuments       (メトリクス評価・集約)              (metric_score + details)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestRankEvalAction.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java` | ソース | RESTエンドポイント |
| TransportRankEvalAction.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java` | ソース | トランスポートアクション（ビジネスロジック） |
| RankEvalRequest.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequest.java` | ソース | リクエストデータ構造 |
| RankEvalResponse.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalResponse.java` | ソース | レスポンスデータ構造 |
| RankEvalSpec.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java` | ソース | 評価仕様定義 |
| EvaluationMetric.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java` | ソース | 評価メトリクスインターフェース |
| PrecisionAtK.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java` | ソース | Precision@K実装 |
| RecallAtK.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RecallAtK.java` | ソース | Recall@K実装 |
| MeanReciprocalRank.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java` | ソース | MRR実装 |
| DiscountedCumulativeGain.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java` | ソース | DCG実装 |
| ExpectedReciprocalRank.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/ExpectedReciprocalRank.java` | ソース | ERR実装 |
| RatedRequest.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java` | ソース | 評価リクエスト定義 |
| RatedDocument.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java` | ソース | 評価済みドキュメント定義 |
| RatedSearchHit.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedSearchHit.java` | ソース | 評価付き検索ヒット |
| EvalQueryQuality.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvalQueryQuality.java` | ソース | 個別クエリの品質評価結果 |
| RankEvalAction.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalAction.java` | ソース | アクション定義 |
| RankEvalModulePlugin.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalModulePlugin.java` | ソース | プラグイン登録 |
| RankEvalNamedXContentProvider.java | `modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalNamedXContentProvider.java` | ソース | NamedXContent提供 |
