# 機能設計書 78-電話番号アナライザ

## 概要

本ドキュメントは、OpenSearchの電話番号アナライザプラグイン（analysis-phonenumber）の機能設計について記述する。Google libphonenumberライブラリを使用した電話番号の解析・トークン化機能を提供するプラグインである。

### 本機能の処理概要

電話番号アナライザプラグインは、電話番号文字列をパース・正規化し、国番号、国内番号、NGram等の各種トークンを生成する。インデックス用（phone）と検索用（phone-search）の2つのアナライザを提供する。

**業務上の目的・背景**：電話番号は国際的に多様なフォーマットで記載されるため、単純な文字列一致では柔軟な検索ができない。国番号付き/なし、ハイフンやスペースの有無など、様々なフォーマットの電話番号を統一的に検索可能にするため、専用のアナライザが必要である。

**機能の利用シーン**：顧客管理システム、連絡先データベース、SIPアドレス管理等で電話番号フィールドに対する検索が必要な場合に使用される。

**主要な処理内容**：
1. PhoneNumberAnalyzer（phone）: NGramトークン生成付きの電話番号解析（インデックス用）
2. PhoneNumberAnalyzer（phone-search）: NGramなしの電話番号解析（検索用）
3. PhoneNumberTermTokenizer: Google libphonenumberによる電話番号パースとトークン生成
4. tel:/sip:プレフィックスの除去
5. @以降のドメイン部分の除去
6. 国番号の分離と国内番号のNGram生成

**関連システム・外部連携**：Google libphonenumber。

**権限による制御**：特別な権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 55 | アナライズ | 参照画面 | テキストに対して電話番号解析プロセスを実行しトークン分解を返す処理 |

## 機能種別

テキスト解析処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| phone-region | String | No | 電話番号のデフォルト地域コード（デフォルト: ZZ） | ISO 3166-1 alpha-2国コード |

### 入力データソース

インデックス設定（index settings）のanalyzerセクションでanalyzerまたはtokenizerとして指定。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tokens | Set<String> | 生成されたトークンセット（重複なし、順序なし） |

phoneアナライザ（addNgrams=true）の場合、以下のトークンが生成される:
- 元の入力文字列
- tel:/sip:プレフィックス（ある場合）
- +を除いた入力文字列
- 国番号 + 国内番号
- 国番号のみ
- 国内番号のみ
- 拡張番号（ある場合）
- 国内番号のNGram（1文字から全文字まで）
- 国番号 + 国内番号NGram

phone-searchアナライザ（addNgrams=false）の場合:
- 元の入力文字列
- +を除いた入力文字列
- 国番号 + 国内番号

### 出力先

Luceneインデックスの転置インデックス、またはAnalyze APIのレスポンス。

## 処理フロー

### 処理シーケンス

```
1. プラグインロード
   └─ PhoneNumberAnalysisPlugin.getAnalyzers/getTokenizersでphone, phone-searchを登録
2. テキスト解析
   └─ PhoneNumberTermTokenizer.getTokens()で電話番号をパース・トークン化
      a. 元入力をトークンに追加
      b. tel:/sip:プレフィックスを処理
      c. @以降を除去
      d. PhoneNumberUtilで電話番号パース
      e. 国番号・国内番号を分離
      f. addNgrams=trueの場合、NGram生成
```

### フローチャート

