# 機能設計書 35-FTS3FTS4全文検索

## 概要

本ドキュメントは、SQLiteにおけるFTS3/FTS4（Full-Text Search 3/4）全文検索機能の設計仕様を定義する。テキストデータに対する高速な全文検索を実現するための仮想テーブルモジュールである。

### 本機能の処理概要

FTS3/FTS4は、テキストコンテンツを転置インデックス（inverted index）構造で保存し、キーワード検索を高速に実行する全文検索エンジンである。

**業務上の目的・背景**：現代のアプリケーションでは、大量のテキストデータ（ドキュメント、メール、チャット履歴等）から関連する情報を素早く検索する必要がある。通常のLIKE検索ではフルテーブルスキャンが必要で非効率だが、FTSにより転置インデックスを使用した高速検索が可能になる。

**機能の利用シーン**：
- ドキュメント管理システムでのキーワード検索
- メールクライアントでの全文検索
- チャットアプリのメッセージ検索
- Webブラウザの履歴検索
- コンテンツ管理システム（CMS）での記事検索

**主要な処理内容**：
1. CREATE VIRTUAL TABLE...USING fts3/fts4でFTSテーブルを作成
2. INSERT時にトークナイザーでテキストを分解し、転置インデックスを構築
3. MATCH演算子でキーワード検索を実行
4. スニペット、オフセット、マッチ情報等の補助機能を提供
5. セグメントベースのインデックス構造でマージを最適化

**関連システム・外部連携**：
- 仮想テーブルモジュール：FTS3/FTS4は仮想テーブルとして実装
- トークナイザー：simple、porter、unicode61、ICU等
- シャドウテーブル：_content、_segments、_segdir等

**権限による制御**：
- 通常のテーブル同様、sqlite3_set_authorizer()による認可チェック
- シャドウテーブルへの直接アクセスは推奨されない

## 関連画面

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

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

## 機能種別

全文検索 / 転置インデックス / テキスト解析

## 入力仕様

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

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| table_name | TEXT | Yes | FTSテーブル名 | 有効な識別子 |
| columns | TEXT | Yes | 検索対象カラム定義 | 1つ以上必要 |
| tokenize | TEXT | No | トークナイザー指定（デフォルト: simple） | 有効なトークナイザー名 |
| content | TEXT | No | 外部コンテンツテーブル（contentless/content=） | FTS4のみ |
| prefix | TEXT | No | プレフィックスインデックス（"2,4"等） | FTS4のみ |
| compress | TEXT | No | 圧縮関数 | FTS4のみ |
| uncompress | TEXT | No | 解凍関数 | FTS4のみ |

### MATCH構文

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| docid/rowid | INTEGER | ドキュメント識別子 |
| カラム値 | TEXT | 格納されたテキスト |
| snippet() | TEXT | マッチ周辺のスニペット |
| offsets() | TEXT | マッチ位置情報 |
| matchinfo() | BLOB | 統計情報バイナリ |

### 出力先

- 結果セット（SELECT文の結果）
- ランキングスコア（ORDER BY bm25()等）

## 処理フロー

### 処理シーケンス

```
1. テーブル作成フェーズ
   ├─ CREATE VIRTUAL TABLE...USING fts3/fts4
   ├─ シャドウテーブル作成（_content, _segments, _segdir等）
   └─ トークナイザー初期化

2. データ挿入フェーズ
   ├─ テキストをトークナイザーで分解
   ├─ 各トークンの位置情報を記録
   └─ doclistを構築してセグメントに書き込み

3. セグメントマージフェーズ
   ├─ レベル0セグメントがMERGE_COUNT（16）に達したらマージ
   ├─ 上位レベルに統合
   └─ 古いセグメントを削除

4. 検索フェーズ
   ├─ クエリをパースしてFts3Expr式ツリーを構築
   ├─ 各タームに対してセグメントを検索
   ├─ doclistをマージして結果を生成
   └─ スニペット、オフセット等を計算
```

### フローチャート

