# 機能設計書 43-インポートエクスポート

## 概要

本ドキュメントは、Payload CMSにおけるデータのインポート/エクスポート機能（plugin-import-export）の設計を記載する。本プラグインは、コレクションのドキュメントをCSV/JSON形式でエクスポートし、同形式でインポートする機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：コンテンツ管理において、データの一括入出力は重要な運用機能である。他システムからのデータ移行、バックアップ、一括更新、データ分析などのユースケースに対応するため、標準的なファイル形式でのインポート/エクスポート機能を提供する。

**機能の利用シーン**：
- 他システムからのコンテンツ移行（CSVインポート）
- 定期的なデータバックアップ（JSONエクスポート）
- 外部ツールでのデータ分析（CSVエクスポート）
- コンテンツの一括更新（インポートのupsertモード）
- ステージング環境から本番環境へのデータ移行
- 既存ドキュメントの一括編集

**主要な処理内容**：
1. コレクションへのエクスポート/インポートメニュー追加
2. ジョブキューまたは同期でのエクスポート処理
3. CSV/JSON形式でのデータ変換（フラット化/アンフラット化）
4. バッチ処理による大量データの効率的な処理
5. プレビュー機能による処理前確認
6. インポート時のcreate/update/upsertモード対応
7. フィールド単位での無効化設定
8. カスタム変換関数（toCSV/fromCSV）のサポート

**関連システム・外部連携**：
- ジョブキュー（Payloadのjobs機能）
- ファイルシステム（エクスポートファイルの一時保存）
- Admin UI（メニュー項目、プレビュー画面）

**権限による制御**：
- コレクションのread/create/update権限に基づくアクセス制御
- エクスポート/インポートコレクションのオーバーライドで独自のアクセス制御が可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | 一覧画面 | 主画面 | エクスポート/インポートメニューの表示 |
| - | エクスポートプレビュー画面 | サブ画面 | エクスポート設定とプレビュー |
| - | インポートプレビュー画面 | サブ画面 | インポートファイルのプレビューと実行 |

## 機能種別

データ変換 / バッチ処理 / CRUD操作

## 入力仕様

### 入力パラメータ（プラグイン設定）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| collections | PluginCollectionConfig[] | Yes | 対象コレクションの設定 | - |
| exportLimit | Limit | No | グローバルエクスポート上限 | 数値または関数 |
| importLimit | Limit | No | グローバルインポート上限 | 数値または関数 |
| batchSize | number | No | バッチサイズ | デフォルト: 100 |
| debug | boolean | No | デバッグモード | デフォルト: false |
| overrideExportCollection | CollectionOverride | No | エクスポートコレクションのオーバーライド | - |
| overrideImportCollection | CollectionOverride | No | インポートコレクションのオーバーライド | - |

### 入力パラメータ（エクスポート）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| collectionSlug | string | Yes | 対象コレクションのslug | 必須 |
| format | 'csv' \| 'json' | Yes | 出力形式 | 必須 |
| fields | string[] | No | 出力フィールドの指定 | - |
| where | Where | No | フィルタ条件 | - |
| sort | Sort | No | ソート条件 | - |
| drafts | 'yes' \| 'no' | No | 下書きの含有 | デフォルト: 'no' |
| locale | string | No | ロケール | - |

### 入力パラメータ（インポート）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| collectionSlug | string | Yes | 対象コレクションのslug | 必須 |
| format | 'csv' \| 'json' | Yes | 入力形式 | 必須 |
| importMode | 'create' \| 'update' \| 'upsert' | Yes | インポートモード | 必須 |
| matchField | string | No | マッチングフィールド | デフォルト: 'id' |
| file | File | Yes | インポートファイル | 必須 |

### 入力データソース

- Payload設定ファイルでのプラグイン設定
- Admin UIからのユーザー入力
- アップロードされたCSV/JSONファイル

## 出力仕様

### 出力データ（エクスポート）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| file | Buffer | エクスポートされたファイルデータ |
| filename | string | ファイル名（{name}-{collection}.{format}） |
| columns | string[] | CSVのカラム名一覧 |
| docs | object[] | エクスポートされたドキュメント |

