Технологии

Что такое FTS5

Полнотекстовый поиск, встроенный в SQLite. Работает из коробки и почти бесплатен — с одной оговоркой, из-за которой «договор» не находит «договоры».

FTS5 — модуль полнотекстового поиска, встроенный в SQLite. Он создаёт виртуальную таблицу, за которой стоит обратный индекс: для каждого слова хранится список документов, где оно встречается. Поиск по такому индексу не требует перебора всех строк, поэтому работает быстро и на сотнях тысяч записей.

Отдельного сервера не нужно — это тот же файл базы данных.

CREATE VIRTUAL TABLE messages_fts USING fts5(
    body,
    content='messages',
    content_rowid='id',
    tokenize='unicode61 remove_diacritics 2'
);

SELECT rowid, bm25(messages_fts) AS score
FROM messages_fts
WHERE messages_fts MATCH 'договор AND подрядчик'
ORDER BY score
LIMIT 20;

Параметр content= делает таблицу внешней: сам текст остаётся в исходной таблице, а в индексе лежат только служебные структуры. Это экономит место, но требует синхронизировать индекс триггерами при вставке и удалении.

#Оговорка про русскую морфологию

Здесь FTS5 подводит новичков чаще всего. Встроенных токенизаторов немного:

Токенизатор Что делает Русский
unicode61 режет по не-буквам, приводит регистр без морфологии
ascii то же, но только для латиницы не подходит
porter стеммер английского языка не работает
trigram индексирует тройки символов поиск по подстроке

Строка porter — источник главного недоразумения: его добавляют «для морфологии», но это стеммер английского, и на русские окончания он не действует. В результате запрос договор не находит документ со словом договоры, а встреча не находит встречи.

Обходные пути, в порядке роста трудозатрат:

  1. Префиксный поиск: MATCH 'договор*' — покрывает окончания, но ловит и лишнее вроде «договорённость».
  2. Индекс префиксов при создании таблицы: prefix='2 3 4' — ускоряет такой поиск ценой размера индекса.
  3. Токенизатор trigram — честный поиск по подстроке, индекс заметно больше, ранжирование хуже.
  4. Нормализация до индексации — приведение слов к начальной форме сторонней библиотекой перед записью. Самый качественный и самый трудоёмкий путь.

Развёрнутый разбор с примерами и замерами — в статье про полнотекстовый поиск FTS5 в SQLite.

#Где путают

Заблуждение первое: FTS5 понимает смысл. Не понимает. Он ищет вхождения слов; синоним, перифраз и опечатка ему недоступны. За смысл отвечает векторный поиск, и связка обоих обычно работает лучше каждого по отдельности — как именно, разобрано в разборе гибридного поиска BM25 и векторов.

Заблуждение второе: bm25() возвращает «больше — лучше». В SQLite функция возвращает отрицательное число, и лучший результат — наименьший. Сортировка ORDER BY bm25(t) DESC даёт худшие совпадения первыми, и ошибка выглядит как «поиск находит ерунду». Что означает сама метрика — в справке про BM25.

Заблуждение третье: FTS5 подходит только для маленьких баз. Практический потолок гораздо выше, чем принято думать: узкое место обычно не в объёме, а в частоте записи и в блокировках. Замеры на больших таблицах — в разборе производительности SQLite FTS5 на миллионах строк.

#Частые вопросы

#Нужно ли отдельно строить индекс после наполнения таблицы?

Нет, индекс обновляется при вставке. Но если таблица внешняя, синхронизацию обеспечивают триггеры, и при массовой загрузке их иногда отключают, а индекс перестраивают одной командой rebuild в конце.

#Что делать с опечатками в запросе?

FTS5 их не прощает. Практическое решение — падать в поиск по подстроке, когда точный запрос ничего не вернул, либо держать отдельный индекс на триграммах для нечёткого случая.

#Можно ли искать по фразе целиком?

Да, фраза берётся в двойные кавычки внутри выражения MATCH. Поддерживаются также операторы AND, OR, NOT и поиск рядом стоящих слов через NEAR.

#FTS5 включён в SQLite по умолчанию?

В большинстве сборок — да, но не во всех. Проверяется одной командой: попыткой создать виртуальную таблицу. Если модуль не собран, вы получите ошибку сразу, а не при первом поиске.

Остальные термины — в глоссарии терминов ИИ.

Хотите ИИ, который помнит вас?

Persona держит контекст между чатами и работает на вашем железе — если развернуть её у себя и подключить локальную модель.

Создать аккаунт