# 機能設計書 36-FTS5全文検索

## 概要

本ドキュメントは、SQLiteにおけるFTS5（Full-Text Search 5）全文検索機能の設計仕様を定義する。FTS3/FTS4の後継として、より高機能で拡張性のある全文検索エンジンを提供する。

### 本機能の処理概要

FTS5は、FTS3/FTS4の設計を一新し、より柔軟なトークナイザーAPI、拡張可能なランキング関数、ロケール対応などの機能を備えた第5世代全文検索エンジンである。

**業務上の目的・背景**：FTS3/FTS4は多くのアプリケーションで使用されているが、カスタムランキング関数の追加が困難、トークナイザーAPIが限定的、国際化サポートが不十分等の課題があった。FTS5はこれらの課題を解決し、より洗練されたAPIと機能を提供する。

**機能の利用シーン**：
- 多言語対応の全文検索システム
- カスタムランキング（BM25等）を使用した検索結果並び替え
- カラム単位のブースティング
- 高度なスニペット生成
- インクリメンタル検索（ユーザー入力中のリアルタイム検索）

**主要な処理内容**：
1. CREATE VIRTUAL TABLE...USING fts5でテーブル作成
2. 新しいトークナイザーAPIv2でテキスト分解（ロケール対応）
3. 拡張可能な補助関数（bm25、highlight、snippet）
4. fts5vocab仮想テーブルでインデックス内容を可視化
5. セグメント自動マージと手動最適化

**関連システム・外部連携**：
- 仮想テーブルモジュール：FTS5は仮想テーブルとして実装
- fts5 API：拡張関数登録インターフェース
- トークナイザーAPI v2：ロケール対応トークナイザー

**権限による制御**：
- 通常のテーブル同様、sqlite3_set_authorizer()による認可チェック
- 独立したネームスペースでの拡張関数登録

## 関連画面

本機能はSQLite内部機能のため、直接的な画面関連はない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | SQLite3 Fiddle | 参照画面 | FTS5テーブルの作成・検索 |

## 機能種別

全文検索 / ランキング / トークナイザー拡張

## 入力仕様

### 入力パラメータ（テーブル作成）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| table_name | TEXT | Yes | FTS5テーブル名 | 有効な識別子 |
| columns | TEXT | Yes | 検索対象カラム定義 | 1つ以上必要 |
| tokenize | TEXT | No | トークナイザー指定 | 有効なトークナイザー名 |
| content | TEXT | No | 外部コンテンツテーブル | 空文字列でcontentless |
| content_rowid | TEXT | No | 外部コンテンツのrowidカラム | contentと併用 |
| prefix | TEXT | No | プレフィックスインデックス | "2,4"等 |
| columnsize | TEXT | No | カラムサイズ保存（0/1） | デフォルト1 |
| detail | TEXT | No | 詳細レベル（full/column/none） | 検索機能に影響 |

### MATCH構文（FTS5拡張）

| 構文要素 | 説明 | 例 |
|---------|------|-----|
| 単語 | 単純なキーワード検索 | `word` |
| フレーズ | 連続する単語の検索 | `"hello world"` |
| AND | 両方を含む（暗黙） | `word1 word2` |
| OR | いずれかを含む | `word1 OR word2` |
| NOT | 除外 | `word1 NOT word2` |
| NEAR | 近接検索 | `NEAR(word1 word2)` |
| * | プレフィックス検索 | `wor*` |
| ^ | 先頭一致 | `^first` |
| カラム指定 | 特定カラムでの検索 | `{column}: word` |
| カラム除外 | 特定カラムを除外 | `-{column}: word` |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| rowid | INTEGER | 行識別子 |
| カラム値 | TEXT | 格納されたテキスト |
| rank | REAL | bm25()等のランキングスコア |
| bm25() | REAL | BM25ランキング関数 |
| highlight() | TEXT | ハイライト付きテキスト |
| snippet() | TEXT | マッチ周辺のスニペット |

### 出力先

- 結果セット（SELECT文の結果）
- fts5vocab仮想テーブル（インデックス統計）

## 処理フロー

### 処理シーケンス

```
1. テーブル作成フェーズ
   ├─ CREATE VIRTUAL TABLE...USING fts5
   ├─ シャドウテーブル作成（_data, _idx, _content, _docsize, _config）
   └─ Fts5Config構造体の初期化

2. データ挿入フェーズ
   ├─ Fts5Storage: コンテンツ保存
   ├─ トークナイザーv2 API: ロケール対応トークン化
   └─ Fts5Index: 転置インデックス更新

3. 検索フェーズ
   ├─ Fts5Expr: クエリパース・式ツリー構築
   ├─ Fts5Cursor: 検索実行・結果イテレーション
   └─ 補助関数: bm25/highlight/snippet計算

4. 拡張関数フェーズ
   ├─ fts5_api_from_db(): fts5 API取得
   ├─ xCreateTokenizer_v2(): カスタムトークナイザー登録
   └─ xCreateFunction(): カスタム補助関数登録
```

