# 機能設計書 15-結合・マージ

## 概要

本ドキュメントは、Nushellにおける結合・マージ機能の設計について記述する。merge、merge deep、join、zipコマンドを通じて、複数のデータ構造を組み合わせる機能を提供する。

### 本機能の処理概要

結合・マージ機能は、レコードやテーブルを様々な方法で結合する。mergeは同一キーの値を上書き、merge deepは再帰的にマージ、joinはSQLライクな結合、zipは要素をペアにする。

**業務上の目的・背景**：データ処理において、複数のソースからのデータを統合することは一般的な要件である。設定ファイルのマージ、テーブルの結合、リストの組み合わせなど、データ統合の多様なパターンに対応する。

**機能の利用シーン**：
- `{a: 1} | merge {b: 2}` でレコードを結合
- `{a: {b: 1}} | merge deep {a: {c: 2}}` で再帰的マージ
- `$table1 | join $table2 id` でテーブルを結合
- `[1 2 3] | zip [a b c]` でリストをペアに

**主要な処理内容**：
1. 入力データと結合データの型検証
2. マージ戦略（Shallow/Deep）の決定
3. レコード/テーブルのマージ実行
4. 結果を適切な形式で返却

**関連システム・外部連携**：特になし。純粋なインメモリ処理。

**権限による制御**：特になし。純粋なデータ操作機能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインREPL | 主画面 | 結合コマンドの入力と結果表示 |

## 機能種別

データ操作（結合処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | Record/Table | Yes (merge) | マージする値 | recordまたはtable |
| --strategy | String | No (merge deep) | リストマージ戦略 | table/overwrite/append/prepend |
| right-table | Table | Yes (join) | 右テーブル | table型 |
| left-on | String | Yes (join) | 左結合キー | 文字列 |
| right-on | String | No (join) | 右結合キー | 文字列（デフォルト: left-onと同じ） |
| --inner/--left/--right/--outer | Switch | No (join) | 結合タイプ | いずれか一つ |
| --prefix/--suffix | String | No (join) | 右カラム名装飾 | 文字列 |
| other | Any/Closure | Yes (zip) | zip対象 | 値またはクロージャ |

### 入力データソース

- パイプライン入力: Record、Table、List、Range
- merge/join: recordまたはtable
- zip: List、Range

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| merged_record | Record | (merge) マージ済みレコード |
| merged_table | Table | (merge) マージ済みテーブル |
| joined_table | Table | (join) 結合済みテーブル |
| zipped_list | List<List> | (zip) ペアのリスト |

### 出力先

- パイプライン出力: `PipelineData`として後続コマンドへ渡す

## 処理フロー

### 処理シーケンス

