# 機能設計書 6-関連記事表示

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける関連記事表示機能の設計仕様を記述する。現在閲覧中の記事に関連する記事を推薦し、記事末尾やフィードに表示する。

### 本機能の処理概要

**業務上の目的・背景**：ユーザーの興味関心に基づいて関連するWikipedia記事を推薦し、継続的な閲覧を促進する。記事末尾に「関連記事」セクションを表示することで、自然な流れで次の記事へ誘導できる。また、探索フィードにも「あなたが読んだ記事に関連」として表示される。

**機能の利用シーン**：
- 記事末尾の「関連記事」セクションを表示した時
- 探索フィードで「Because you read」カードを表示した時
- 記事を閲覧し終えて次の記事を探す時

**主要な処理内容**：
1. MediaWiki APIの「morelike」検索機能を使用して関連記事を取得
2. 記事サマリー（タイトル、説明、サムネイル等）を取得
3. 関連記事リストをUI表示用に整形
4. 記事フッターまたはフィードカードに表示

**関連システム・外部連携**：
- MediaWiki API（morelike検索）
- Core Data（関連記事キャッシュ）
- フィードコンテンツコントローラー

**権限による制御**：
- 特別な権限は不要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | RelatedArticles画面 | 主画面 | 関連記事リストの表示 |
| 5 | Article画面 | 親画面 | 記事フッターでの関連記事表示 |
| 1 | Explore画面 | 表示先 | フィードでの関連記事カード表示 |

## 機能種別

データ取得 / 表示処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| articleURL | URL | Yes | 基準となる記事のURL | Wikipedia URLであること |
| limit | Int | No | 取得件数（デフォルト20） | 正の整数 |

### APIリクエストパラメータ

| パラメータ名 | 値 | 説明 |
|-------------|-----|------|
| action | query | APIアクション |
| generator | search | 検索ジェネレーター |
| gsrlimit | 20 | 検索結果件数 |
| gsrnamespace | 0 | メインネームスペースのみ |
| gsrqiprofile | classic_noboostlinks | 検索プロファイル |
| gsrsearch | morelike:{articleTitle} | morelike検索クエリ |
| prop | pageimages\|description\|info\|extracts | 取得プロパティ |
| pithumbsize | 160 | サムネイルサイズ |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 関連記事辞書 | [WMFInMemoryURLKey: ArticleSummary] | 記事キーとサマリーの辞書 |

### ArticleSummaryの構造

| プロパティ | 型 | 説明 |
|-----------|-----|------|
| id | Int64 | ページID |
| title | String | 記事タイトル |
| displayTitle | String | 表示用タイトル |
| articleDescription | String? | 記事の説明 |
| extract | String? | 記事の抜粋 |
| thumbnail | ArticleSummaryImage? | サムネイル画像情報 |
| contentURLs | ArticleSummaryContentURLs | 記事URL情報 |

### 出力先

- 記事フッター（関連記事セクション）
- 探索フィード（Related Pagesカード）
- Core Data（キャッシュ）

## 処理フロー

### 処理シーケンス

