# 機能設計書 193-検索API

## 概要

本ドキュメントは、Chatwootにおける検索APIの設計を記載したものである。この機能により、ユーザーは会話、メッセージ、連絡先、記事を横断的に検索できる。

### 本機能の処理概要

**業務上の目的・背景**：カスタマーサポート業務では、過去の会話履歴や連絡先情報を迅速に検索する必要がある。特に大量のデータを扱う場合、効率的な検索機能は業務効率化に不可欠である。この機能は、エージェントが必要な情報に素早くアクセスするための統一された検索インターフェースを提供する。

**機能の利用シーン**：エージェントが特定の顧客との過去の会話を確認する際、キーワードで関連する会話やメッセージを検索する。また、ヘルプセンターの記事検索や連絡先の検索にも利用される。管理者はレポート作成のために特定条件の会話を検索することもある。

**主要な処理内容**：
1. 検索クエリの受信と解析
2. 検索タイプに応じた対象の選択（all/Conversation/Contact/Message/Article）
3. インボックスアクセス権限に基づくフィルタリング
4. 時間範囲、送信者、インボックスIDによる追加フィルタリング（高度な検索機能有効時）
5. ページネーションされた検索結果の返却

**関連システム・外部連携**：高度な検索機能が有効な場合、Elasticsearch/Searchkickとの連携が可能。GINインデックスを使用したPostgreSQLの全文検索も利用可能。

**権限による制御**：管理者はすべてのインボックスを検索可能。エージェントは割り当てられたインボックスのみ検索可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | 検索画面 | 主画面 | 会話・連絡先・記事の横断検索 |

## 機能種別

検索・フィルタリング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| q | String | Yes | 検索クエリ文字列 | - |
| page | Integer | No | ページ番号 | デフォルト: 1 |
| since | Integer | No | 検索開始日時（Unix timestamp） | 最大90日前まで |
| until | Integer | No | 検索終了日時（Unix timestamp） | 最大90日後まで |
| from | String | No | 送信者フィルタ（例: "contact:123", "agent:456"） | advanced_search有効時のみ |
| inbox_id | Integer | No | インボックスIDフィルタ | advanced_search有効時のみ |

### 入力データソース

- APIリクエストパラメータ（クエリストリング）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| payload.conversations | Array | 検索にマッチした会話一覧 |
| payload.contacts | Array | 検索にマッチした連絡先一覧 |
| payload.messages | Array | 検索にマッチしたメッセージ一覧 |
| payload.articles | Array | 検索にマッチした記事一覧 |

### 出力先

- JSON形式でAPIレスポンスとして返却

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ 検索パラメータの取得
2. SearchServiceの初期化
   └─ current_user, current_account, search_type, paramsの設定
3. 検索タイプに応じた処理分岐
   ├─ 'all': 全タイプを検索
   ├─ 'Conversation': 会話のみ検索
   ├─ 'Contact': 連絡先のみ検索
   ├─ 'Message': メッセージのみ検索
   └─ 'Article': 記事のみ検索
4. インボックスアクセス権限の適用
   └─ 管理者以外はアクセス可能なインボックスでフィルタ
5. 検索クエリの実行
   └─ GINインデックス、LIKE検索、またはElasticsearch
6. ページネーション適用
   └─ 15件/ページ