```
1. コマンド引数の解析
   └─ merge: value、strategyを取得
   └─ join: right-table、結合キー、結合タイプを取得
   └─ zip: other値またはクロージャを取得

2. 型検証
   └─ merge: 両方がrecordまたは両方がtableか検証
   └─ join: 両方がtableか検証

3. 結合処理実行
   └─ merge: do_merge()でマージ
   └─ join: lookup_tableを構築しjoin_rows()
   └─ zip: イテレータをzip

4. 結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|merge| C[型検証]
    B -->|merge deep| D[strategy解析]
    B -->|join| E[結合タイプ決定]
    B -->|zip| F[other評価]

    C --> G{両方record?}
    G -->|Yes| H[merge_records]
    G -->|No| I{両方table?}
    I -->|Yes| J[merge_tables]
    I -->|No| K[型エラー]

    D --> L{strategy}
    L -->|table| M[Elementwise]
    L -->|append| N[Append]
    L -->|prepend| O[Prepend]
    L -->|overwrite| P[Overwrite]
    M --> Q[再帰的マージ]
    N --> Q
    O --> Q
    P --> Q

    E --> R{結合タイプ}
    R -->|inner| S[Inner Join]
    R -->|left| T[Left Join]
    R -->|right| U[Right Join]
    R -->|outer| V[Outer Join]
    S --> W[lookup_table構築]
    T --> W
    U --> W
    V --> W
    W --> X[join_rows実行]

    F --> Y{Closure?}
    Y -->|Yes| Z[クロージャ実行]
    Y -->|No| AA[into_pipeline_data]
    Z --> AB[イテレータzip]
    AA --> AB
    AB --> AC[ペア作成]

    H --> AD[終了]
    Q --> AD
    X --> AD
    AC --> AD
    K --> AD
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 同名キー上書き | merge時に同名キーは右側の値で上書き | merge使用時 |
| BR-02 | 再帰マージ | merge deepはネストしたrecordを再帰的にマージ | merge deep使用時 |
| BR-03 | 結合キー一意 | join出力で共有キーは一度のみ出力 | join使用時 |
| BR-04 | 短い方で終了 | zipは短い方のリストで終了 | zip使用時 |

### 計算ロジック

MergeStrategy:
- Shallow: 同名キーを単純に上書き
- Deep(Elementwise): テーブルは要素ごとにマージ
- Deep(Append): リストを末尾に追加
- Deep(Prepend): リストを先頭に追加
- Deep(Overwrite): リストを上書き

JoinType:
- Inner: 両方に存在するキーのみ
- Left: 左テーブルの全行（右がない場合null）
- Right: 右テーブルの全行（左がない場合null）
- Outer: 両方の全行

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

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| OnlySupportsThisInputType | 型エラー | record/table以外の入力 | サポート型を入力 |
| IncorrectValue | パラメータエラー | 無効なstrategy値 | table/overwrite/append/prependを指定 |
| UnsupportedInput | 入力エラー | join引数の型不一致 | 正しい型を指定 |

### リトライ仕様

該当なし（同期処理のため）

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

該当なし（状態変更を伴わない）

## パフォーマンス要件

- join: HashMapでルックアップテーブルを構築（O(n)）
- merge: レコード内キー走査（O(k) k=キー数）
- zip: ストリーミング処理可能

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

- zipのクロージャ: エンジンのサンドボックス内で実行

## 備考

- joinの--prefix/--suffixで右テーブルカラム名の衝突を回避可能
- merge deepのstrategyでリスト結合方法を細かく制御可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | common.rs | `crates/nu-command/src/filters/merge/common.rs` | MergeStrategy/ListMerge列挙型(3-21行目) |
| 1-2 | join.rs | `crates/nu-command/src/filters/join.rs` | JoinType列挙型(11-16行目) |

**読解のコツ**: `MergeStrategy`はShallow/Deep、`ListMerge`はリスト結合方法を定義。JoinTypeはSQLの結合タイプに対応。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | merge_.rs | `crates/nu-command/src/filters/merge/merge_.rs` | run関数(84-103行目)で基本フローを理解 |
| 2-2 | deep.rs | `crates/nu-command/src/filters/merge/deep.rs` | run関数(124-158行目)でstrategy処理を理解 |
| 2-3 | join.rs | `crates/nu-command/src/filters/join.rs` | run関数(79-125行目)で結合処理を理解 |
| 2-4 | zip.rs | `crates/nu-command/src/filters/zip.rs` | run関数(93-116行目)でzip処理を理解 |

**主要処理フロー (merge)**:
1. **92行目**: `call.req()`でmerge_valueを取得
2. **97行目**: `input.into_value()`で入力を収集
3. **99行目**: `typecheck_merge()`で型検証
4. **101行目**: `do_merge()`でマージ実行

**主要処理フロー (join)**:
1. **87-91行目**: 結合キーを取得
2. **93行目**: `join_type()`で結合タイプを決定
3. **100行目**: 入力を収集
4. **109行目**: `join()`関数で結合実行

#### Step 3: マージロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | common.rs | `crates/nu-command/src/filters/merge/common.rs` | do_merge関数(54-111行目)でマージ分岐を理解 |
| 3-2 | common.rs | `crates/nu-command/src/filters/merge/common.rs` | merge_records関数(142-175行目)でレコードマージを理解 |

**主要処理フロー**:
- **64-80行目**: strategyとvalue型に応じた分岐
- **150-152行目**: Shallowは単純にinsert
- **165-170行目**: Deepは再帰的にdo_merge呼び出し

#### Step 4: 結合ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | join.rs | `crates/nu-command/src/filters/join.rs` | join関数(183-286行目)で全体フローを理解 |
| 4-2 | join.rs | `crates/nu-command/src/filters/join.rs` | lookup_table関数(397-414行目)でルックアップ構築を理解 |
| 4-3 | join.rs | `crates/nu-command/src/filters/join.rs` | join_rows関数(291-381行目)で行結合を理解 |

**主要処理フロー**:
- **234行目**: `lookup_table()`でHashMapを構築
- **248-261行目**: `join_rows()`で行を結合
- **419-462行目**: `merge_records()`でカラム名衝突を処理

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

```
Merge::run() / MergeDeep::run() / Join::run() / Zip::run()
    │
    ├─ Merge:
    │      ├─ typecheck_merge()
    │      │      └─ is_list_of_records()
    │      │
    │      └─ do_merge(strategy=Shallow)
    │             └─ merge_records()
    │
    ├─ MergeDeep:
    │      ├─ strategy解析
    │      │
    │      └─ do_merge(strategy=Deep)
    │             ├─ merge_records() [再帰]
    │             │      └─ do_merge()
    │             │
    │             └─ merge_tables()
    │
    ├─ Join:
    │      ├─ join_type()
    │      │
    │      └─ join()
    │             ├─ lookup_table()
    │             │
    │             └─ join_rows()
    │                    └─ merge_records()
    │
    └─ Zip:
           ├─ (Closure) ClosureEvalOnce::run_with_input()
           │
           └─ input.into_iter().zip(other)
                  └─ Value::list([x, y])