### 出力データ（インポート）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| imported | number | 新規作成された件数 |
| updated | number | 更新された件数 |
| total | number | 処理された総件数 |
| errors | Error[] | エラー一覧 |

### 出力先

- エクスポート: ダウンロード、またはエクスポートコレクションへの保存
- インポート: 対象コレクションへのドキュメント作成/更新
- インポートコレクション: インポート結果の記録

## 処理フロー

### エクスポート処理シーケンス

```
1. エクスポートリクエスト受信
2. アクセス権限の確認
3. ドキュメント件数の取得
   └─ maxLimitとの比較
4. バッチ処理の開始
   ├─ ジョブキュー（disableJobsQueue=false）
   └─ 同期処理（disableJobsQueue=true）
5. 各バッチでのドキュメント取得
6. データ変換
   ├─ CSVの場合: flattenObject
   └─ JSONの場合: そのまま
7. 無効フィールドの除外
8. toCSV関数の適用（設定時）
9. ファイル生成
   ├─ CSV: BOM付きCSV生成
   └─ JSON: JSON配列生成
10. 結果の返却
    ├─ ダウンロード（download=true）
    └─ エクスポートコレクションへの保存
```

### インポート処理シーケンス

```
1. インポートリクエスト受信
2. ユーザー認証の確認
3. ファイルのパース
   ├─ CSV: parseCSV
   └─ JSON: parseJSON
4. データの変換（CSVの場合）
   └─ unflattenObject
5. maxLimitの確認
6. 無効フィールドの除外
7. バッチ処理の開始
8. 各ドキュメントの処理
   ├─ create: payload.create
   ├─ update: payload.findByID + payload.update
   └─ upsert: findByID || create
9. バージョン状態の設定
10. 結果の集計と返却
```

### フローチャート