```mermaid
flowchart TD
    A[INSERT INTO fts_table] --> B[トークナイザー呼び出し]
    B --> C[テキストをトークン分解]
    C --> D[各トークンの位置記録]
    D --> E[doclist構築]
    E --> F[セグメントに書き込み]
    F --> G{セグメント数 >= MERGE_COUNT?}
    G -->|Yes| H[セグメントマージ]
    G -->|No| I[完了]
    H --> I

    J[SELECT WHERE MATCH] --> K[クエリパース]
    K --> L[Fts3Expr式ツリー構築]
    L --> M[各ターム検索]
    M --> N[doclistマージ]
    N --> O[結果生成]
    O --> P{snippet/offsets要求?}
    P -->|Yes| Q[位置情報計算]
    P -->|No| R[結果返却]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-35-1 | トークン化 | テキストはトークナイザーにより分解される | INSERT/UPDATE時 |
| BR-35-2 | 大文字小文字 | デフォルトで大文字小文字を区別しない | simple/porter/unicode61 |
| BR-35-3 | 位置記録 | トークンの位置（カラム内オフセット）を記録 | 常時 |
| BR-35-4 | マージ戦略 | レベルNのセグメントがMERGE_COUNT個になったらレベルN+1にマージ | 自動 |
| BR-35-5 | NULL処理 | NULL値は空文字列として扱われる | INSERT/UPDATE時 |

### 計算ロジック

**Varint（可変長整数）エンコーディング**：
- 7ビット + 継続フラグ方式
- FTS3独自のリトルエンディアン形式
- docidやオフセットの効率的な格納

**doclistフォーマット**：
- docid（前ドキュメントからのデルタ）
- 位置リスト（カラム、オフセット）
- POS_END（0）で終端

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| CREATE | fts_table | CREATE | 仮想テーブル作成 |
| CREATE | fts_table_content | CREATE | コンテンツ格納 |
| CREATE | fts_table_segments | CREATE | セグメントデータ格納 |
| CREATE | fts_table_segdir | CREATE | セグメントディレクトリ |
| INSERT | 全シャドウテーブル | INSERT | ドキュメント追加 |
| SELECT | _segments, _segdir | SELECT | 検索実行 |

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

#### _content（FTS4、content=未指定時）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | docid | 自動採番 | ドキュメントID |
| INSERT | c0, c1, ... | 各カラムのテキスト | 元テキスト保存 |

#### _segments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | blockid | 自動採番 | ブロック識別子 |
| INSERT/UPDATE | block | BLOB | セグメントデータ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SQLITE_ERROR | 構文エラー | 不正なMATCH構文 | MATCH構文を修正 |
| SQLITE_ERROR | トークナイザーエラー | 未知のトークナイザー指定 | 有効なトークナイザーを指定 |
| SQLITE_CORRUPT | データ破損 | セグメントデータ破損 | 再構築が必要 |
| SQLITE_NOMEM | メモリ不足 | 大量データ処理時 | メモリ増設/バッチ処理 |

### リトライ仕様

FTS操作のエラーは基本的にリトライ不可。データ破損時はrebuildコマンドまたはテーブル再作成が必要。

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

- FTS操作は通常のトランザクション内で実行可能
- ロールバック時、シャドウテーブルの変更も取り消される
- WALモードでの並行読み取りをサポート

## パフォーマンス要件

- MERGE_COUNT（16）により挿入性能と検索性能のバランスを最適化
- プレフィックスインデックス（FTS4）で前方一致検索を高速化
- contentless table（FTS4）でストレージ削減可能
- automergeオプションでバックグラウンドマージを制御

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

- シャドウテーブルへの直接アクセスは避けるべき
- ユーザー入力をMATCH句に使用する場合、SQLインジェクションに注意
- 機密テキストは暗号化FTS拡張の使用を検討

## 備考

- SQLITE_ENABLE_FTS3でコンパイル時に有効化
- FTS4はFTS3の上位互換（追加オプションをサポート）
- FTS5は別モジュールとして提供（より高機能）
- porterトークナイザーは英語ステミングをサポート
- unicode61トークナイザーはUnicode正規化をサポート

---

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

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

### 推奨読解順序

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | fts3.c | `ext/fts3/fts3.c` | ファイル先頭コメント（26-289行目）のアーキテクチャ説明 |
| 1-2 | fts3Int.h | `ext/fts3/fts3Int.h` | Fts3Table、Fts3Cursor、Fts3Expr等の構造体定義 |

**読解のコツ**: fts3.c先頭の長いコメントがアーキテクチャの核心。varint、doclist、セグメント、マージ戦略が詳細に説明されている。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | fts3.c | `ext/fts3/fts3.c` | fts3InitVtab()関数 |

**主要処理フロー**:
- シャドウテーブル（_content、_segments、_segdir等）の作成
- トークナイザーの初期化
- Fts3Table構造体の設定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | fts3_write.c | `ext/fts3/fts3_write.c` | fts3UpdateMethod()、fts3InsertData() |

**主要処理フロー**:
- テキストのトークン化
- doclistの構築
- セグメントへの書き込み
- 必要に応じたマージ

#### Step 4: 検索処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | fts3_expr.c | `ext/fts3/fts3_expr.c` | クエリパース、Fts3Expr構築 |
| 4-2 | fts3.c | `ext/fts3/fts3.c` | fts3FilterMethod()、fts3NextMethod() |

**主要処理フロー**:
- MATCH文字列のパース
- 式ツリー（Fts3Expr）の構築
- セグメント検索とdoclistマージ
- 結果のイテレーション

#### Step 5: 補助機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | fts3_snippet.c | `ext/fts3/fts3_snippet.c` | snippet()、offsets()、matchinfo()の実装 |

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

```
CREATE VIRTUAL TABLE...USING fts3
    │
    └─ fts3InitVtab()
           │
           ├─ シャドウテーブル作成
           └─ トークナイザー初期化

