# 機能設計書 113-Parent-Join

## 概要

本ドキュメントは、OpenSearchのParent-Joinモジュールの機能設計書である。ドキュメント間の親子関係を定義し、親子関係に基づく検索・集計を実現する。

### 本機能の処理概要

**業務上の目的・背景**：RDBのようなテーブル結合を必要とするデータモデル（例：質問と回答、親カテゴリと子カテゴリ、注文と注文明細）をOpenSearchで表現するために、同一インデックス内でドキュメント間の親子関係を定義する機能が必要である。Parent-Joinモジュールは、joinフィールドマッパーと専用クエリ・集計を提供する。

**機能の利用シーン**：一対多の関係を持つデータモデリング、親ドキュメントの条件で子ドキュメントを検索、子ドキュメントの条件で親ドキュメントを検索、親子関係に基づく集計処理で利用される。

**主要な処理内容**：
1. ParentJoinFieldMapperによるjoinフィールドの定義（親子関係のマッピング）
2. HasChildQueryBuilderによる子ドキュメント条件での親ドキュメント検索
3. HasParentQueryBuilderによる親ドキュメント条件での子ドキュメント検索
4. ParentIdQueryBuilderによる親IDでの子ドキュメント直接検索
5. ChildrenAggregationBuilderによる子ドキュメント集計
6. ParentAggregationBuilderによる親ドキュメント集計

**関連システム・外部連携**：Luceneの親子ドキュメントインデックス機能（同一ルーティング値を使用）と連携する。

**権限による制御**：対象インデックスへの読み取り権限（検索時）および書き込み権限（マッピング定義時）が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面機能マッピングに直接対応する画面なし。検索API（画面No.21）のクエリ・集計として使用される |

## 機能種別

データ定義 / 検索処理 / 集計処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type (マッピング) | String | Yes | "join" フィールドタイプ指定 | joinであること |
| relations (マッピング) | Object | Yes | 親子関係の定義（親: [子, ...]） | 少なくとも1つの関係が必要 |
| has_child.type | String | Yes (has_child) | 子ドキュメントのタイプ名 | 定義済みの子タイプであること |
| has_child.query | Object | Yes (has_child) | 子ドキュメントに適用するクエリ | 有効なクエリであること |
| has_parent.parent_type | String | Yes (has_parent) | 親ドキュメントのタイプ名 | 定義済みの親タイプであること |
| has_parent.query | Object | Yes (has_parent) | 親ドキュメントに適用するクエリ | 有効なクエリであること |
| parent_id.type | String | Yes (parent_id) | 子ドキュメントのタイプ名 | 定義済みの子タイプであること |
| parent_id.id | String | Yes (parent_id) | 親ドキュメントのID | 非null |

### 入力データソース

検索API経由のクエリリクエスト、マッピングAPI経由の定義リクエスト。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hits (has_child) | Array | 条件に一致する子を持つ親ドキュメント |
| hits (has_parent) | Array | 条件に一致する親を持つ子ドキュメント |
| hits (parent_id) | Array | 指定親IDの子ドキュメント |
| children (集計) | Object | 子ドキュメントのバケット集計結果 |
| parent (集計) | Object | 親ドキュメントのバケット集計結果 |

### 出力先

検索APIレスポンス（hitsまたはaggregationsセクション）

## 処理フロー

### 処理シーケンス

```
1. マッピング定義
   └─ ParentJoinFieldMapper.TypeParserがjoinフィールド定義をパース
   └─ ParentIdFieldMapper、MetaJoinFieldMapperを内部的に生成
2. ドキュメント登録
   └─ joinフィールドにname（タイプ名）とparent（親ID、子の場合）を設定
   └─ 同一ルーティング値で登録（親子は同一シャードに配置）
3. 検索実行
   └─ HasChildQueryBuilder/HasParentQueryBuilder/ParentIdQueryBuilderがクエリを構築
   └─ Luceneの親子インデックス機能を使用して効率的に検索
4. 集計実行
   └─ ChildrenAggregationBuilder/ParentAggregationBuilderが集計を構築
```

### フローチャート

