предоставляющее функционал полнотекстового поиска. В отличие от стандартных операторов LIKE или REGEXP, которые сканируют всю таблицу построчно (O(N)), FTS5 строит инвертированный индекс (O(log N)), позволяя находить вхождения слов в миллионах строк за доли миллисекунд.

---

1. Архитектура и хранение данных (Инвертированный индекс)

При создании FTS5-таблицы SQLite автоматически формирует виртуальную таблицу, под капотом которой создаются 4–5 служебных таблиц базы данных (формата имя_fts_content, имя_fts_idx, имя_fts_data и др.).

Главный механизм FTS5 — инвертированный индекс:

  • В обычной таблице: rowid → текст.
  • В инвертированном индексе: термин (слово) → [rowid, позиция в тексте].

Структура fts_data (B-Tree + Segments)

Индекс FTS5 разбивается на сегменты. Каждый сегмент содержит лексикографически отсортированный список терминов.

  • Level 0 (RAM/Малые сегменты): Новые вставки попадают во временную структуру.
  • Level N (Диск): По мере роста данные сливаются (Merge Operations) в более крупные сегменты.

Каждая запись в индексе хранит Posting List (список вхождений) с применением вариативного кодирования целых чисел (varint) и разностного кодирования (delta compression) для минимизации размера на диске.

---

2. Процесс обработки текста: Токенизаторы

Перед сохранением слова в индекс текст проходит через токенизатор, который разбивает поток символов на токены (слова).

В SQLite3 встроен ряд стандартных токенизаторов:

| Токенизатор | Принцип работы | Пример применения |
| --- | --- | --- |
| unicode61 *(по умолчанию)* | Разбивает по пробелам и знакам пунктуации. Приводит к нижнему регистру и удаляет диакритические знаки (акценты). | Полнотекстовый поиск по общему языковому тексту. |
| ascii | Аналог unicode61, но обрабатывает только символ ASCII. Работает быстрее. | Англоязычный текст или идентификаторы. |
| porter | Обертка над другим токенизатором, применяющая алгоритм Стеминга Портера (отсечение окончаний). | Поиск форм слова (например, *running* → *run*). |
| trigram | Разбивает текст на подстроки по 3 символа (триграммы). | Поиск по частичным совпадениям внутри слов (LIKE %abc%). |

-- Пример создания таблицы с токенизатором porter
CREATE VIRTUAL TABLE documents USING fts5(
    title, 
    body, 
    tokenize = 'porter unicode61 remove_diacritics 1'
);

---

3. Модели таблиц: Внутренний vs Внешний контент

FTS5 поддерживают 3 формата хранения данных, влияющих на размер базы и производительность:

1. Content Table (Стандартный режим):
FTS5 хранит и инвертированный индекс, и копию оригинального текста. Запросы SELECT title FROM fts возвращают данные напрямую из FTS5.
2. Contentless Table (content=''):
FTS5 хранит только инвертированный индекс. Оригинальный текст отбрасывается.

  • *Плюс:* Огромная экономия места на диске.
  • *Минус:* Из FTS-таблицы нельзя запросить оригинальный текст; невозможно выполнить подсветку совпадений (highlight()) без внешней передачи оригинальных данных.

3. External Content Table (content='my_table', content_rowid='id'):
FTS5 индексирует данные из обычной таблицы SQLite. Текст в FTS5 не дублируется. Синхронизация между основной таблицей и FTS5 выполняется вручную через триггеры (AFTER INSERT, AFTER DELETE, AFTER UPDATE).

---

4. Язык запросов (Query Syntax)

FTS5 поддерживает богатый синтаксис поиска через оператор MATCH:

  • Поиск по флуидному слову / флуидной фразе:
SELECT * FROM documents WHERE documents MATCH 'sqlite database'; -- Поиск записей со обоими словами
SELECT * FROM documents WHERE documents MATCH '"sqlite database"'; -- Строгая фраза
  • Логические операторы:
SELECT * FROM documents WHERE documents MATCH 'sqlite NOT postgresql';
SELECT * FROM documents WHERE documents MATCH 'fast OR quick';
  • Префиксный поиск (Prefix searches):
SELECT * FROM documents WHERE documents MATCH 'optim*'; -- Оптимизирует, оптимизация и т.д.
  • Ограничение по колонкам:
SELECT * FROM documents WHERE documents MATCH 'title:sqlite AND body:performance';
  • Оператор NEAR (Близость слов):
-- Найти "sqlite" и "speed", между которыми не более 5 слов
SELECT * FROM documents WHERE documents MATCH 'NEAR("sqlite" "speed", 5)';

---

5. Ранжирование и Вспомогательные функции

FTS5 предоставляет встроенные алгоритмы для сортировки результатов по релевантности.

Встроенные функции ранжирования

1. BM25 (bm25(fts_table)):
Алгоритм ранжирования Okapi BM25 — индустриальный стандарт. Учитывает частоту термина (TF) и обратную частоту документа (IDF).

SELECT title, bm25(documents) AS rank 
FROM documents 
WHERE documents MATCH 'sqlite search' 
ORDER BY rank; -- BM25 возвращает отрицательные значения, меньшее число = выше релевантность

Можно задавать вес колонок (например, совпадение в title в 10 раз важнее, чем в body):

SELECT title FROM documents WHERE documents MATCH 'sqlite' ORDER BY bm25(documents, 10.0, 1.0);

2. Highlight и Snippet:
Встроенные функции для генерации фрагментов с подсвеченными совпадениями.

SELECT snippet(documents, 1, '**', '**', '...', 10) FROM documents WHERE documents MATCH 'search';

---

6. Производительность и Обслуживание (Optimize)

При частых операциях INSERT, UPDATE и DELETE индекс FTS5 становится фрагментированным, так как новые записи добавляются в мелкие сегменты, а удаленные отмечаются в "tombstone"-списках (без мгновенного физического удаления).

Для поддержания максимальной скорости чтения необходимо периодически выполнять процедуру слияния сегментов:

  • Интенсивное слияние (Optimize): Объединяет все сегменты индекса в один крупный сегмент.
INSERT INTO documents(documents) VALUES('optimize');
  • Пошаговое автослияние (Automerge): FTS5 автоматически объединяет мелкие сегменты в фоновом режиме по достижении лимита automerge (по умолчанию 4).