# 機能設計書 60-検索インデックス更新

## 概要

本ドキュメントは、LEGACY CMSにおける検索インデックス更新機能の設計を定義する。この機能は、コンテンツ（記事・ページ）の作成・更新・削除時に、Zend_Search_Lucene検索インデックスを自動的に更新するための機能である。

### 本機能の処理概要

**業務上の目的・背景**：CMSにおいて全文検索機能は重要なユーザビリティ向上要素である。サイト訪問者がキーワードでコンテンツを検索できるよう、記事やページの内容をLuceneインデックスとして管理する。コンテンツの変更が即座に検索結果に反映されることで、常に最新の情報が検索可能な状態を維持する。

**機能の利用シーン**：管理画面で記事やページを編集・保存した際に、バックグラウンドで検索インデックスが更新される。コンテンツ削除時にはインデックスからも該当エントリが削除される。サイト初期構築時にはインデックスファイルを新規作成する。

**主要な処理内容**：
1. コンテンツ保存時にインデックスエントリを更新（delete + add）
2. コンテンツ削除時にインデックスエントリを削除
3. サイトインデックスの新規作成

**関連システム・外部連携**：Zend_Search_Lucene（Apache Lucene互換のPHP全文検索エンジン）によるインデックス管理。インデックスファイルはファイルシステム上に保存。

**権限による制御**：コンテンツの編集権限を持つユーザーがコンテンツを保存する際に自動実行されるため、個別の権限制御は不要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | 検索結果画面 | 主機能 | Lucene検索エンジンによる全文検索 |
| 36 | 記事編集画面 | 補助機能 | 保存時に検索インデックスを更新 |
| 37 | 記事新規作成画面 | 補助機能 | 保存時に検索インデックスを更新 |
| 49 | イベント編集画面 | 補助機能 | 保存時に検索インデックスを更新 |
| 50 | イベント新規作成画面 | 補助機能 | 保存時に検索インデックスを更新 |
| 61 | ページ編集画面 | 補助機能 | 保存時に検索インデックスを更新 |
| 62 | ページ新規作成画面 | 補助機能 | 保存時に検索インデックスを更新 |

## 機能種別

バックグラウンド処理 / インデックス管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string | Yes | インデックスエントリの一意キー（例：'a123', 'p456'） | NULL不可 |
| date | string | Yes | ドキュメント日付（Ymd形式） | - |
| title | string | Yes | ドキュメントタイトル | - |
| url | string | Yes | ドキュメントURL | - |
| details | string | No | 詳細情報（著者・日付等） | NULL可 |
| stub | string | Yes | 検索結果に表示する抜粋（140文字で切り詰め） | - |
| contents | string | Yes | 全文検索対象となる本文 | - |

### 入力データソース

- Articlesモデル、Pagesモデルから呼び出し
- コンテンツの各フィールドをパラメータとして渡す

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| なし | - | 成功時は戻り値なし、失敗時は例外をスロー |

### 出力先

- Luceneインデックスファイル（site-index/）

## 処理フロー

### 処理シーケンス