```mermaid
flowchart TD
    A[電話番号入力] --> B{tel:/sip:プレフィックス?}
    B -->|Yes| C[プレフィックス除去]
    B -->|No| D[元文字列追加]
    C --> D
    D --> E{@あり?}
    E -->|Yes| F[@以降を除去]
    E -->|No| G[PhoneNumberUtil.parse]
    F --> G
    G --> H{パース成功?}
    H -->|Yes| I[国番号+国内番号をトークン追加]
    H -->|No| J{addNgrams?}
    I --> J
    J -->|Yes| K[NGram生成]
    J -->|No| L[トークン出力]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-78-01 | デフォルトリージョン | phone-region未指定時はZZ（不明）を使用 | PhoneNumberTermTokenizer（126行目） |
| BR-78-02 | インデックス用NGram | phoneアナライザ（addNgrams=true）はNGram生成を行い、部分一致検索を可能にする | PhoneNumberAnalysisPlugin（32行目） |
| BR-78-03 | 検索用シンプル | phone-searchアナライザ（addNgrams=false）はNGramを生成せず、完全一致系の検索に使用 | PhoneNumberAnalysisPlugin（36行目） |
| BR-78-04 | 重複排除 | トークンはHashSetで管理され、重複は自動排除 | PhoneNumberTermTokenizer（93行目） |
| BR-78-05 | 数字のみNGram | NGram生成は入力が数字のみの場合に実行 | PhoneNumberTermTokenizer（156行目） |

### 計算ロジック

NGram生成: 国内番号が"9198243333"の場合、"9", "91", "919", "9198", ... と1文字目から全文字までの部分文字列を生成。国番号がある場合は"19", "191", "1919", ...も追加。

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

本機能は直接的なデータベース操作を行わない。

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

該当なし。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NumberParseException | パースエラー | PhoneNumberUtilが電話番号を解析できない | エラーは無視されNGram処理に進む |
| StringIndexOutOfBoundsException | パースエラー | 不正な文字列フォーマット | エラーは無視されNGram処理に進む |

### リトライ仕様

リトライは不要。パースエラーは例外をキャッチして処理を継続する（PhoneNumberTermTokenizer 151行目）。

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

テキスト解析はステートレス処理であり、トランザクション管理は不要。

## パフォーマンス要件

NGram生成はトークン数が電話番号桁数に比例して増加するため、大量ドキュメントのインデックス時にはトークン数増加に注意が必要。

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

電話番号は個人情報（PII）に該当するため、インデックスデータの適切なアクセス制御が必要である。

## 備考

phoneとphone-searchの2つのアナライザは、indexフィールドにphone、search_analyzerフィールドにphone-searchを指定して組み合わせて使用することが推奨される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PhoneNumberAnalysisPlugin.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberAnalysisPlugin.java` | phone, phone-searchの2つのアナライザと2つのトークナイザを登録 |

**読解のコツ**: phoneとphone-searchの違いはaddNgramsパラメータ（true/false）のみ。

#### Step 2: アナライザを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PhoneNumberAnalyzer.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberAnalyzer.java` | Analyzerのシンプルな実装。TokenStreamComponentsとしてPhoneNumberTermTokenizerを使用 |

**主要処理フロー**:
- **41-44行目**: コンストラクタ - addNgramsとsettingsを保持
- **47-50行目**: createComponents() - PhoneNumberTermTokenizerを生成し、TokenStreamComponentsとして返却

#### Step 3: トークナイザを理解する（コアロジック）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PhoneNumberTermTokenizer.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberTermTokenizer.java` | getTokens()メソッド（92-166行目）がコアロジック。電話番号のパース、プレフィックス処理、NGram生成を実装 |

**主要処理フロー**:
- **92-166行目**: getTokens() - メインのトークン生成ロジック
  - **95-98行目**: 入力文字列読み込みと元トークン追加
  - **100-105行目**: tel:/sip:プレフィックス処理
  - **107-109行目**: +記号のスキップ
  - **112-119行目**: @以降のドメイン除去
  - **122-153行目**: PhoneNumberUtil.parse()による電話番号パース
  - **130-131行目**: 国番号と国内番号の分離
  - **134行目**: 国番号+国内番号をトークン追加
  - **136-148行目**: addNgrams=true時の追加トークン生成
  - **156-163行目**: 数字のみの場合のNGram生成ループ

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

```
PhoneNumberAnalysisPlugin (プラグイン登録)
    |
    +-- getAnalyzers()
    |       +-- PhoneNumberAnalyzerProvider ("phone", addNgrams=true)
    |       +-- PhoneNumberAnalyzerProvider ("phone-search", addNgrams=false)
    |               +-- PhoneNumberAnalyzer
    |                       +-- createComponents()
    |                               +-- PhoneNumberTermTokenizer
    |                                       +-- getTokens()
    |                                           +-- PhoneNumberUtil.parse()
    |
    +-- getTokenizers()
            +-- PhoneNumberTermTokenizerFactory ("phone", addNgrams=true)
            +-- PhoneNumberTermTokenizerFactory ("phone-search", addNgrams=false)
```

### データフロー図

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

電話番号文字列 ───> プレフィックス除去 ───> PhoneNumberUtil.parse() ───> トークンセット
("+81-3-1234-5678")  (tel:/sip:/@)         (国番号/国内番号分離)        ({813...}, {81}, {3...},
                                                                         NGram: {8,81,813,...})
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PhoneNumberAnalysisPlugin.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberAnalysisPlugin.java` | ソース | プラグインエントリーポイント |
| PhoneNumberAnalyzer.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberAnalyzer.java` | ソース | 電話番号アナライザ |
| PhoneNumberAnalyzerProvider.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberAnalyzerProvider.java` | ソース | アナライザプロバイダ |
| PhoneNumberTermTokenizer.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberTermTokenizer.java` | ソース | 電話番号トークナイザ（コアロジック） |
| PhoneNumberTermTokenizerFactory.java | `plugins/analysis-phonenumber/src/main/java/org/opensearch/analysis/phone/PhoneNumberTermTokenizerFactory.java` | ソース | トークナイザファクトリ |
