# 機能設計書 90-Derived Fields

## 概要

本ドキュメントは、実行時に動的に算出される派生フィールド（Derived Fields）機能の設計を記述する。

### 本機能の処理概要

Derived Fieldsは、インデックス時にデータを格納するのではなく、検索時にスクリプトを実行してフィールド値を動的に算出する機能である。DerivedFieldMapperとDerivedFieldTypeを中心に、検索・集計・値取得の各操作において、_sourceからスクリプトで値を抽出・変換する。これにより、インデックスサイズを増加させずに仮想的なフィールドを定義できる。

**業務上の目的・背景**：既存のデータから派生する情報（例: ログメッセージからのIP抽出、タイムスタンプの時間帯分類など）をインデックス時に計算して格納すると、ストレージコストが増加し、スキーマ変更時の再インデックスが必要となる。Derived Fieldsにより、これらの派生値を検索時に動的に計算できる。

**機能の利用シーン**：ログ分析でのフィールド抽出（IPアドレス、ステータスコード等）、既存フィールドの型変換、複数フィールドの結合、条件付き値の算出。

**主要な処理内容**：
1. DerivedFieldMapperによるマッピング定義（type, script, prefilter_field, properties, format, ignore_malformed）
2. DerivedFieldTypeによる各種クエリの実行（term, range, fuzzy, prefix, wildcard, regexp, phrase等）
3. DerivedFieldValueFetcherによるスクリプト実行と値取得
4. DerivedFieldQueryによるLucene MemoryIndexを使用した動的フィルタリング
5. prefilter_fieldによる事前フィルタリングでのパフォーマンス最適化

**関連システム・外部連携**：Painlessスクリプトエンジン、_sourceフィールド、Lucene MemoryIndex。

**権限による制御**：_sourceが有効である必要がある（無効の場合はIllegalArgumentException）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | マッピング定義と検索クエリを通じて間接的に使用される |

## 機能種別

計算処理 / フィールドマッピング / 検索

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | String | Yes | 派生フィールドの型（boolean, date, geo_point, ip, keyword, long, double, float, text, object） | DerivedFieldSupportedTypesに定義された型のみ |
| script | Script | Yes | 値を算出するスクリプト | null可（パラメータ定義上） |
| prefilter_field | String | No | 事前フィルタリング用のtextフィールド名 | 存在するtextフィールドのみ |
| properties | Map<String, Object> | No | ネストされたフィールドのプロパティ定義 | 値はString型のみ |
| format | String | No | 日付フォーマット | date型で使用 |
| ignore_malformed | boolean | No | 不正な値を無視するか | デフォルトfalse |

### 入力データソース

- インデックスマッピング定義（derived_fields句）
- 検索リクエスト内のderived_fields句
- ドキュメントの_sourceフィールド

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 派生値 | Object | スクリプト実行結果（型はtype設定に依存） |
| クエリ結果 | Query | DerivedFieldQuery（Luceneクエリ） |
| 集計結果 | Object | AggregationScript経由の集計値 |

### 出力先

検索結果、集計結果、_fieldsレスポンスに含まれる。

## 処理フロー

### 処理シーケンス

```
1. マッピング定義
   └─ DerivedFieldMapper.Builderでパラメータ解析、DerivedFieldType生成
2. 検索クエリ実行
   └─ DerivedFieldType.termQuery()等 -> DerivedFieldQuery生成
3. DerivedFieldQuery実行
   └─ 各ドキュメントに対してスクリプト実行 -> MemoryIndexに仮想インデックス作成 -> 元クエリでマッチ判定
4. prefilter最適化
   └─ prefilter_fieldがある場合、BooleanQueryでFILTER句として事前フィルタを追加
5. 集計実行
   └─ DerivedFieldType.getAggregationScript() -> スクリプト実行 -> 値返却
```

### フローチャート

