# 機能設計書 26-semi_join

## 概要

本ドキュメントは、dplyrパッケージにおける`semi_join`関数の機能設計を定義する。`semi_join`は、右側テーブル（y）に一致するキーを持つ左側テーブル（x）の行のみを返すFiltering Join関数である。

### 本機能の処理概要

`semi_join`関数は、SQLの WHERE EXISTS サブクエリに相当する操作を実行する。左側のデータフレーム（x）の行のうち、右側のデータフレーム（y）に一致するキーが存在するものだけを結果に含める。重要な特徴として、yの列は結果に追加されず、xの列のみが保持される。また、yに複数のマッチがあっても、xの行は重複しない。

**業務上の目的・背景**：別のテーブルの条件に基づいてデータをフィルタリングしたいが、そのテーブルの列自体は必要ない場合に使用する。例えば、「注文履歴のある顧客のみを顧客マスタから抽出する」「在庫のある商品のみを商品リストから選択する」などのケースである。`semi_join`は、サブクエリ的なフィルタリングを簡潔に表現するための関数として設計されている。

**機能の利用シーン**：
- 関連テーブルに存在するレコードのみを抽出するフィルタリング
- 条件テーブルを使用した動的なフィルタリング
- データの存在確認に基づくサブセット作成
- inner_joinの結果からxの列のみが必要な場合

**主要な処理内容**：
1. 結合キーの特定（by引数の解析またはcommon columnsの自動検出）
2. キー列の型の共通化（型が異なる場合のキャスト処理）
3. vctrs::vec_locate_matchesによるマッチング処理（multiple="any"で効率化）
4. マッチした行のインデックスでxをスライス
5. 結果データフレームの返却（xの列のみ）

**関連システム・外部連携**：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） |
| na_matches | character | No | NAの一致方法 | "na"（デフォルト）または"never" |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | data.frame / tibble | xと同じ型で、yにマッチした行のみを含む（xの列のみ） |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 引数の検証
   └─ check_dots_empty0: 不正な追加引数のチェック
2. データソースの同期
   └─ auto_copy: 異なるソースの場合のコピー処理
3. join_filter関数への委譲（type="semi"）
   ├─ na_matchesの検証
   ├─ by引数の解析（join_by_commonまたはas_join_by）
   ├─ join_cols: キー列の決定
   ├─ join_cast_common: キー列の型統一
   ├─ join_rows: マッチング処理（multiple="any"）
   └─ マッチした行のインデックスを取得
4. dplyr_row_slice: xの行をスライス
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[check_dots_empty0]
    B --> C[auto_copy: yをxのソースにコピー]
    C --> D[join_filter呼び出し type=semi]
    D --> E[na_matches検証]
    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: マッチング multiple=any]
    K --> L[マッチした行のインデックス取得]
    L --> M[dplyr_row_slice: xをスライス]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-26-01 | xの列のみ | 結果にはxの列のみが含まれ、yの列は追加されない | 常に適用 |
| BR-26-02 | 重複なし | yに複数のマッチがあっても、xの行は重複しない | 常に適用 |
| BR-26-03 | 行順序保持 | xの行順序を維持 | 常に適用 |
| BR-26-04 | 属性保持 | データフレーム属性（グループ化含む）を保持 | 常に適用 |
| BR-26-05 | 効率化 | multiple="any"で最初のマッチのみを検出 | 常に適用 |

### 計算ロジック

- **マッチング**: vctrs::vec_locate_matchesを使用
- **効率化**: multiple="any"で「少なくとも1つのマッチが存在するか」のみを判定
- **スライシング**: dplyr_row_sliceでマッチした行のみを抽出

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| dplyr_error_join | 結合エラー | キー列の型が互換性なし | 同じ型に変換するか結合キーを見直す |

### リトライ仕様

該当なし（同期処理）

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

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

## パフォーマンス要件

- 計算量: O(n + m)（multiple="any"により効率的）
- メモリ使用量: 結果行数分のメモリを消費

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

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

## 備考

- `inner_join(x, y) |> select(names(x))`と類似するが、重複行が発生しない点が異なる
- `filter(x, key %in% y$key)`と同様だが、複数キーや型変換が自動で処理される
- anti_joinとは逆の操作（マッチする行を保持 vs 保持しない）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | join.R | `R/join.R` | Filtering Joinsのドキュメント（433-472行目） |

**読解のコツ**: Filtering Joinsは「filter rows from x based on the presence or absence of matches in y」。

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

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

**主要処理フロー**:
1. **474-478行目**: semi_joinジェネリック関数定義
2. **482-489行目**: semi_join.data.frameメソッドのパラメータ定義
3. **490行目**: check_dots_empty0
4. **491行目**: auto_copy
5. **492-499行目**: join_filter呼び出し - type="semi"を指定

#### Step 3: フィルタリング結合コア処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | join.R | `R/join.R` | join_filter関数（804-889行目） |

**主要処理フロー**:
- **854行目**: multiple = "any" - マッチ検出のみで十分
- **858行目**: relationship = NULL - 関係性チェック不要
- **862行目**: unmatched = "drop" - 非マッチは常に除外
- **879-881行目**: type="semi"の場合、rows$xをそのまま使用
- **888行目**: dplyr_row_slice(x, idx) - xの行をスライス

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

```
semi_join(x, y, by, copy, na_matches)
    │
    ├─ check_dots_empty0(...)
    │
    ├─ auto_copy(x, y, copy)
    │
    └─ join_filter(x, y, by, type="semi", na_matches)
           │
           ├─ join_cols(x_names, y_names, by)
           │
           ├─ join_cast_common(x_key, y_key, vars)
           │
           ├─ join_rows(x_key, y_key, type="semi", multiple="any", ...)
           │      └─ dplyr_locate_matches(...)
           │
           ├─ idx = rows$x  # マッチした行のインデックス
           │
           └─ dplyr_row_slice(x, idx)
```

### データフロー図

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

x (data.frame) ──┐
                 ├─▶ join_rows(type="semi", multiple="any")
y (data.frame) ──┤           │
                 │           └─▶ マッチした行のインデックス
                 │
                 └─▶ dplyr_row_slice(x, idx)
                           │
                           └─▶ フィルタリング結果 (data.frame)
                                    │
                                    └─▶ xの列のみ、yにマッチした行のみ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| join.R | `R/join.R` | ソース | semi_join, anti_join関数の定義 |
| join-rows.R | `R/join-rows.R` | ソース | join_rows関数の定義 |
| join-cols.R | `R/join-cols.R` | ソース | join_cols関数の定義 |
| join-by.R | `R/join-by.R` | ソース | join_by関数の定義 |