```

### データフロー図

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

                       ┌─────────────────────────────────┐
PipelineData ─────────▶│         merge処理                │
     │                 │  typecheck_merge()              │
     │                 │  do_merge(input, merge_value)   │
merge_value ──────────▶│     └─ merge_records()          │
     │                 └─────────────────────────────────┘
     │                                  │
     │                                  ▼
     │                          Record/Table (merged)
     │
     │                 ┌─────────────────────────────────┐
     ├────────────────▶│           join処理               │
     │                 │  lookup_table(right)            │
right_table ──────────▶│  join_rows(left, lookup)        │
     │                 │     └─ merge_records()          │
     │                 └─────────────────────────────────┘
     │                                  │
     │                                  ▼
     │                           Table (joined)
     │
     │                 ┌─────────────────────────────────┐
     └────────────────▶│           zip処理                │
                       │  input.into_iter()              │
other ────────────────▶│  .zip(other)                    │
                       │  .map(|(x,y)| [x,y])            │
                       └─────────────────────────────────┘
                                        │
                                        ▼
                                 List<[a, b]>
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| merge_.rs | `crates/nu-command/src/filters/merge/merge_.rs` | ソース | mergeコマンド実装 |
| deep.rs | `crates/nu-command/src/filters/merge/deep.rs` | ソース | merge deepコマンド実装 |
| common.rs | `crates/nu-command/src/filters/merge/common.rs` | ソース | マージ共通ロジック |
| join.rs | `crates/nu-command/src/filters/join.rs` | ソース | joinコマンド実装 |
| zip.rs | `crates/nu-command/src/filters/zip.rs` | ソース | zipコマンド実装 |
| mod.rs | `crates/nu-command/src/filters/merge/mod.rs` | ソース | mergeモジュールエクスポート |