```
1. 関連記事取得リクエスト
   └─ fetchRelatedArticles(forArticleWithURL:)を呼び出し
   └─ 記事URL、タイトル、サイトURLを抽出

2. MediaWiki APIリクエスト
   └─ morelike検索クエリを構築
   └─ performDecodableMediaWikiAPIGET()を実行

3. レスポンス処理
   └─ RelatedResponseをデコード
   └─ getRelatedPages()でページ情報を抽出
   └─ 言語バリアント対応のタイトル取得

4. ArticleSummary変換
   └─ RelatedPageからArticleSummaryを生成
   └─ キーと値の辞書を作成

5. 完了コールバック
   └─ completion(nil, summaries)で結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[関連記事取得要求] --> B{articleURL有効?}
    B -->|No| C[invalidParametersError]
    B -->|Yes| D[APIリクエスト構築]
    D --> E[morelike検索実行]
    E --> F{成功?}
    F -->|No| G[unexpectedResponseError]
    F -->|Yes| H[RelatedResponse処理]
    H --> I[getRelatedPages]
    I --> J{言語バリアント?}
    J -->|Yes| K[バリアント対応タイトル取得]
    J -->|No| L[標準タイトル使用]
    K --> M[ArticleSummary変換]
    L --> M
    M --> N[辞書作成]
    N --> O[completion呼び出し]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | デフォルト件数 | デフォルトで20件取得 | limitが指定されない場合 |
| BR-002 | ネームスペース制限 | メインネームスペース（0）のみ対象 | 常時 |
| BR-003 | キャッシュ有効期限 | 86400秒（24時間）のキャッシュ | smaxage/maxage設定 |
| BR-004 | 言語バリアント | アプリの言語バリアント設定に応じたタイトルを使用 | 言語バリアント対応言語 |
| BR-005 | サムネイルサイズ | 160pxのサムネイルを取得 | 常時 |

### 計算ロジック

- morelike検索: `gsrsearch=morelike:{articleTitle}`
- 言語バリアントタイトル取得: `varianttitles[appLegacyLanguageVariantCode]`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| キャッシュ更新 | WMFArticle | INSERT/UPDATE | 関連記事のサマリーをキャッシュ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| invalidParametersError | パラメータエラー | articleURLがnil、タイトル取得失敗 | エラーコールバック |
| unexpectedResponseError | レスポンスエラー | API応答のデコード失敗 | エラーコールバック |

### リトライ仕様

- 自動リトライなし
- Pull-to-Refreshで手動リトライ可能（フィード表示時）

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

- トランザクション処理なし（読み取り専用）

## パフォーマンス要件

- API応答の目標時間：2秒以内
- キャッシュ使用時は即時表示

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

- API通信はHTTPS
- origin=*でCORS対応

## 備考

- morelike検索はWikipediaのElasticSearch機能を使用
- 検索プロファイル「classic_noboostlinks」でリンク数によるブーストを無効化

---

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

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

### 推奨読解順序

#### Step 1: データ取得クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RelatedSearchFetcher.swift | `WMF Framework/RelatedSearchFetcher.swift` | 関連記事取得のメインクラス |

**主要コード**:
- **6-8行目**: fetchRelatedArticles(forArticleWithURL:completion:) - 公開API
- **10-69行目**: fetchRelatedArticles(forArticleWithURL:limit:completion:) - メイン実装
- **23-42行目**: クエリパラメータ定義（morelike検索設定）
- **44-67行目**: API実行とレスポンス処理
- **75-100行目**: getRelatedPages() - レスポンスからページ情報抽出
- **102-124行目**: 言語バリアント対応処理

**読解のコツ**: `gsrsearch: "morelike:\(articleTitle)"`がWikipediaのmorelike検索機能を使用している。

#### Step 2: レスポンスデータ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RelatedSearchFetcher.swift | `WMF Framework/RelatedSearchFetcher.swift` | ArticleSummary変換 |

**主要コード**:
- **147-161行目**: ArticleSummary extension - RelatedPageからの変換

#### Step 3: 表示コンポーネントを理解する

関連記事の表示は記事フッターまたはフィードカードで行われる。

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

```
ArticleViewController / ExploreViewController
    │
    ├─ RelatedSearchFetcher
    │      │
    │      ├─ fetchRelatedArticles()
    │      │      │
    │      │      ├─ performDecodableMediaWikiAPIGET()
    │      │      │      └─ MediaWiki API (morelike search)
    │      │      │
    │      │      └─ getRelatedPages()
    │      │             └─ RelatedPage -> ArticleSummary変換
    │      │
    │      └─ 言語バリアント処理
    │             ├─ getAppLegacyLanguageVariantCode()
    │             └─ getVariantSpecificPageTitle()
    │
    └─ [表示コンポーネント]
           ├─ 記事フッター（関連記事セクション）
           └─ フィードカード（Related Pages）
```

### データフロー図

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

articleURL ─────────────▶ RelatedSearchFetcher
    │                          │
    ├─ title ─────────────▶    ├─ morelikeクエリ構築
    │                          │
    └─ siteURL ───────────▶    ├─ MediaWiki API呼び出し
                               │
                               ▼
                         RelatedResponse
                               │
                               ▼
                         getRelatedPages()
                               │
                               ▼
                         ArticleSummary配列
                               │
                               ▼
                         [WMFInMemoryURLKey: ArticleSummary]
                               │
                               ▼
                         [関連記事表示]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RelatedSearchFetcher.swift | `WMF Framework/` | ソース | 関連記事取得 |
| RelatedResponse.swift | `WMF Framework/` | ソース | APIレスポンスモデル |
| RelatedPage.swift | `WMF Framework/` | ソース | 関連ページモデル |
| ArticleSummary.swift | `WMF Framework/` | ソース | 記事サマリーモデル |
| Fetcher.swift | `WMF Framework/` | ソース | 基底Fetcherクラス |
| WMFContentGroup.swift | `WMF Framework/` | ソース | フィードコンテンツグループ |