```mermaid
flowchart TD
    A[検索クエリ受信] --> B{DerivedFieldか?}
    B -->|No| C[通常のクエリ処理]
    B -->|Yes| D[DerivedFieldType.termQuery等]
    D --> E{prefilter_field設定あり?}
    E -->|Yes| F[BooleanQuery生成]
    F --> G[prefilterクエリ + DerivedFieldQuery]
    E -->|No| H[DerivedFieldQuery生成]
    G --> I[各ドキュメントに対して実行]
    H --> I
    I --> J[DerivedFieldScript実行]
    J --> K[_sourceから値抽出]
    K --> L[MemoryIndexに仮想格納]
    L --> M[元クエリでマッチ判定]
    M --> N[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | _source必須 | _sourceが無効なインデックスではDerived Fieldsは使用不可 | クエリ実行時 |
| BR-02 | サポート型 | boolean, date, geo_point, ip, keyword, long, double, float, text, objectをサポート | type設定時 |
| BR-03 | prefilter_fieldはtext型 | prefilter_fieldはtext型フィールドのみ指定可能 | prefilter設定時 |
| BR-04 | propertiesはString値のみ | propertiesマップの値はString型のみ許可 | properties設定時 |
| BR-05 | existsQuery非サポート | Derived FieldsはexistsQueryをサポートしない | existsQuery実行時 |
| BR-06 | spanPrefixQuery非サポート | span prefix queriesはサポートしない | spanPrefixQuery実行時 |
| BR-07 | Object Derived Field | 名前に"."を含む場合はObjectDerivedFieldTypeとして生成 | Builder.build時 |
| BR-08 | IP型フォーマット | IP型の集計値はBytesRefにフォーマットされる | formatValues時 |

### 計算ロジック

DerivedFieldQueryの実行: 各ドキュメントについて、(1) DerivedFieldScriptで_sourceから値を抽出、(2) indexableFieldGeneratorでIndexableFieldに変換、(3) Lucene MemoryIndexに格納、(4) 元のLuceneクエリでマッチ判定を実行。

prefilter最適化: prefilter_fieldを指定すると、元のクエリをprefilter_fieldに対しても実行し、BooleanQuery(FILTER)で事前フィルタリングする。これにより、Derived Fieldのスクリプト実行対象ドキュメント数を削減できる。

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

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

Derived Fieldsはインデックスに格納されないため、直接的なデータベース操作は行わない。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 検索 | _source | SELECT | _sourceからスクリプトで値を動的抽出 |
| 仮想インデックス | MemoryIndex | INSERT/SELECT | 一時的なMemoryIndexで仮想インデックスを作成・検索 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | IllegalArgumentException | _sourceが無効 | _sourceを有効にする |
| - | MapperException | prefilter_fieldが存在しない | 有効なフィールド名を指定 |
| - | MapperException | prefilter_fieldがtext型でない | text型フィールドを指定 |
| - | MapperParsingException | propertiesの値がString型でない | String値のみ指定 |
| - | UnsupportedOperationException | existsQuery実行 | Derived FieldsではexistsQueryは使用不可 |
| - | UnsupportedOperationException | parseCreateField実行 | DerivedFieldMapperはインデックスに格納されない |

### リトライ仕様

該当なし。

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

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

## パフォーマンス要件

Derived Fieldsは検索時にスクリプトを実行するため、大量のドキュメントに対する検索ではパフォーマンスが低下する可能性がある。prefilter_fieldを活用してスクリプト実行対象ドキュメント数を削減することが推奨される。isAggregatable()はtrueを返し、集計に使用可能。

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

- スクリプト実行のセキュリティはScriptServiceのスクリプトセキュリティ設定に従う
- _sourceの内容にアクセスするため、フィールドレベルのアクセス制御が必要な場合は考慮が必要

## 備考

Derived Fieldsは@PublicApi(since="2.14.0")として公開されている（DerivedFieldクラス）。ObjectDerivedFieldTypeはネストされたオブジェクトフィールドの派生をサポートする。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DerivedField.java | `server/src/main/java/org/opensearch/index/mapper/DerivedField.java` | @PublicApi(since="2.14.0")。name, type, script, prefilterField, properties, ignoreMalformed, formatの7フィールド。Writeable/ToXContentFragment実装 |
| 1-2 | DerivedFieldSupportedTypes.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldSupportedTypes.java` | enum定義。BOOLEAN, DATE, GEO_POINT, IP, KEYWORD, LONG, DOUBLE, FLOAT, TEXT, OBJECTの各型のFieldMapper生成ロジックとIndexableField生成関数 |

