# 機能設計書 11-記事検索

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける「記事検索」機能の設計を記述する。この機能は、ユーザーがキーワードを入力してWikipedia記事を検索し、結果を一覧表示するものである。

### 本機能の処理概要

**業務上の目的・背景**：ユーザーが膨大なWikipedia記事から目的の記事を効率的に見つけられるようにするため、キーワード検索機能を提供する。モバイルデバイスでの情報検索において、直感的な検索体験は必須機能であり、本機能はアプリの中核的な機能の一つとして位置付けられる。

**機能の利用シーン**：ユーザーが特定のトピックについて調べたいとき、Exploreタブやその他の画面から検索バーにアクセスし、キーワードを入力して記事を検索する。検索は複数言語に対応しており、ユーザーの設定した言語で検索が可能である。

**主要な処理内容**：
1. 検索バーへのキーワード入力を受け付ける
2. 入力されたキーワードをWikipedia APIに送信して検索を実行する
3. 検索結果（記事タイトル、説明、サムネイル画像）を取得する
4. 結果をコレクションビューに一覧表示する
5. 最近の検索履歴を保存・表示する
6. 検索言語の切り替えに対応する

**関連システム・外部連携**：Wikipedia MediaWiki API（prefix検索およびfulltext検索エンドポイント）と連携する。検索結果はAPI経由で取得され、サムネイル画像はWikimedia CommonsのURLから非同期で読み込まれる。

**権限による制御**：本機能は認証不要で利用可能。ログイン/非ログインに関わらず、すべてのユーザーが検索機能を使用できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | Explore画面 | 参照元画面 | ナビゲーションバーの検索機能から記事検索を実行 |
| 2 | Places画面 | 参照元画面 | 検索バーからの記事検索 |
| 4 | Search画面 | 主画面 | キーワードによるWikipedia記事の検索 |
| 10 | SearchResults画面 | 結果表示画面 | 検索結果一覧の表示 |

## 機能種別

検索処理 / データ取得 / 一覧表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| searchTerm | String | Yes | 検索キーワード | 空白のみでないこと、NSTextAttachment.characterで始まらないこと |
| siteURL | URL | Yes | 検索対象のWikipediaサイトURL | 有効なURLであること |
| resultLimit | Int | No | 取得する結果の最大数 | デフォルトはWMFMaxSearchResultLimit |

### 入力データソース

- 画面入力：SearchViewControllerの検索バー（UISearchBar）
- ユーザー設定：選択中の言語（SearchLanguagesBarViewControllerから取得）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| results | [MWKSearchResult] | 検索結果の記事配列 |
| searchTerm | String | 検索に使用したキーワード |
| redirectMappings | [MWKSearchRedirectMapping] | リダイレクト情報 |
| thumbnailURL | URL? | 記事のサムネイル画像URL |
| displayTitleHTML | String | 記事タイトル（HTML形式） |
| wikidataDescription | String? | Wikidata由来の記事説明 |

### 出力先

- 画面表示：SearchResultsViewControllerのCollectionView
- データストア：MWKRecentSearchList（検索履歴の保存）

## 処理フロー

### 処理シーケンス

```
1. ユーザーが検索バーにキーワードを入力
   └─ UISearchResultsUpdating.updateSearchResults(for:)が呼び出される
2. 検索キーワードのバリデーション
   └─ 空白のみ、または特殊文字で始まる場合は検索をスキップ
3. WMFSearchFetcherによるAPI呼び出し
   └─ fetchArticles(forSearchTerm:siteURL:resultLimit:)を実行
4. 検索結果の取得
   └─ 結果が12件未満の場合、fulltext検索も追加実行
5. 結果をSearchResultsViewControllerに反映
   └─ resultsプロパティに設定、collectionViewをリロード
6. 検索履歴の保存（結果タップ時）
   └─ MWKRecentSearchEntryを作成してMWKRecentSearchListに追加
```

### フローチャート

