# バッチ設計書 12-exportBatchProcessor

## 概要

本ドキュメントは、Payload CMSのインポート/エクスポートプラグインにおけるエクスポートバッチプロセッサ（exportBatchProcessor）のバッチ処理設計を定義する。

### 本バッチの処理概要

exportBatchProcessorは、コレクションのドキュメントをバッチ単位で取得・変換し、CSV/JSON形式でエクスポートするための汎用バッチ処理ユーティリティである。

**業務上の目的・背景**：大規模なコレクションからデータをエクスポートする際、全件を一度にメモリに読み込むとメモリ不足やタイムアウトが発生する。本バッチプロセッサは、ページネーションを活用してドキュメントを分割取得し、段階的にエクスポートファイルを構築することで、大量データのエクスポートを安定的に実行する。バックアップ、データ移行、外部システム連携、レポート作成など多様な用途に対応する。

**バッチの実行タイミング**：エクスポートタスク（createCollectionExport）から内部的に呼び出される。ユーザーが管理画面からエクスポートを開始したタイミング、または外部APIからの呼び出し時に実行される。

**主要な処理内容**：
1. エクスポート設定（コレクション、フィルタ条件、ソート順等）の受け取り
2. ページネーションによるドキュメントのバッチ取得（payload.find）
3. 各ドキュメントへの変換関数（transformDoc）適用
4. CSV形式の場合はカラム名の動的検出と追跡
5. 最大ドキュメント数（maxDocs）に達するまでの繰り返し処理
6. ストリーミングエクスポート対応（AsyncGenerator）

**前後の処理との関連**：createCollectionExportタスクから呼び出される。インポートバッチプロセッサ（importBatchProcessor）と対をなし、エクスポートしたデータを別環境にインポートする用途で連携する。

**影響範囲**：読み取り専用の処理であり、データベースへの書き込みは行わない。エクスポートファイルの生成（メモリまたはストリーム）に影響。

## バッチ種別

データ連携 / データエクスポート / バッチ処理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（ユーザー操作時またはAPI呼び出し時） |
| 実行時刻 | 任意 |
| 実行曜日 | 任意 |
| 実行日 | 任意 |
| トリガー | イベント（エクスポートタスク経由） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| コレクション存在 | エクスポート対象のコレクションがPayload設定に存在すること |
| 有効なリクエスト | PayloadRequestオブジェクトが有効であること |
| transformDoc関数 | ドキュメント変換関数が提供されていること |

### 実行可否判定

- コレクションが存在しない場合はpayload.findでエラー
- maxDocsに達した場合は処理を終了
- hasNextPageがfalseの場合は処理を終了

## 入力仕様

### 入力パラメータ

#### createExportBatchProcessor オプション

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| batchSize | number | No | 100 | 1バッチあたりの取得件数 |
| debug | boolean | No | false | デバッグログ出力の有無 |

#### processExport オプション

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| collectionSlug | string | Yes | - | エクスポート対象コレクションのスラッグ |
| findArgs | ExportFindArgs | Yes | - | payload.findに渡す引数 |
| format | 'csv' \| 'json' | Yes | - | エクスポート形式 |
| maxDocs | number | Yes | - | エクスポート最大ドキュメント数 |
| req | PayloadRequest | Yes | - | Payloadリクエストオブジェクト |
| startPage | number | No | 1 | 開始ページ番号 |
| transformDoc | (doc) => Record | Yes | - | ドキュメント変換関数 |

#### ExportFindArgs

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| collection | string | Yes | - | コレクションスラッグ |
| depth | number | Yes | - | リレーション展開の深さ |
| draft | boolean | Yes | - | ドラフトを含めるか |
| limit | number | Yes | - | 1ページあたりの取得件数 |
| locale | string | No | - | 取得対象ロケール |
| overrideAccess | boolean | Yes | - | アクセス制御をオーバーライドするか |
| page | number | No | - | ページ番号（内部で設定） |
| select | SelectType | No | - | 取得フィールドの選択 |
| sort | Sort | No | - | ソート条件 |
| user | TypedUser | No | - | 実行ユーザー |
| where | Where | No | - | フィルタ条件 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| 対象コレクション | DB | エクスポート対象のドキュメント |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| ExportResult | メモリ | エクスポート結果（docs, columns, fetchedCount） |

### ExportResult構造

| 項目名 | 型 | 説明 |
|--------|-----|------|
| docs | Record<string, unknown>[] | 変換済みドキュメント配列 |
| columns | string[] | CSV用カラム名配列（出現順） |
| fetchedCount | number | 取得ドキュメント総数 |