```mermaid
flowchart TD
    A[インポートリクエスト] --> B{ユーザー認証?}
    B -->|No| C[401エラー]
    B -->|Yes| D[ファイルパース]
    D --> E{maxLimit超過?}
    E -->|Yes| F[400エラー]
    E -->|No| G[無効フィールド除外]
    G --> H[バッチ処理開始]
    H --> I{インポートモード?}
    I -->|create| J[payload.create]
    I -->|update| K[payload.update]
    I -->|upsert| L{ドキュメント存在?}
    L -->|Yes| K
    L -->|No| J
    J --> M[結果集計]
    K --> M
    M --> N{次のバッチ?}
    N -->|Yes| H
    N -->|No| O[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | デフォルトバッチサイズ | バッチサイズは100 | batchSize未指定時 |
| BR-02 | 公開ドキュメント優先 | drafts='no'の場合は_status='published'のみエクスポート | バージョン有効コレクション |
| BR-03 | upsertモード | matchFieldでドキュメントを検索し、存在すれば更新、なければ作成 | importMode='upsert' |
| BR-04 | デフォルトマッチフィールド | マッチフィールドのデフォルトは'id' | matchField未指定時 |
| BR-05 | フィールド無効化 | custom['plugin-import-export'].disabled=trueのフィールドは除外 | フィールド設定 |
| BR-06 | BOM付きCSV | CSVは最初のバッチのみBOMを付与 | CSV形式エクスポート |
| BR-07 | スキーマベースカラム | CSVカラムはスキーマから推論し、データからも補完 | CSV形式エクスポート |

### 計算ロジック

**maxExportDocuments計算**:
```typescript
if (maxLimit > 0 && incomingLimit > 0) {
  maxExportDocuments = Math.min(incomingLimit, maxLimit)
} else if (maxLimit > 0) {
  maxExportDocuments = maxLimit
} else {
  maxExportDocuments = incomingLimit || undefined
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| エクスポート | 対象コレクション | SELECT | ドキュメント取得 |
| エクスポート保存 | payload-exports | INSERT/UPDATE | エクスポートファイル保存 |
| インポート（create） | 対象コレクション | INSERT | 新規ドキュメント作成 |
| インポート（update） | 対象コレクション | UPDATE | 既存ドキュメント更新 |
| インポート記録 | payload-imports | INSERT | インポート結果保存 |

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

#### payload-exports（エクスポートコレクション）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | file | エクスポートファイル | アップロードコレクション |
| UPDATE | file | 更新ファイル | 再エクスポート時 |

#### payload-imports（インポートコレクション）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | file | インポートファイル | アップロードコレクション |
| INSERT | imported, updated, errors | 結果サマリー | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | APIError | ユーザー認証なし | ログインしてから再実行 |
| 400 | APIError | コレクションslug未指定 | collectionSlugを指定 |
| 400 | APIError | ファイルデータなし | ファイルをアップロード |
| 400 | APIError | maxLimit超過 | ファイルを分割して再実行 |
| 404 | APIError | コレクション未発見 | 正しいslugを指定 |
| - | Error | インポート時の個別エラー | errorsに記録、処理は継続 |

### リトライ仕様

- ジョブキュー使用時はPayloadのジョブリトライ機能を利用
- 同期処理時のリトライは未実装

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

- インポート時の各ドキュメント処理は独立したトランザクション
- 一部のドキュメントでエラーが発生しても、他のドキュメントの処理は継続
- エラーはerrors配列に記録され、最終結果に含まれる

## パフォーマンス要件

- バッチサイズによる効率的なメモリ使用（デフォルト100件）
- ストリーミングエクスポート（download=true時）でメモリ効率化
- スキーマベースのカラム推論で空のエクスポートにも対応

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

- エクスポート/インポートはPayloadのアクセス制御に従う
- overrideAccess=falseでユーザー権限を尊重
- アクセス拒否された場合は空のエクスポートを生成

## 備考

- CSVの配列フィールドはフラット化される（array.0.field形式）
- リレーションシップフィールドはIDで出力される
- fromCSV/toCSV関数でカスタム変換が可能
- 翻訳は多言語に対応

---

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

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

### 推奨読解順序

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

プラグイン設定とインポート/エクスポートの型を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/plugin-import-export/src/types.ts` | ImportExportPluginConfig、ExportConfig、ImportConfig |

**読解のコツ**: `ToCSVFunction`と`FromCSVFunction`がカスタム変換のインターフェース。`Limit`は数値または関数（リクエストコンテキストベース）で柔軟な制限が可能。

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

プラグインのメインロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/plugin-import-export/src/index.ts` | importExportPlugin関数の全体フロー |

**主要処理フロー**:
1. **24-28行目**: getPluginCollectionsでエクスポート/インポートコレクション取得
2. **44-49行目**: コレクション重複チェックと追加
3. **55-57行目**: ImportExportProviderの注入
4. **60-61行目**: ジョブタスクの追加
5. **108-128行目**: リストメニュー項目の追加
6. **155-187行目**: collection.customへの設定保存

#### Step 3: エクスポート処理を理解する

エクスポートの中核処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | createExport.ts | `packages/plugin-import-export/src/export/createExport.ts` | createExport関数 |
| 3-2 | batchProcessor.ts | `packages/plugin-import-export/src/export/batchProcessor.ts` | バッチ処理ロジック |

**主要処理フロー**:
- **createExport.ts 138-155行目**: maxExportDocumentsの計算
- **createExport.ts 248-412行目**: ストリーミングエクスポート（download=true）
- **createExport.ts 420-452行目**: バッファードエクスポート

#### Step 4: インポート処理を理解する

インポートの中核処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | createImport.ts | `packages/plugin-import-export/src/import/createImport.ts` | createImport関数 |
| 4-2 | batchProcessor.ts | `packages/plugin-import-export/src/import/batchProcessor.ts` | バッチ処理ロジック |

**主要処理フロー**:
- **createImport.ts 139-145行目**: CSV/JSONパース
- **createImport.ts 169-179行目**: CSVアンフラット化
- **createImport.ts 229-236行目**: maxLimit確認
- **createImport.ts 258-265行目**: バッチプロセッサー実行

#### Step 5: ユーティリティを理解する

データ変換ユーティリティを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | flattenObject.ts | `packages/plugin-import-export/src/utilities/flattenObject.ts` | オブジェクトのフラット化 |
| 5-2 | unflattenObject.ts | `packages/plugin-import-export/src/utilities/unflattenObject.ts` | フラットデータの復元 |
| 5-3 | getSchemaColumns.ts | `packages/plugin-import-export/src/utilities/getSchemaColumns.ts` | スキーマからのカラム推論 |

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

```
importExportPlugin (index.ts)
    │
    ├─ getPluginCollections (utilities/getPluginCollections.ts)
    │      └─ エクスポート/インポートコレクションの生成
    │
    ├─ getCreateCollectionExportTask (export/getCreateExportCollectionTask.ts)
    │      └─ createExport (export/createExport.ts)
    │             ├─ createExportBatchProcessor (export/batchProcessor.ts)
    │             ├─ flattenObject (utilities/flattenObject.ts)
    │             ├─ getSchemaColumns (utilities/getSchemaColumns.ts)
    │             └─ getExportFieldFunctions (utilities/getExportFieldFunctions.ts)
    │
    ├─ getCreateCollectionImportTask (import/getCreateImportCollectionTask.ts)
    │      └─ createImport (import/createImport.ts)
    │             ├─ createImportBatchProcessor (import/batchProcessor.ts)
    │             ├─ parseCSV (utilities/parseCSV.ts)
    │             ├─ parseJSON (utilities/parseJSON.ts)
    │             ├─ unflattenObject (utilities/unflattenObject.ts)
    │             └─ getImportFieldFunctions (utilities/getImportFieldFunctions.ts)
    │
    └─ translations (translations/index.ts)
           └─ 多言語翻訳リソース
```

### データフロー図

```
[エクスポート]
                    [処理]                         [出力]

データベース ──────▶ payload.find ─────────────▶ ドキュメント配列
                        │
                        ▼
                   flattenObject ──────────────▶ フラットデータ
                        │
                        ▼
                   filterDisabled ─────────────▶ フィルタ済みデータ
                        │
                        ▼
                   CSV/JSON生成 ───────────────▶ ファイル

[インポート]
                    [処理]                         [出力]

ファイル ──────────▶ parseCSV/JSON ────────────▶ フラットデータ配列
                        │
                        ▼
                   unflattenObject ────────────▶ ドキュメント配列
                        │
                        ▼
                   create/update ──────────────▶ データベース
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/plugin-import-export/src/index.ts` | ソース | プラグインのエントリーポイント |
| types.ts | `packages/plugin-import-export/src/types.ts` | ソース | 型定義 |
| createExport.ts | `packages/plugin-import-export/src/export/createExport.ts` | ソース | エクスポート処理 |
| createImport.ts | `packages/plugin-import-export/src/import/createImport.ts` | ソース | インポート処理 |
| batchProcessor.ts (export) | `packages/plugin-import-export/src/export/batchProcessor.ts` | ソース | エクスポートバッチ処理 |
| batchProcessor.ts (import) | `packages/plugin-import-export/src/import/batchProcessor.ts` | ソース | インポートバッチ処理 |
| flattenObject.ts | `packages/plugin-import-export/src/utilities/flattenObject.ts` | ソース | オブジェクトフラット化 |
| unflattenObject.ts | `packages/plugin-import-export/src/utilities/unflattenObject.ts` | ソース | データアンフラット化 |
| parseCSV.ts | `packages/plugin-import-export/src/utilities/parseCSV.ts` | ソース | CSVパース |
| parseJSON.ts | `packages/plugin-import-export/src/utilities/parseJSON.ts` | ソース | JSONパース |
| getSchemaColumns.ts | `packages/plugin-import-export/src/utilities/getSchemaColumns.ts` | ソース | スキーマからのカラム推論 |
| getPluginCollections.ts | `packages/plugin-import-export/src/utilities/getPluginCollections.ts` | ソース | プラグインコレクション生成 |