**読解のコツ**: DerivedFieldはWriteableを実装しており、バージョン互換性（V_2_15_0以降でproperties等を追加）がある点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DerivedFieldMapper.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java` | CONTENT_TYPE="derived"（34行目）。Builder（47-191行目）でtype, script, properties, prefilter_field, format, ignoreMalformedのパラメータ定義 |

**主要処理フロー**:
1. **155-190行目**: Builder.build() - DerivedField生成、DerivedFieldSupportedTypes.getFieldMapperFromType()でFieldMapper取得、DerivedFieldType生成
2. **175-179行目**: 名前に"."を含む場合はObjectDerivedFieldType
3. **235-239行目**: parseCreateField()はUnsupportedOperationException（インデックスに格納されない）

#### Step 3: クエリ実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DerivedFieldType.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldType.java` | termQuery()（160-173行目）: typeFieldMapperのtermQueryを元にDerivedFieldQueryを生成。prefilter_field設定時はBooleanQuery(FILTER)で結合 |

**主要処理フロー**:
- **160-173行目**: termQuery - typeFieldMapperでクエリ生成 -> DerivedFieldQuery生成 -> prefilterがあればconjunction
- **98-116行目**: getPrefilterFieldType - prefilter_fieldの存在・型チェック
- **140-152行目**: valueFetcher - DerivedFieldValueFetcher生成
- **495-510行目**: getDerivedFieldLeafFactory - _source有効チェック、DerivedFieldScript.Factory取得
- **512-539行目**: getAggregationScript - 集計用スクリプト

#### Step 4: 値取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DerivedFieldValueFetcher.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldValueFetcher.java` | DerivedFieldScript.LeafFactoryを使用してスクリプト実行し値を取得 |
| 4-2 | DerivedFieldResolver.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldResolver.java` | Derived Fieldの解決インターフェース |
| 4-3 | DerivedFieldResolverFactory.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldResolverFactory.java` | Resolverのファクトリ |

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

```
DerivedFieldMapper
    |
    +-- Builder.build()
    |       +-- DerivedField(name, type, script)
    |       +-- DerivedFieldSupportedTypes.getFieldMapperFromType()
    |       +-- DerivedFieldSupportedTypes.getIndexableFieldGeneratorType()
    |       +-- DerivedFieldType / ObjectDerivedFieldType
    |
DerivedFieldType
    |
    +-- termQuery(value, context)
    |       +-- typeFieldMapper.mappedFieldType.termQuery()
    |       +-- new DerivedFieldQuery(query, valueFetcher, ...)
    |       +-- getPrefilterFieldType() -> createConjuctionQuery()
    |
    +-- valueFetcher(context, searchLookup, format)
    |       +-- getDerivedFieldLeafFactory(script, context, searchLookup)
    |       +-- new DerivedFieldValueFetcher(leafFactory, valueForDisplay)
    |
    +-- getAggregationScript(context)
            +-- valueFetcher() -> DerivedFieldValueFetcher
            +-- new AggregationScript() [anonymous]
                    +-- fetchValuesInternal() -> formatValues()
```

### データフロー図

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

マッピング定義 ──────> DerivedFieldMapper.Builder
  (type, script,          |
   prefilter_field)  DerivedFieldType生成
                          |
検索クエリ ──────> DerivedFieldType.termQuery()
                          |
                    DerivedFieldQuery
                          |
                    各ドキュメント:
                      _source ──> DerivedFieldScript実行
                                      |
                                  スクリプト結果値
                                      |
                                  IndexableField変換
                                      |
                                  MemoryIndex格納
                                      |
                                  元クエリでマッチ判定 ──> 検索結果
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DerivedFieldMapper.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java` | ソース | 派生フィールドマッパー |
| DerivedField.java | `server/src/main/java/org/opensearch/index/mapper/DerivedField.java` | ソース | 派生フィールドデータモデル |
| DerivedFieldType.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldType.java` | ソース | 派生フィールド型（クエリ実行ロジック） |
| DerivedFieldValueFetcher.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldValueFetcher.java` | ソース | スクリプトベースの値取得 |
| DerivedFieldSupportedTypes.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldSupportedTypes.java` | ソース | サポート型の定義とFieldMapper/IndexableField生成 |
| DerivedFieldResolver.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldResolver.java` | ソース | Derived Field解決インターフェース |
| DerivedFieldResolverFactory.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldResolverFactory.java` | ソース | Resolverファクトリ |
| DerivedFieldGenerator.java | `server/src/main/java/org/opensearch/index/mapper/DerivedFieldGenerator.java` | ソース | Derived Field生成ヘルパー |