```
1. エントリ更新（updateEntry）
   ├─ 既存エントリ削除（deleteEntry）
   │   └─ keyで検索し該当ドキュメントを削除
   └─ 新規エントリ作成（createEntry）
       ├─ Lucene_Documentオブジェクト作成
       ├─ 各フィールドを追加（Keyword/Text/UnStored）
       └─ インデックスにドキュメント追加

2. エントリ削除（deleteEntry）
   └─ keyで検索し該当ドキュメントを削除

3. サイトインデックス作成（createSiteIndex）
   └─ 新規Luceneインデックスを作成
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{操作種別}
    B -->|更新| C[updateEntry]
    B -->|削除| D[deleteEntry]
    B -->|新規作成| E[createSiteIndex]

    C --> F[deleteEntry呼び出し]
    F --> G[createEntry呼び出し]
    G --> H[Lucene_Document作成]
    H --> I[フィールド追加]
    I --> J[インデックス追加]
    J --> K[終了]

    D --> L{keyがNULL?}
    L -->|Yes| M[例外スロー]
    L -->|No| N[インデックスオープン]
    N --> O[keyで検索]
    O --> P[該当ドキュメント削除]
    P --> K

    E --> Q[新規インデックス作成]
    Q --> K

    M --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-60-01 | キー形式 | 記事は'a'+ID、ページは'p'+ID形式 | 常時 |
| BR-60-02 | 公開コンテンツのみ | status='published'のコンテンツのみインデックス | 更新時 |
| BR-60-03 | スタブ長制限 | 抜粋は140文字で切り詰め | 常時 |
| BR-60-04 | 更新方式 | 更新は削除+追加で実装 | 常時 |

### 計算ロジック

**stubTrim処理**：
- 入力文字列からHTMLタグを除去
- 140文字を超える場合、単語区切りで切り詰め
- 末尾に省略記号（[...]）を追加
- BODY_TRIM_METHOD_STRLENがtrueの場合は単純なsubstr切り詰め

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| なし | - | - | データベース操作なし（ファイルベースインデックス） |

### Luceneインデックス操作詳細

#### インデックスドキュメント構造

| フィールド名 | フィールド型 | 説明 | 検索可能 | 保存 |
|-------------|-------------|------|---------|------|
| key | Keyword | 一意識別キー | Yes | Yes |
| date | Keyword | 日付（ソート用） | Yes | Yes |
| title | Text | タイトル | Yes | Yes |
| details | Text | 詳細情報 | Yes | Yes |
| stub | Text | 抜粋文 | Yes | Yes |
| url | Text | URL | Yes | Yes |
| contents | UnStored | 本文（検索のみ） | Yes | No |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Exception | deleteEntry時にkeyがNULL | 'Invalid entry key'例外スロー |
| - | Exception | newEntry/updateEntry時にparamsが配列でない | 'Invalid parameters'例外スロー |
| - | Zend_Search_Lucene_Exception | インデックスファイルが存在しない | インデックス再作成が必要 |

### リトライ仕様

リトライ処理は実装されていない。ファイルロック等の一時的エラー時はそのまま例外がスローされる。

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

Luceneインデックスはファイルベースであり、データベーストランザクションとは独立している。コンテンツのDB更新が成功した後にインデックス更新が実行されるため、インデックス更新失敗時にDBはロールバックされない。

## パフォーマンス要件

- インデックス更新は同期処理で実行される
- 大量コンテンツ更新時は個別に更新されるためパフォーマンス低下の可能性あり
- インデックスファイルはファイルロックにより同時書き込みが制御される

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

- インデックスファイルはWebアクセス不可能な場所に配置
- 検索クエリはLuceneクエリパーサーで処理（SQLインジェクションとは無関係）
- コンテンツのHTMLタグはstrip_tagsで除去されてインデックス化

## 備考

- SearchモデルとSearchIndexアクションヘルパーは同様の機能を持つ重複実装
- 現在はSearchモデル経由でArticles/Pagesから呼び出されている
- SearchIndexヘルパーはコントローラから直接利用可能だが、使用箇所なし
- インデックスパスはレジストリのsearch->search->syspahtに設定

---

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

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

### 推奨読解順序

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

Luceneインデックスのドキュメント構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Search.php | `application/models/Search.php` | createEntryメソッドのフィールド定義（61-75行目） |

**読解のコツ**: Zend_Search_Lucene_Fieldの3つの型を理解する。
- Keyword: 完全一致検索用、トークナイズなし
- Text: 全文検索用、トークナイズあり、保存あり
- UnStored: 全文検索用、トークナイズあり、保存なし（検索結果には含まれない）

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

インデックス更新の各メソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Search.php | `application/models/Search.php` | deleteEntry（81-97行目）、newEntry（103-110行目）、updateEntry（116-124行目） |

**主要処理フロー**:
1. **81-97行目**: deleteEntry - keyをNULLチェック後、Lucene検索でヒットしたドキュメントを削除
2. **103-110行目**: newEntry - パラメータ配列チェック後、createEntryを呼び出し
3. **116-124行目**: updateEntry - deleteEntry + createEntryで更新を実装

#### Step 3: 呼び出し元を理解する

実際にインデックスを更新するモデルを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Articles.php | `application/models/Articles.php` | updateArticle内のupdateEntry呼び出し（358-366行目） |
| 3-2 | Articles.php | `application/models/Articles.php` | deleteArticle内のdeleteEntry呼び出し（270-271行目） |
| 3-3 | Pages.php | `application/models/Pages.php` | updatePage内のupdateEntry呼び出し（226-234行目） |
| 3-4 | Pages.php | `application/models/Pages.php` | deletePage内のdeleteEntry呼び出し（194-195行目） |

**主要処理フロー**:
- 記事更新時：status='published'の場合のみインデックス更新
- ページ更新時：status='published'の場合のみインデックス更新
- 削除時：ステータス関係なくインデックスから削除

#### Step 4: ヘルパーの代替実装を理解する

アクションヘルパーとしての実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SearchIndex.php | `application/helpers/SearchIndex.php` | delete（62-74行目）、add（76-94行目）、update（96-117行目）、create（119-126行目） |

**注意**: SearchIndexヘルパーはSearchモデルと同等の機能を持つが、現在使用されていない。

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

```
Articles::updateArticle() / Pages::updatePage()
    │
    └─ Search::updateEntry($params)
           │
           ├─ Search::deleteEntry($key)
           │      ├─ Zend_Search_Lucene::open()
           │      ├─ $index->find('key:' . $key)
           │      └─ $index->delete($hit->id)
           │
           └─ Search::createEntry($params)
                  ├─ Zend_Search_Lucene::open()
                  ├─ new Zend_Search_Lucene_Document()
                  ├─ Zend_Search_Lucene_Field::Keyword('key', ...)
                  ├─ Zend_Search_Lucene_Field::Text('title', ...)
                  ├─ Zend_Search_Lucene_Field::UnStored('contents', ...)
                  └─ $index->addDocument($doc)

Articles::deleteArticle() / Pages::deletePage()
    │
    └─ Search::deleteEntry('a'.$id / 'p'.$id)
           ├─ Zend_Search_Lucene::open()
           ├─ $index->find('key:' . $key)
           └─ $index->delete($hit->id)
```

### データフロー図

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

コンテンツ更新 ───▶ Articles/Pages    ───▶ Luceneインデックス
(title, content,      Model                  (site-index/)
 url, etc.)              │
                         ▼
                   Search Model
                         │
                    ┌────┴────┐
                    ▼         ▼
               deleteEntry  createEntry
                    │         │
                    ▼         ▼
              Zend_Search_Lucene
                         │
                         ▼
                   site-index/
                  (ファイルシステム)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Search.php | `application/models/Search.php` | モデル | Luceneインデックス操作のメイン実装 |
| SearchIndex.php | `application/helpers/SearchIndex.php` | ヘルパー | アクションヘルパーとしての代替実装（未使用） |
| Articles.php | `application/models/Articles.php` | モデル | 記事更新・削除時にインデックス更新を呼び出し |
| Pages.php | `application/models/Pages.php` | モデル | ページ更新・削除時にインデックス更新を呼び出し |