### フローチャート

```mermaid
flowchart TD
    A[CREATE VIRTUAL TABLE...USING fts5] --> B[Fts5Config解析]
    B --> C[シャドウテーブル作成]
    C --> D[Fts5Global構造体初期化]

    E[INSERT INTO fts5_table] --> F[Fts5Storage.pContentInsert]
    F --> G[トークナイザー呼び出し]
    G --> H[Fts5Index更新]

    I[SELECT WHERE MATCH] --> J[Fts5Expr構築]
    J --> K[Fts5Cursor初期化]
    K --> L[xFilter: 検索開始]
    L --> M{結果あり?}
    M -->|Yes| N[xColumn: 値取得]
    M -->|No| O[完了]
    N --> P{bm25等要求?}
    P -->|Yes| Q[補助関数計算]
    P -->|No| R[xNext]
    Q --> R
    R --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-1 | ロケール対応 | トークナイザーv2 APIでロケールを考慮したトークン化 | fts5_locale()使用時 |
| BR-36-2 | カラムブースト | bm25()でカラムごとに重み付け可能 | bm25(fts, w1, w2, ...)使用時 |
| BR-36-3 | 自動マージ | セグメント数が閾値を超えると自動マージ | INSERT時 |
| BR-36-4 | detail設定 | detail=noneでストレージ削減、検索機能制限 | テーブル作成時 |
| BR-36-5 | 先頭一致 | ^演算子で先頭一致検索が可能 | FTS5独自機能 |

### 計算ロジック

**BM25スコア計算**：
```
BM25(D, Q) = Sum over q in Q of:
  IDF(q) * (f(q, D) * (k1 + 1)) / (f(q, D) + k1 * (1 - b + b * |D| / avgdl))

where:
  IDF(q) = log((N - n(q) + 0.5) / (n(q) + 0.5))
  f(q, D) = term frequency in document D
  |D| = document length
  avgdl = average document length
  k1 = 1.2 (default)
  b = 0.75 (default)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| CREATE | fts5_table | CREATE | 仮想テーブル作成 |
| CREATE | fts5_table_data | CREATE | インデックスデータ |
| CREATE | fts5_table_idx | CREATE | 語彙インデックス |
| CREATE | fts5_table_content | CREATE | コンテンツ格納（contentless以外） |
| CREATE | fts5_table_docsize | CREATE | ドキュメントサイズ |
| CREATE | fts5_table_config | CREATE | 設定情報 |

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

#### _data

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | id | 自動採番 | ブロックID |
| INSERT/UPDATE | block | BLOB | 圧縮済みインデックスデータ |

#### _docsize

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | rowid | ドキュメントID |
| INSERT | sz | BLOB | 各カラムのトークン数 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SQLITE_ERROR | 構文エラー | 不正なMATCH構文 | MATCH構文を修正 |
| SQLITE_ERROR | 設定エラー | 不正なオプション値 | 有効なオプションを指定 |
| SQLITE_CORRUPT | データ破損 | インデックスデータ破損 | rebuild実行 |
| SQLITE_ERROR | detail制限 | detail=noneでNEAR等使用 | detailレベルを上げる |

### リトライ仕様

FTS5操作のエラーは基本的にリトライ不可。データ破損時はrebuild('rebuild')コマンドで再構築。

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

- Fts5Cursorはトランザクション状態を追跡
- xBegin/xSync/xCommit/xRollbackを実装
- セーブポイント（xSavepoint/xRelease/xRollbackTo）をサポート
- WALモードでの並行読み取りをサポート

## パフォーマンス要件

- FTS3/FTS4より効率的なインデックス構造
- detail=noneでストレージを最大75%削減可能
- 自動マージによるフラグメンテーション防止
- bm25()は事前計算された統計情報を使用

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

- カスタムトークナイザー/補助関数はC言語で実装されるため、メモリ安全性に注意
- fts5vocab仮想テーブルでインデックス内容が可視化される点に注意
- ユーザー入力をMATCH句に使用する場合、エスケープ処理が必要

## 備考

- SQLITE_ENABLE_FTS5でコンパイル時に有効化
- FTS3/FTS4との互換性はない（独立したモジュール）
- fts5vocabでインデックス内容（row/col/instance）を可視化可能
- 'rebuild'、'optimize'、'merge'コマンドで保守操作

---

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

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

### 推奨読解順序

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