### 出力ファイル仕様

本プロセッサはメモリ上にデータを構築する。ファイル出力は呼び出し元（createCollectionExport）が担当。

## 処理フロー

### 処理シーケンス

```
1. バッチプロセッサ生成
   └─ createExportBatchProcessor(options)でインスタンス生成
2. processExport呼び出し
   └─ エクスポート処理のメイン実行
   2.1. 初期化
        └─ docs配列、columnsSet、currentPage、fetched初期化
   2.2. バッチループ開始
        └─ hasNextPage && fetched < maxDocsの間ループ
   2.3. 残数計算
        └─ remaining = maxDocs - fetched
   2.4. ドキュメント取得
        └─ payload.find(findArgs, limit: min(batchSize, remaining), page: currentPage)
   2.5. ドキュメント変換
        └─ 各docに対してtransformDoc適用
   2.6. カラム追跡（CSV時）
        └─ 変換後オブジェクトのキーをcolumnsに追加
   2.7. 結果追加
        └─ 変換済みドキュメントをdocs配列に追加
   2.8. カウンタ更新
        └─ fetched += result.docs.length, currentPage++
3. 結果返却
   └─ { columns, docs, fetchedCount }を返却
```

### フローチャート

```mermaid
flowchart TD
    A[processExport開始] --> B[初期化]
    B --> C{remaining > 0?}
    C -->|No| D[結果返却]
    C -->|Yes| E[payload.find実行]
    E --> F{デバッグモード?}
    F -->|Yes| G[デバッグログ出力]
    F -->|No| H[ドキュメントループ]
    G --> H
    H --> I[transformDoc適用]
    I --> J{format == 'csv'?}
    J -->|Yes| K[カラム追跡]
    J -->|No| L[docs配列に追加]
    K --> L
    L --> M{次のドキュメント?}
    M -->|Yes| I
    M -->|No| N[fetched更新]
    N --> O[currentPage++]
    O --> P{hasNextPage && fetched < maxDocs?}
    P -->|Yes| C
    P -->|No| D
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ドキュメント取得 | {collectionSlug} | SELECT | ページネーションによるバッチ取得 |

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

#### 対象コレクション（{collectionSlug}）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | findArgs.selectで指定 | findArgs.whereで指定 | ページネーション（page, limit） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| COLLECTION_NOT_FOUND | 設定エラー | コレクションが存在しない | コレクション設定を確認 |
| ACCESS_DENIED | 認証エラー | ユーザーに読み取り権限がない | ユーザー権限を確認 |
| QUERY_ERROR | システムエラー | 不正なwhere条件 | クエリ条件を確認 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 呼び出し元の設定に依存 |
| リトライ間隔 | 呼び出し元の設定に依存 |
| リトライ対象エラー | 一時的なDB接続エラー等 |

### 障害時対応

- payload.findでエラーが発生した場合は例外がスローされる
- 呼び出し元でエラーをキャッチして適切に処理する必要がある
- 部分的な結果は返却されない（エラー発生時点で処理中断）

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | なし（読み取り専用） |
| コミットタイミング | N/A |
| ロールバック条件 | N/A |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | maxDocsに依存 |
| 目標処理時間 | ドキュメント数とtransformDocの複雑さに依存 |
| メモリ使用量上限 | 全変換済みドキュメントがメモリに保持される |

**ストリーミングモード**: 大量データの場合は`streamExport`を使用することで、メモリ使用量を抑えてバッチごとに処理可能。

## 排他制御

- 読み取り専用のため排他制御なし
- 同時実行による競合なし

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| デバッグログ | バッチ処理ごと（debugモード時） | バッチ番号、ドキュメント数 |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 呼び出し元の設定に依存 | 呼び出し元の監視機構 |

## 備考

### 提供メソッド

本プロセッサは以下の3つのメソッドを提供する：

1. **processExport**: 全ドキュメントを取得・変換してExportResultを返却
2. **streamExport**: AsyncGeneratorとしてバッチごとにyield（メモリ効率的）
3. **discoverColumns**: CSVエクスポート用にカラム名を事前検出

### ストリーミングエクスポート

```typescript
const processor = createExportBatchProcessor({ batchSize: 100 })

for await (const batch of processor.streamExport({ ... })) {
  // バッチごとに処理（ファイル書き込み等）
  console.log(`Got ${batch.docs.length} documents`)
}
```

### カラム検出

CSV形式では、各ドキュメントの変換結果からカラム名を動的に検出する。カラムの出現順序は、ドキュメントでの出現順に保持される。
