# 機能設計書 22-left_join

## 概要

本ドキュメントは、dplyrパッケージにおける`left_join`関数の機能設計を定義する。`left_join`は、2つのデータフレームを左結合し、左側テーブル（x）のすべての行を保持しながら、右側テーブル（y）から一致する列を追加するMutating Join関数である。

### 本機能の処理概要

`left_join`関数は、SQLの LEFT OUTER JOIN に相当する操作を実行する。左側のデータフレーム（x）のすべての行を保持し、指定されたキー列に基づいて右側のデータフレーム（y）から一致する行のデータを結合する。yに一致する行がない場合、該当する列にはNAが設定される。

**業務上の目的・背景**：データ分析において、メインとなるデータセットを基準として、補助的な情報を別のテーブルから追加したいケースは非常に頻繁に発生する。例えば、売上データに顧客情報を追加する、商品リストにカテゴリ情報を付与する、などの操作である。`left_join`は、メインデータの完全性を保ちながら関連情報を統合するための標準的な手法として設計されている。すべてのメインレコードを保持することで、結合キーが存在しないレコードも失われないことが保証される。

**機能の利用シーン**：
- マスターデータに参照データを付与する場合（例：顧客IDから顧客名・住所を追加）
- 集計テーブルに詳細情報を追加する場合
- 複数のデータソースを統合してレポート用データセットを作成する場合
- 1対多または多対1の関係を持つデータの結合

**主要な処理内容**：
1. 結合キーの特定（by引数の解析またはcommon columnsの自動検出）
2. キー列の型の共通化（型が異なる場合のキャスト処理）
3. vctrs::vec_locate_matchesによるマッチング処理
4. 結果データフレームの構築（xの全行 + yからの一致列）
5. 重複列名への接尾辞付与

**関連システム・外部連携**：vctrパッケージのvec_locate_matches関数を使用してマッチング処理を実行。rlangパッケージによるNSE処理、tibbleパッケージとの相互運用性を提供。

**権限による制御**：特になし。Rの通常のオブジェクト操作権限に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | 本プロジェクトはRライブラリであり、UIは存在しない |

## 機能種別

データ変換 / 結合処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | data.frame / tibble | Yes | 左側（メイン）テーブル | data.frameまたはその拡張型 |
| y | data.frame / tibble | Yes | 右側（結合対象）テーブル | data.frameまたはその拡張型 |
| by | join_by / character / NULL | No | 結合キーの指定 | join_by()、文字ベクトル、またはNULL |
| copy | logical | No | 異なるソース間でのコピー許可 | TRUE/FALSE（デフォルト: FALSE） |
| suffix | character(2) | No | 重複列名への接尾辞 | 長さ2の文字ベクトル（デフォルト: c(".x", ".y")） |
| keep | logical / NULL | No | 両テーブルのキー列を保持するか | TRUE/FALSE/NULL |
| na_matches | character | No | NAの一致方法 | "na"（デフォルト）または"never" |
| multiple | character | No | 複数マッチの処理方法 | "all", "any", "first", "last" |
| unmatched | character | No | 非マッチ行の処理 | "drop"または"error" |
| relationship | character / NULL | No | 期待される関係性 | "one-to-one", "one-to-many", "many-to-one", "many-to-many", NULL |

### 入力データソース

R環境内のdata.frame、tibble、またはdbplyr/dtplyrによるリモートテーブル

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | data.frame / tibble | xと同じ型で、yの列が追加されたデータフレーム |

### 出力先

R環境内のオブジェクトとして返却

## 処理フロー

### 処理シーケンス

