# 機能設計書 33-検索サジェスト

## 概要

本ドキュメントは、Shopwareストアフロントにおける検索サジェスト（オートコンプリート）機能の設計について記述する。

### 本機能の処理概要

本機能は、検索入力フィールドでユーザーがキーワードを入力する際に、リアルタイムで関連商品を候補として表示するオートコンプリート機能である。入力中に即座にAjaxリクエストを送信し、マッチする商品を表示することで、顧客の検索体験を向上させる。

**業務上の目的・背景**：ECサイトにおいて、検索サジェストは顧客が目的の商品に素早くたどり着くための重要な機能である。キーワード入力中に候補を表示することで、タイプミスの防止、検索時間の短縮、人気商品への誘導が可能となる。また、顧客が商品名を正確に覚えていない場合でも、部分一致で候補を表示することで発見を支援する。

**機能の利用シーン**：
- ヘッダーの検索バーにキーワードを入力し始めた際
- 検索キーワードを変更した際（文字追加・削除）
- モバイル版での検索入力時

**主要な処理内容**：
1. 検索キーワードの受け取り（Ajax通信）
2. 集計処理の無効化（パフォーマンス最適化）
3. 商品サジェストルート（ProductSuggestRoute）の呼び出し
4. 検索結果の取得（最大10件に制限）
5. サジェストテンプレートのレンダリング

**関連システム・外部連携**：
- Elasticsearch（オプション）：高速なサジェスト検索

**権限による制御**：特になし（匿名ユーザーも利用可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | 検索サジェスト | 主画面 | 検索入力時のオートコンプリート（SearchController::suggest） |
| 5 | 検索結果ページ | 遷移先画面 | サジェストから検索実行時の遷移先 |

## 機能種別

データ検索・表示（Read操作 / Ajax通信）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| search | string | Yes | 検索キーワード | 空文字でないこと |
| no-aggregations | bool | No | 集計無効フラグ（自動設定） | - |

### 入力データソース

- クエリパラメータ（search）
- SalesChannelContext（言語、通貨、顧客グループ等のコンテキスト情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page | SuggestPage | サジェストページオブジェクト |
| page.searchResult | ProductListingResult | 商品候補一覧 |
| page.searchResult.elements | ProductCollection | 商品エンティティの配列（最大10件） |
| page.searchResult.total | int | 総マッチ件数 |
| page.searchTerm | string | 検索キーワード |

### 出力先

- HTMLフラグメント（@Storefront/storefront/layout/header/search-suggest.html.twig）
- Ajax通信によるドロップダウン表示

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（Ajax）
   └─ SearchController::suggest()がXmlHttpRequestを受信
2. 集計無効フラグ設定
   └─ no-aggregationsがない場合は自動的にtrueを設定
3. SuggestPageLoader呼び出し
   └─ サジェストページのロード処理を開始
4. GenericPageLoader呼び出し
   └─ 共通ページ情報をロード
5. 検索条件構築
   └─ Criteriaオブジェクトを生成（limit=10, totalCountMode=EXACT）
6. ProductSuggestRoute呼び出し
   └─ 商品サジェストルートで商品を検索
7. 検索結果設定
   └─ ProductListingResultをページに設定
8. 検索キーワード設定
   └─ 検索キーワードをページに保持
9. イベントディスパッチ
   └─ SuggestPageLoadedEventを発行
10. フック実行
    └─ SuggestPageLoadedHookを実行
11. テンプレートレンダリング
    └─ search-suggest.html.twigをレンダリングして返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: suggest Ajax] --> B{no-aggregations設定済み?}
    B -->|No| C[no-aggregations = true設定]
    B -->|Yes| D[SuggestPageLoader::load]
    C --> D
    D --> E[GenericPageLoader::load]
    E --> F[Criteria構築 limit=10]
    F --> G[ProductSuggestRoute::load]
    G --> H[検索結果取得]
    H --> I[SuggestPage生成]
    I --> J[検索キーワード設定]
    J --> K[SuggestPageLoadedEvent発行]
    K --> L[SuggestPageLoadedHook実行]
    L --> M[search-suggest.html.twigレンダリング]
    M --> N[HTMLフラグメント返却]
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-33-01 | 表示件数制限 | サジェスト結果は最大10件に制限 | 常時 |
| BR-33-02 | 集計無効化 | パフォーマンス向上のため集計処理を無効化 | 常時 |
| BR-33-03 | 総件数取得 | 実際のマッチ件数を正確にカウント（TOTAL_COUNT_MODE_EXACT） | 常時 |
| BR-33-04 | Ajax専用 | XmlHttpRequestのみ対応（通常のページリクエストは非対応） | 常時 |
| BR-33-05 | 販売チャネル制限 | 現在の販売チャネルで有効な商品のみ検索対象 | 常時 |

### 計算ロジック

特になし（検索処理のみ）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 商品検索 | product | SELECT | キーワードにマッチする商品を取得 |
| 商品検索 | product_translation | SELECT | 商品の翻訳情報（名前、説明）を検索 |

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

#### product / product_translation

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, product_number, name, description, price, cover | 全文検索条件 AND active = true AND sales_channel条件 | LIMIT 10、集計なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| MISSING_REQUEST_PARAMETER | パラメータ不足 | searchパラメータが空の場合 | 空の結果を返却 |

### リトライ仕様

特になし（読み取り専用処理のためリトライ不要）

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

読み取り専用処理のため、トランザクション制御なし

## パフォーマンス要件

- レスポンス時間: 200ms以内（ユーザー入力への即時応答）
- 結果件数を10件に制限してクエリ負荷を軽減
- 集計処理を無効化してレスポンス高速化
- Elasticsearch使用時は更に高速

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