```mermaid
flowchart TD
    A[検索リクエスト受信] --> B{クエリタイプ判定}
    B -->|has_child| C[HasChildQueryBuilder]
    B -->|has_parent| D[HasParentQueryBuilder]
    B -->|parent_id| E[ParentIdQueryBuilder]
    C --> F[子ドキュメントでフィルタ]
    D --> G[親ドキュメントでフィルタ]
    E --> H[親IDでフィルタ]
    F --> I[親ドキュメントを返却]
    G --> J[子ドキュメントを返却]
    H --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-113-01 | 同一シャード配置 | 親子ドキュメントは同一シャードに配置される必要がある（同一ルーティング値） | ドキュメント登録時 |
| BR-113-02 | 単一joinフィールド | 1つのインデックスに定義できるjoinフィールドは1つのみ | マッピング定義時 |
| BR-113-03 | 多階層対応 | 複数階層の親子関係を定義可能（祖父->親->子） | マッピング定義時 |

### 計算ロジック

親子クエリは内部的にLuceneのBlockJoin機能を使用し、同一セグメント内のドキュメントブロックから親子関係を解決する。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| マッピング定義 | インデックスメタデータ | UPDATE | joinフィールドの定義 |
| 検索実行 | 対象インデックス | SELECT | 親子クエリの実行 |

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

親子クエリは読み取り専用操作。マッピング定義時はインデックスメタデータが更新される。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | MapperParsingException | joinフィールド定義のパースエラー | マッピング定義を確認する |
| 400 | QueryShardException | 存在しない子/親タイプの指定 | 定義済みのタイプ名を使用する |
| 400 | IllegalArgumentException | ルーティング値の未指定（子ドキュメント登録時） | routing値を指定する |

### リトライ仕様

検索処理の一部として実行され、リトライは検索APIの仕様に従う。

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

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

## パフォーマンス要件

- 親子クエリは通常のクエリと比較してオーバーヘッドがあるため、大規模データでは注意が必要
- 親子ドキュメントは同一シャードに配置されるため、ルーティング設計が重要

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

- 対象インデックスへのアクセス権限に基づく制御

## 備考

- モジュール名: parent-join
- プラグインクラス: ParentJoinModulePlugin
- クエリ: has_child, has_parent, parent_id
- 集計: children, parent

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ParentJoinModulePlugin.java | `modules/parent-join/src/main/java/org/opensearch/join/ParentJoinModulePlugin.java` | プラグイン登録内容：クエリ3種、集計2種、マッパー1種 |
| 1-2 | ParentJoinFieldMapper.java | `modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java` | joinフィールドのマッピング定義：relations、parentIdFieldMapper |

**読解のコツ**: ParentJoinFieldMapperは内部的にParentIdFieldMapperとMetaJoinFieldMapperを生成する。relationsマップが親子関係の定義を保持する。

#### Step 2: クエリを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | HasChildQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java` | has_childクエリの構築ロジック |
| 2-2 | HasParentQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java` | has_parentクエリの構築ロジック |
| 2-3 | ParentIdQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java` | parent_idクエリの構築ロジック |

#### Step 3: 集計を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ChildrenAggregationBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java` | children集計のビルダー |
| 3-2 | ParentAggregationBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java` | parent集計のビルダー |
| 3-3 | ChildrenToParentAggregator.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java` | 子→親方向の集計ロジック |

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

```
ParentJoinModulePlugin
    |
    +-- getMappers()
    |       +-- ParentJoinFieldMapper.TypeParser
    |               +-- ParentIdFieldMapper
    |               +-- MetaJoinFieldMapper
    |
    +-- getQueries()
    |       +-- HasChildQueryBuilder
    |       +-- HasParentQueryBuilder
    |       +-- ParentIdQueryBuilder
    |
    +-- getAggregations()
            +-- ChildrenAggregationBuilder --> InternalChildren
            +-- ParentAggregationBuilder --> InternalParent
```

### データフロー図

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

マッピング定義        --> ParentJoinFieldMapper          --> joinフィールド定義
  (relations)

has_childクエリ      --> HasChildQueryBuilder            --> 親ドキュメント群
  (type, query)           +-> Lucene BlockJoinQuery

has_parentクエリ     --> HasParentQueryBuilder           --> 子ドキュメント群
  (parent_type, query)    +-> Lucene BlockJoinQuery

parent_idクエリ      --> ParentIdQueryBuilder            --> 子ドキュメント群
  (type, id)              +-> TermQuery on _parent_join
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ParentJoinModulePlugin.java | `modules/parent-join/src/main/java/org/opensearch/join/ParentJoinModulePlugin.java` | ソース | プラグイン登録 |
| ParentJoinFieldMapper.java | `modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java` | ソース | joinフィールドマッパー |
| ParentIdFieldMapper.java | `modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentIdFieldMapper.java` | ソース | 親IDフィールドマッパー |
| MetaJoinFieldMapper.java | `modules/parent-join/src/main/java/org/opensearch/join/mapper/MetaJoinFieldMapper.java` | ソース | メタjoinフィールドマッパー |
| HasChildQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java` | ソース | has_childクエリ |
| HasParentQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java` | ソース | has_parentクエリ |
| ParentIdQueryBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java` | ソース | parent_idクエリ |
| ChildrenAggregationBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java` | ソース | children集計 |
| ParentAggregationBuilder.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java` | ソース | parent集計 |
| ChildrenToParentAggregator.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java` | ソース | 集計ロジック |
| InternalChildren.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalChildren.java` | ソース | children内部結果 |
| InternalParent.java | `modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalParent.java` | ソース | parent内部結果 |