7. 結果の返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[SearchService初期化]
    B --> C{search_typeは?}
    C -->|all| D[全タイプ検索]
    C -->|Conversation| E[会話検索]
    C -->|Contact| F[連絡先検索]
    C -->|Message| G[メッセージ検索]
    C -->|Article| H[記事検索]
    D --> I[インボックスアクセス権限適用]
    E --> I
    F --> I
    G --> I
    H --> I
    I --> J{advanced_search有効?}
    J -->|Yes| K[時間・送信者・インボックスフィルタ適用]
    J -->|No| L[基本検索実行]
    K --> M{search_with_gin有効?}
    L --> M
    M -->|Yes| N[GINインデックス検索]
    M -->|No| O[ILIKE検索]
    N --> P[ページネーション適用]
    O --> P
    P --> Q[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-193-01 | インボックスアクセス制限 | エージェントは割り当てられたインボックスのみ検索可能 | 常時 |
| BR-193-02 | 管理者全アクセス | 管理者はすべてのインボックスを検索可能 | 管理者ロール |
| BR-193-03 | メッセージ検索期間制限 | メッセージ検索は過去3ヶ月以内のみ対象 | 常時 |
| BR-193-04 | 時間フィルタ上限 | since/untilは90日以内に制限 | advanced_search有効時 |
| BR-193-05 | ページサイズ固定 | 1ページあたり15件固定 | 常時 |

### 計算ロジック

**時間フィルタの上限値計算**:
- `cap_since_time`: max(requested_time, 90.days.ago)
- `cap_until_time`: min(requested_time, 90.days.from_now)

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 会話検索 | conversations, contacts | SELECT | 会話と連絡先の結合検索 |
| 連絡先検索 | contacts | SELECT | 連絡先の検索 |
| メッセージ検索 | messages | SELECT | メッセージの全文検索 |
| 記事検索 | articles | SELECT | 記事の全文検索 |

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

#### conversations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | display_id, contacts.name, contacts.email, contacts.phone_number, contacts.identifier | ILIKEによる部分一致検索 | contactsテーブルとJOIN |

#### messages

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | content | GINインデックスまたはILIKEによる検索 | created_at >= 3.months.ago |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | ArgumentErrorが発生した場合 | エラーメッセージを返却 |

### リトライ仕様

- Elasticsearch接続失敗時は自動的にSQL検索にフォールバック

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

- 読み取り専用のため、明示的なトランザクション管理なし

## パフォーマンス要件

- GINインデックスを使用した高速な全文検索
- ページネーションによる結果セットの制限
- インボックスフィルタリングによるスキャン範囲の限定

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

- 認証済みユーザーのみアクセス可能
- インボックスアクセス権限に基づくデータフィルタリング
- SQLインジェクション対策（パラメータ化クエリ）

## 備考

- `advanced_search`フィーチャーフラグにより高度な検索機能を制御
- `search_with_gin`フィーチャーフラグによりGINインデックス使用を制御

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | search_controller.rb | `app/controllers/api/v1/accounts/search_controller.rb` | 検索APIのエンドポイント定義 |

**主要処理フロー**:
1. **2-4行目**: `index`アクション - 全タイプ検索
2. **6-8行目**: `conversations`アクション - 会話のみ検索
3. **10-12行目**: `contacts`アクション - 連絡先のみ検索
4. **14-16行目**: `messages`アクション - メッセージのみ検索
5. **18-20行目**: `articles`アクション - 記事のみ検索
6. **24-33行目**: `search`プライベートメソッド - SearchService呼び出し

#### Step 2: 検索サービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | search_service.rb | `app/services/search_service.rb` | 検索ロジックの実装 |

**主要処理フロー**:
1. **2行目**: 初期化パラメータ（current_user, current_account, params, search_type）
2. **8-21行目**: `perform`メソッド - 検索タイプに応じた処理分岐
3. **33-47行目**: `filter_conversations` - 会話検索ロジック
4. **49-57行目**: `filter_messages` - メッセージ検索（GIN/LIKE/Elasticsearch選択）
5. **72-96行目**: `filter_messages_with_gin` - GINインデックスを使用した検索
6. **98-105行目**: `filter_messages_with_like` - ILIKEを使用した検索
7. **164-175行目**: `filter_contacts` - 連絡先検索
8. **177-182行目**: `filter_articles` - 記事検索

**読解のコツ**: `pattr_initialize`はRubyの初期化パターンで、引数をインスタンス変数に自動設定する。`!`サフィックスは必須パラメータを示す。

#### Step 3: ビュー（JSON出力）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.json.jbuilder | `app/views/api/v1/accounts/search/index.json.jbuilder` | JSON出力形式 |

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

```
SearchController
    │
    ├─ index → search('all')
    │           └─ SearchService#perform
    │                  ├─ filter_conversations
    │                  ├─ filter_contacts
    │                  ├─ filter_messages
    │                  │      ├─ filter_messages_with_gin
    │                  │      ├─ filter_messages_with_like
    │                  │      └─ advanced_search (Elasticsearch)
    │                  └─ filter_articles
    │
    ├─ conversations → search('Conversation')
    │           └─ SearchService#perform
    │                  └─ filter_conversations
    │
    ├─ contacts → search('Contact')
    │           └─ SearchService#perform
    │                  └─ filter_contacts
    │
    ├─ messages → search('Message')
    │           └─ SearchService#perform
    │                  └─ filter_messages
    │
    └─ articles → search('Article')
                └─ SearchService#perform
                       └─ filter_articles
```

### データフロー図

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

q (検索クエリ)      ───▶ SearchController#search
page               ───▶     │
since/until        ───▶     ▼
from/inbox_id      ───▶ SearchService#perform
                              │
                    ┌─────────┼─────────┐
                    ▼         ▼         ▼
              会話検索   メッセージ検索  連絡先検索
                    │         │         │
                    │    ┌────┴────┐    │
                    │    ▼         ▼    │
                    │   GIN     LIKE    │
                    │  検索     検索    │
                    │         or        │
                    │    Elasticsearch  │
                    └─────────┼─────────┘
                              ▼
                         ページネーション
                              │
                              ▼
                    { conversations, contacts,
                      messages, articles }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| search_controller.rb | `app/controllers/api/v1/accounts/search_controller.rb` | ソース | 検索APIコントローラー |
| search_service.rb | `app/services/search_service.rb` | ソース | 検索ロジック実装 |
| index.json.jbuilder | `app/views/api/v1/accounts/search/index.json.jbuilder` | テンプレート | JSON出力テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（160-166行目） |