- XSS対策としてテンプレート側で検索キーワードをエスケープ
- SQLインジェクション対策としてDAL（Data Abstraction Layer）を使用
- Ajax専用のため、CSRFトークン検証は不要（GET/読み取り専用）

## 備考

- 入力のデバウンス処理はフロントエンド（JavaScript）側で実装
- 日本語入力（IME）への対応はフロントエンド側で考慮

---

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

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

### 推奨読解順序

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

まず、サジェスト機能で使用されるデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SuggestPage.php | `src/Storefront/Page/Suggest/SuggestPage.php` | サジェストページの構造（searchResult, searchTerm） |
| 1-2 | ProductListingResult.php | `src/Core/Content/Product/SalesChannel/Listing/ProductListingResult.php` | リスティング結果の構造 |
| 1-3 | ProductSuggestRouteResponse.php | `src/Core/Content/Product/SalesChannel/Suggest/ProductSuggestRouteResponse.php` | サジェストルートのレスポンス構造 |

**読解のコツ**: SuggestPageはPageを継承しており、searchResultフィールドでProductListingResultを保持する。SearchPageとは異なるプロパティ名（listing vs searchResult）に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SearchController.php | `src/Storefront/Controller/SearchController.php` | suggestメソッドがエントリーポイント |

**主要処理フロー**:
1. **71-76行目**: ルート定義（/suggest, XmlHttpRequest専用）
2. **77行目**: suggestメソッドの開始
3. **79-81行目**: no-aggregationsフラグの自動設定
4. **83行目**: SuggestPageLoaderの呼び出し
5. **85行目**: SuggestPageLoadedHookの実行
6. **87行目**: search-suggest.html.twigのレンダリング

#### Step 3: ページローダーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SuggestPageLoader.php | `src/Storefront/Page/Suggest/SuggestPageLoader.php` | サジェストページロード処理 |

**主要処理フロー**:
- **37-59行目**: load()メソッド - サジェストページ生成のメイン処理
- **39行目**: GenericPageLoaderで共通情報取得
- **41行目**: SuggestPage::createFromで変換
- **43-45行目**: Criteria構築（limit=10, TOTAL_COUNT_MODE_EXACT）
- **46-50行目**: ProductSuggestRouteで商品検索
- **52行目**: 検索キーワードをページに設定
- **54-56行目**: SuggestPageLoadedEventの発行

#### Step 4: サジェストルートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AbstractProductSuggestRoute.php | `src/Core/Content/Product/SalesChannel/Suggest/AbstractProductSuggestRoute.php` | Store-APIサジェストルートの契約 |
| 4-2 | ProductSuggestRoute.php | `src/Core/Content/Product/SalesChannel/Suggest/ProductSuggestRoute.php` | 実際のサジェスト処理実装 |

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

```
SearchController::suggest()
    │
    ├─ request->request->set('no-aggregations', true) [集計無効化]
    │
    ├─ SuggestPageLoader::load()
    │      │
    │      ├─ GenericPageLoader::load() [共通ページ情報]
    │      │
    │      ├─ SuggestPage::createFrom() [ページ変換]
    │      │
    │      ├─ Criteria [limit=10, TOTAL_COUNT_MODE_EXACT]
    │      │
    │      ├─ ProductSuggestRoute::load() [商品サジェスト検索]
    │      │      └─ ProductListingResult (max 10件)
    │      │
    │      └─ EventDispatcher::dispatch() [SuggestPageLoadedEvent]
    │
    ├─ hook() [SuggestPageLoadedHook]
    │
    └─ renderStorefront() [テンプレートレンダリング]
```

### データフロー図

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

search (Ajax) ────────▶ SuggestPageLoader ────────▶ SuggestPage
                              │                          │
                              ▼                          ├─ searchResult
                    ProductSuggestRoute                  │   ├─ elements (max 10)
                              │                          │   └─ total
                              ▼                          └─ searchTerm
                    product テーブル                           │
                    (Elasticsearch)                           ▼
                              │                    search-suggest.html.twig
                              ▼                              │
                    ProductListingResult                     ▼
                       (max 10件)                     HTMLフラグメント
                                                    (ドロップダウン表示)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SearchController.php | `src/Storefront/Controller/SearchController.php` | コントローラー | HTTPリクエスト処理 |
| SuggestPageLoader.php | `src/Storefront/Page/Suggest/SuggestPageLoader.php` | ページローダー | サジェストページデータ取得 |
| SuggestPage.php | `src/Storefront/Page/Suggest/SuggestPage.php` | DTO | サジェストページデータ構造 |
| AbstractProductSuggestRoute.php | `src/Core/Content/Product/SalesChannel/Suggest/AbstractProductSuggestRoute.php` | 抽象クラス | Store-APIサジェストルート契約 |
| ProductSuggestRoute.php | `src/Core/Content/Product/SalesChannel/Suggest/ProductSuggestRoute.php` | Store-APIルート | サジェスト検索処理 |
| search-suggest.html.twig | `src/Storefront/Resources/views/storefront/layout/header/search-suggest.html.twig` | テンプレート | サジェスト表示用ビュー |
| SuggestPageLoadedHook.php | `src/Storefront/Page/Suggest/SuggestPageLoadedHook.php` | フック | 拡張ポイント |
| SuggestPageLoadedEvent.php | `src/Storefront/Page/Suggest/SuggestPageLoadedEvent.php` | イベント | イベント通知 |
| search-widget.js | `src/Storefront/Resources/app/storefront/src/plugin/header/search-widget.plugin.js` | JavaScript | フロントエンド検索処理 |