```mermaid
flowchart TD
    A[検索バー入力] --> B{入力検証}
    B -->|無効| C[検索キャンセル]
    B -->|有効| D[prefix検索API呼び出し]
    D --> E{結果数確認}
    E -->|12件以上| F[結果表示]
    E -->|12件未満| G[fulltext検索追加実行]
    G --> F
    F --> H{ユーザー選択}
    H -->|記事タップ| I[検索履歴保存]
    I --> J[記事画面へ遷移]
    H -->|キャンセル| K[検索終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-11-01 | 検索結果上限 | 検索結果はWMFMaxSearchResultLimit件まで取得 | 常時 |
| BR-11-02 | 補完検索 | prefix検索で12件未満の場合、fulltext検索を追加実行 | prefix検索結果が12件未満 |
| BR-11-03 | 履歴保存 | 検索結果をタップした場合のみ履歴に保存 | 検索結果タップ時 |
| BR-11-04 | 履歴上限 | 検索履歴は最新のものを保持 | 履歴追加時 |

### 計算ロジック

特になし。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 検索履歴保存 | MWKRecentSearchList | INSERT | 検索キーワードと言語URLを保存 |
| 検索履歴取得 | MWKRecentSearchList | SELECT | 最近の検索履歴一覧を取得 |
| 検索履歴削除 | MWKRecentSearchList | DELETE | 特定の検索履歴または全履歴を削除 |
| 記事情報取得/作成 | WMFArticle | SELECT/INSERT | 検索結果から記事情報を取得または作成 |

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

#### MWKRecentSearchEntry

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | searchTerm | 検索キーワード | - |
| INSERT | siteURL | 検索対象のサイトURL | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ネットワークエラー | API接続失敗時 | emptyViewTypeを.noInternetConnectionに設定 |
| - | 検索結果なし | 該当記事なし | emptyViewTypeを.noSearchResultsに設定 |

### リトライ仕様

自動リトライは行わない。ユーザーが再度検索を実行することで再試行可能。

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

検索履歴の保存は単一操作で完結し、特別なトランザクション管理は不要。

## パフォーマンス要件

- 検索開始から結果表示まで3秒以内（ネットワーク状態に依存）
- 検索ログにはelapsedTimeを記録してパフォーマンス監視

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

- 検索キーワードはWikipedia APIに平文で送信される（HTTPSで暗号化）
- 検索履歴はローカルデバイスにのみ保存
- ユーザー認証不要のため、認可チェックは不要

## 備考

SearchFunnel.sharedを通じて検索イベントのログ記録が行われ、分析に活用される。

---

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

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

### 推奨読解順序

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

検索結果のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFSearchResults | `WMF Framework/` | 検索結果全体を表すクラス、searchTerm, results, redirectMappingsを持つ |
| 1-2 | MWKSearchResult | `WMF Framework/` | 個別の検索結果を表すクラス、articleID, displayTitle, thumbnailURLなど |
| 1-3 | MWKRecentSearchEntry | `WMF Framework/` | 検索履歴エントリ、searchTermとsiteURLを保持 |

**読解のコツ**: WMFSearchResultsがAPI応答全体を、MWKSearchResultが個別の記事情報を表す。この2層構造を理解することが重要。

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

SearchViewControllerが検索機能のメインエントリーポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SearchViewController.swift | `Wikipedia/Code/SearchViewController.swift` | 検索画面のメインViewController |

**主要処理フロー**:
1. **123-128行目**: viewDidLoad - 最近の検索埋め込み、結果ViewController埋め込み、言語バー設定
2. **344-349行目**: searchAndMakeResultsVisibleForSearchTerm - 外部からの検索実行エントリーポイント
3. **355-420行目**: search(for:suggested:) - 実際の検索処理、API呼び出し
4. **506-518行目**: saveLastSearch - 検索履歴の保存処理

#### Step 3: 検索結果表示を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SearchResultsViewController.swift | `Wikipedia/Code/SearchResultsViewController.swift` | 検索結果一覧の表示を担当 |

**主要処理フロー**:
- **6-11行目**: resultsプロパティ - 検索結果の保持とリロード
- **40-42行目**: articleURL(at:) - インデックスから記事URLを取得
- **58-65行目**: collectionView(_:didSelectItemAt:) - セル選択時の処理
- **90-120行目**: configure(cell:forItemAt:layoutOnly:) - セルの構成処理

#### Step 4: 言語切り替えを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SearchLanguagesBarViewController.swift | `Wikipedia/Code/SearchLanguagesBarViewController.swift` | 検索言語バーの管理 |

**主要処理フロー**:
- **136-156行目**: selectedSearchContentLanguageCode - 選択中の言語コード管理
- **234-236行目**: languageBarLanguages() - 言語バーに表示する言語リスト取得
- **290-297行目**: setCurrentlySelectedLanguageToButtonLanguage - 言語ボタンタップ時の処理

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

```
SearchViewController
    │
    ├─ WMFSearchFetcher.fetchArticles()
    │      └─ Wikipedia API呼び出し (prefix/fulltext)
    │
    ├─ SearchResultsViewController
    │      └─ ArticleCollectionViewController (継承)
    │             └─ CollectionView描画
    │
    ├─ SearchLanguagesBarViewController
    │      └─ 言語選択UI管理
    │
    └─ MWKRecentSearchList
           └─ 検索履歴の永続化
```

### データフロー図

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

キーワード入力 ───▶ SearchViewController ───▶ 検索結果表示
                         │
                         ├─▶ WMFSearchFetcher
                         │       └─▶ Wikipedia API
                         │
                         └─▶ SearchResultsViewController
                                 └─▶ CollectionView
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SearchViewController.swift | `Wikipedia/Code/` | ソース | 検索画面のメインViewController |
| SearchResultsViewController.swift | `Wikipedia/Code/` | ソース | 検索結果一覧表示 |
| SearchLanguagesBarViewController.swift | `Wikipedia/Code/` | ソース | 言語選択バー |
| SearchFunnel.swift | `Wikipedia/Code/` | ソース | 検索イベントログ |
| WMFSearchFetcher | `WMF Framework/` | ソース | Wikipedia検索API呼び出し |
| WMFSearchResults | `WMF Framework/` | ソース | 検索結果データモデル |
| MWKSearchResult | `WMF Framework/` | ソース | 個別記事検索結果 |
| MWKRecentSearchList | `WMF Framework/` | ソース | 検索履歴管理 |
| MWKRecentSearchEntry | `WMF Framework/` | ソース | 検索履歴エントリ |
| SearchBarExtendedViewController.swift | `Wikipedia/Code/` | ソース | 検索バー拡張 |