```
1. 引数の検証
   └─ check_dots_empty0: 不正な追加引数のチェック
2. データソースの同期
   └─ auto_copy: 異なるソースの場合のコピー処理
3. join_mutate関数への委譲
   ├─ na_matchesの検証
   ├─ keepの検証
   ├─ by引数の解析（join_by_commonまたはas_join_by）
   ├─ join_cols: 出力列の決定
   ├─ join_cast_common: キー列の型統一
   ├─ join_rows: マッチング処理
   └─ 結果の構築
4. dplyr_reconstruct: 出力型の復元
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[check_dots_empty0]
    B --> C[auto_copy: yをxのソースにコピー]
    C --> D[join_mutate呼び出し]
    D --> E[na_matches/keep検証]
    E --> F{by指定あり?}
    F -->|No| G[join_by_common: 共通列検出]
    F -->|Yes| H[as_join_by: by解析]
    G --> I[join_cols: 出力列計算]
    H --> I
    I --> J[join_cast_common: 型統一]
    J --> K[join_rows: マッチング]
    K --> L[結果データフレーム構築]
    L --> M[dplyr_reconstruct]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | 左テーブル保持 | xのすべての行を結果に含める | 常に適用 |
| BR-22-02 | 非マッチNA | yに一致しない行はyの列がNAになる | 常に適用 |
| BR-22-03 | 共通列自動検出 | by未指定時は共通列名で結合 | by=NULL時 |
| BR-22-04 | 重複列名処理 | 非キー列の重複にはsuffixを付与 | 列名重複時 |
| BR-22-05 | 多対多警告 | 等価結合で多対多関係を検出時に警告 | relationship=NULL時 |
| BR-22-06 | キー列マージ | keep=FALSEまたはNULLの等価結合でキー列をマージ | 等価結合時 |

### 計算ロジック

- **マッチング**: vctrs::vec_locate_matchesを使用して効率的なマッチング
- **型変換**: vec_ptype2/vec_castによる共通型への変換
- **行インデックス**: マッチしない行はNA、マッチする行は対応するyのインデックス

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

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

本機能はインメモリ処理のため、データベース操作は発生しない。ただし、dbplyrバックエンドと連携した場合、SQLのLEFT JOINクエリが生成される。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| dplyr_error_join | 結合エラー | キー列の型が互換性なし | 同じ型に変換するか結合キーを見直す |
| dplyr_error_join_matches_remaining | 非マッチエラー | unmatched="error"でyに非マッチ行あり | 非マッチ行を確認するかunmatched="drop"に変更 |
| dplyr_error_join_relationship | 関係性エラー | relationship指定に違反 | データの関係性を確認 |

### リトライ仕様

該当なし（同期処理）

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

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

## パフォーマンス要件

- 計算量: O(n + m) ～ O(n * m)（nはxの行数、mはyの行数、マッチングパターンに依存）
- メモリ使用量: 最大で結果行数分のメモリを消費

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

特になし。入力データの機密性は呼び出し側の責任で管理する。

## 備考

- 等価結合以外に、inequality join（>=, <=など）、rolling join（closest）、overlap joinもサポート
- 多対多の関係は警告を発生させるため、必要に応じてrelationship引数で明示的に指定

---

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

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

### 推奨読解順序

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

まず、join_by構造とjoin操作の基本概念を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | join-by.R | `R/join-by.R` | join_by関数とdplyr_join_byクラスの構造 |
| 1-2 | join.R | `R/join.R` | Mutating Joinsのドキュメント（1-247行目） |

**読解のコツ**: join_byオブジェクトはx（左キー列）、y（右キー列）、condition（演算子）、filter（ローリング結合用）の4つの主要フィールドを持つ。

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

left_join関数の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | join.R | `R/join.R` | left_join関数本体（297-339行目） |

**主要処理フロー**:
1. **297-307行目**: left_joinジェネリック関数定義
2. **311-323行目**: left_join.data.frameメソッドのパラメータ定義
3. **324行目**: check_dots_empty0 - 不正な追加引数のチェック
4. **325行目**: auto_copy - 異なるデータソースのコピー処理
5. **326-338行目**: join_mutate呼び出し - type="left"を指定

#### Step 3: 結合コア処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | join.R | `R/join.R` | join_mutate関数（687-802行目）- 主要な結合ロジック |
| 3-2 | join-rows.R | `R/join-rows.R` | join_rows関数（1-75行目）- マッチング処理 |
| 3-3 | join-cols.R | `R/join-cols.R` | join_cols関数（1-80行目）- 出力列の決定 |

**主要処理フロー**:
- **704-706行目**: na_matchesの検証
- **709行目**: keepの検証
- **714-726行目**: by引数の解析処理
- **728-735行目**: join_cols - 出力列名とインデックスの計算
- **740-745行目**: キー列の抽出と名前付け
- **743行目**: join_cast_common - キー列の型統一
- **750-763行目**: join_rows呼び出し - マッチング処理
- **771-772行目**: 結果データフレームの構築
- **801行目**: dplyr_reconstruct - 出力型の復元

#### Step 4: マッチング詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | join-rows.R | `R/join-rows.R` | dplyr_locate_matches関数（77-143行目） |
| 4-2 | join-by.R | `R/join-by.R` | join_by_common関数（376-394行目） |

**主要処理フロー**:
- **77-143行目**: dplyr_locate_matches - vctrs::vec_locate_matchesのラッパー
- **376-394行目**: join_by_common - 共通列名の自動検出

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

```
left_join(x, y, by, copy, suffix, keep, na_matches, multiple, unmatched, relationship)
    │
    ├─ check_dots_empty0(...)
    │
    ├─ auto_copy(x, y, copy)
    │
    └─ join_mutate(x, y, by, type="left", ...)
           │
           ├─ check_na_matches(na_matches)
           │
           ├─ check_keep(keep)
           │
           ├─ as_join_by(by) または join_by_common(x_names, y_names)
           │
           ├─ join_cols(x_names, y_names, by, suffix, keep)
           │
           ├─ join_cast_common(x_key, y_key, vars)
           │      └─ join_ptype_common(x, y, vars)
           │             └─ vec_ptype2(x, y)
           │
           ├─ join_rows(x_key, y_key, type, ...)
           │      └─ dplyr_locate_matches(needles, haystack, ...)
           │             └─ vctrs::vec_locate_matches(...)
           │
           └─ dplyr_reconstruct(out, x)
```

### データフロー図

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

x (data.frame) ──┐
                 ├─▶ join_cols() ─▶ 出力列構造
y (data.frame) ──┤
                 │
                 ├─▶ join_cast_common() ─▶ 型統一されたキー
                 │
                 └─▶ join_rows() ─▶ マッチング結果
                           │
                           ├─▶ x_slicer (xの行インデックス)
                           └─▶ y_slicer (yの行インデックス)
                                    │
                                    └─▶ vec_slice() ─▶ 結合結果 (data.frame)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| join.R | `R/join.R` | ソース | left_join, right_join, inner_join, full_join, semi_join, anti_join, nest_joinの定義 |
| join-rows.R | `R/join-rows.R` | ソース | join_rows, dplyr_locate_matches関数の定義 |
| join-cols.R | `R/join-cols.R` | ソース | join_cols, join_cast_common関数の定義 |
| join-by.R | `R/join-by.R` | ソース | join_by関数とby引数の解析処理 |
| join-common-by.R | `R/join-common-by.R` | ソース | 共通列の検出処理 |