FTS5の中核となるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | fts5Int.h | `ext/fts5/fts5Int.h` | Fts5Table、Fts5Cursor、Fts5Global等の構造体定義 |
| 1-2 | fts5_main.c | `ext/fts5/fts5_main.c` | 37-88行目のトランザクション管理コメント |
| 1-3 | fts5_main.c | `ext/fts5/fts5_main.c` | 78-152行目のFts5Global/Fts5FullTable構造体 |

**読解のコツ**: FTS5はFTS3とは異なるアーキテクチャ。Fts5Global（接続単位）→Fts5FullTable（テーブル単位）→Fts5Cursor（カーソル単位）の階層構造を理解する。

#### Step 2: 設定とテーブル作成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fts5_config.c | `ext/fts5/fts5_config.c` | Fts5Config構造体、オプション解析 |

**主要処理フロー**:
- テーブルオプション（content, prefix, detail等）の解析
- トークナイザー設定の解析

#### Step 3: インデックス構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fts5_index.c | `ext/fts5/fts5_index.c` | Fts5Index構造体、インデックス操作 |
| 3-2 | fts5_storage.c | `ext/fts5/fts5_storage.c` | Fts5Storage、コンテンツ管理 |

**主要処理フロー**:
- B-Tree構造のインデックス管理
- セグメントの書き込みとマージ

#### Step 4: クエリ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fts5_expr.c | `ext/fts5/fts5_expr.c` | Fts5Expr、クエリパース |
| 4-2 | fts5_main.c | `ext/fts5/fts5_main.c` | fts5FilterMethod、fts5NextMethod |

#### Step 5: 補助関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | fts5_aux.c | `ext/fts5/fts5_aux.c` | bm25、highlight、snippet実装 |

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

```
CREATE VIRTUAL TABLE...USING fts5
    │
    └─ fts5InitVtab()
           │
           ├─ sqlite3Fts5ConfigParse()  設定解析
           ├─ sqlite3Fts5StorageOpen()  ストレージ初期化
           └─ sqlite3Fts5IndexOpen()    インデックス初期化

INSERT INTO fts5_table
    │
    └─ fts5UpdateMethod()
           │
           ├─ sqlite3Fts5StorageContentInsert()
           └─ sqlite3Fts5IndexWrite()
                  │
                  └─ fts5WriteAppendRowid()
                  └─ fts5WriteBtreeTerm()

SELECT WHERE MATCH
    │
    ├─ fts5BestIndexMethod()
    │
    └─ fts5FilterMethod()
           │
           ├─ sqlite3Fts5ExprNew()  クエリパース
           │
           └─ fts5CursorFirst()
                  │
                  └─ fts5NextMethod()

bm25() / highlight() / snippet()
    │
    └─ fts5Bm25Function() / fts5HighlightFunction() / fts5SnippetFunction()
```

### データフロー図

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

テキストデータ ────────────▶ トークナイザーv2 ─────────────▶ トークンリスト
fts5_locale()               xTokenize()                     位置+ロケール情報

トークンリスト ────────────▶ Fts5Index ────────────────────▶ _data, _idx
                            sqlite3Fts5IndexWrite()         B-Treeインデックス

MATCHクエリ ──────────────▶ sqlite3Fts5ExprNew() ─────────▶ Fts5Expr
"word1 word2"               クエリパース                     式ツリー

Fts5Expr ─────────────────▶ fts5CursorFirst() ────────────▶ 結果セット
式ツリー                     インデックス検索                 rowid列

結果セット ───────────────▶ fts5Bm25Function() ───────────▶ ランキング
rowid列                     BM25計算                         負のスコア値

fts5vocab ─────────────────▶ インデックス可視化 ────────────▶ 統計情報
                                                            term/doc/col/cnt
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fts5_main.c | `ext/fts5/fts5_main.c` | ソース | メインモジュール、仮想テーブル実装 |
| fts5Int.h | `ext/fts5/fts5Int.h` | ヘッダー | 内部構造体定義 |
| fts5_config.c | `ext/fts5/fts5_config.c` | ソース | 設定解析 |
| fts5_index.c | `ext/fts5/fts5_index.c` | ソース | インデックス管理 |
| fts5_storage.c | `ext/fts5/fts5_storage.c` | ソース | コンテンツストレージ |
| fts5_expr.c | `ext/fts5/fts5_expr.c` | ソース | クエリパーサー |
| fts5_aux.c | `ext/fts5/fts5_aux.c` | ソース | 補助関数（bm25等） |
| fts5_tokenize.c | `ext/fts5/fts5_tokenize.c` | ソース | トークナイザー |
| fts5_vocab.c | `ext/fts5/fts5_vocab.c` | ソース | fts5vocab仮想テーブル |
| fts5_hash.c | `ext/fts5/fts5_hash.c` | ソース | ハッシュテーブル |
| fts5_buffer.c | `ext/fts5/fts5_buffer.c` | ソース | バッファ管理 |