INSERT INTO fts_table
    │
    └─ fts3UpdateMethod()
           │
           ├─ トークナイザー呼び出し
           ├─ fts3InsertData()
           │      └─ doclist構築
           └─ セグメント書き込み/マージ

SELECT WHERE MATCH
    │
    ├─ fts3BestIndexMethod()  クエリプラン
    │
    └─ fts3FilterMethod()
           │
           ├─ sqlite3Fts3ExprParse()  クエリパース
           │
           └─ fts3EvalStart()  検索実行
                  │
                  └─ fts3NextMethod()  結果イテレーション

snippet()/offsets()/matchinfo()
    │
    └─ fts3SnippetFunc() / fts3OffsetsFunc() / fts3MatchinfoFunc()
```

### データフロー図

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

テキストデータ ────────────▶ トークナイザー ────────────────▶ トークンリスト
INSERT INTO                  fts3Tokenize()                  位置情報付き

トークンリスト ────────────▶ doclist構築 ──────────────────▶ セグメントデータ
                            fts3PendingTermsAdd()            _segments

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

Fts3Expr ─────────────────▶ fts3EvalStart() ──────────────▶ 結果セット
式ツリー                     セグメント検索                   docid列

結果セット ───────────────▶ fts3SnippetFunc() ────────────▶ スニペット
docid列                     補助関数                         "...match..."
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| fts3.c | `ext/fts3/fts3.c` | ソース | メインモジュール、仮想テーブル実装 |
| fts3Int.h | `ext/fts3/fts3Int.h` | ヘッダー | 内部構造体定義 |
| fts3_write.c | `ext/fts3/fts3_write.c` | ソース | インデックス書き込み |
| fts3_expr.c | `ext/fts3/fts3_expr.c` | ソース | クエリパーサー |
| fts3_snippet.c | `ext/fts3/fts3_snippet.c` | ソース | snippet/offsets/matchinfo |
| fts3_tokenizer.c | `ext/fts3/fts3_tokenizer.c` | ソース | トークナイザーインターフェース |
| fts3_tokenizer1.c | `ext/fts3/fts3_tokenizer1.c` | ソース | simpleトークナイザー |
| fts3_porter.c | `ext/fts3/fts3_porter.c` | ソース | porterステマートークナイザー |
| fts3_unicode.c | `ext/fts3/fts3_unicode.c` | ソース | unicode61トークナイザー |
| fts3_hash.c | `ext/fts3/fts3_hash.c` | ソース | ハッシュテーブル実装 |
| fts3_aux.c | `ext/fts3/fts3_aux.c` | ソース | fts3_terms等の補助テーブル |
