Вы вводите запрос в строку поиска документации. Появляются результаты. Но что произошло между нажатием клавиши и результатами? Для большинства платформ документации ответ — либо «немного» (простое сопоставление ключевых слов, которое пропускает очевидные результаты), либо «много инфраструктуры» (кластер Elasticsearch, который ваша команда эксплуатации должна поддерживать).

Есть золотая середина, о которой большинство команд не знают: встроенный полнотекстовый поиск. DocPlatform использует Bleve, поисковую библиотеку на Go, скомпилированную прямо в бинарный файл. Никаких внешних сервисов, сетевых вызовов, кластеров для управления — но с функциями, которые вам реально нужны: стемминг, нечёткий поиск, бустинг полей и ранжирование по релевантности.

Вот как это работает изнутри.

Почему сопоставление по ключевым словам не работает

Простейшая реализация поиска — LIKE '%query%' в SQL. Это то, что вы получаете, когда разработчик добавляет поиск как побочную мысль. Работает для точных совпадений, но проваливается во всех остальных случаях:

  • Поиск «install» не находит страницы с «installation» или «installing»
  • Поиск «authn» не находит страницы об «authentication»
  • Опечатка «deploymnet» не возвращает ничего
  • Все результаты имеют одинаковый рейтинг — страница с заголовком «Installation Guide» ранжируется так же, как страница, где «install» упоминается один раз в подвале

Некоторые платформы улучшают это расширением SQLite FTS5, которое добавляет токенизацию и базовое ранжирование. Это шаг вперёд, но всё ещё без стемминга, нечёткого поиска и возможности повышать вес определённых полей (например, заголовков) над другими.

Что на самом деле делает полнотекстовый поиск

Полноценный поисковый движок обрабатывает текст в две фазы: индексирование (когда контент создаётся) и запрос (когда кто-то ищет). Обе фазы делают больше работы, чем можно ожидать.

Индексирование: что происходит при сохранении страницы

Когда вы создаёте или обновляете страницу в DocPlatform, контент проходит через аналитический пайплайн перед индексированием:

1. Токенизация — Текст разбивается на отдельные термины. «Getting started with DocPlatform» превращается в ["getting", "started", "with", "docplatform"].

2. Приведение к нижнему регистру — Все токены нормализуются к нижнему регистру. «DocPlatform» и «docplatform» совпадают.

3. Удаление стоп-слов — Распространённые слова вроде «the», «is», «with», «a» удаляются. Они встречаются почти в каждом документе и не помогают различать релевантные результаты.

4. Стемминг — Слова сводятся к корневой форме. «installing», «installation» и «installed» все становятся «instal». Это означает, что поиск по любому из этих слов находит все остальные.

5. Разделение по полям — Разные части документа индексируются отдельно. Заголовок, тело, теги и путь получают свои поля в индексе. Это позволяет бустинг полей при запросе.

Результирующий индекс — это структура данных, называемая обратным индексом — карта от каждого термина к списку документов, содержащих его, вместе с позиционной информацией (где в документе появляется термин и как часто).

"instal"     → [doc_3 (title, pos:0), doc_7 (body, pos:45), doc_12 (body, pos:102)]
"deploy"     → [doc_3 (body, pos:23), doc_5 (title, pos:0)]
"kubernetes" → [doc_5 (body, pos:15), doc_5 (body, pos:89)]

Запрос: что происходит при поиске

Когда пользователь вводит запрос, поисковый движок пропускает текст запроса через тот же аналитический пайплайн (токенизация, приведение к нижнему регистру, стемминг), затем ищет каждый термин в обратном индексе.

Но реальная ценность — в ранжировании. Не все совпадения равны. Bleve использует вариант TF-IDF (частота термина, обратная частота документа) в сочетании с бустингом полей для вычисления оценки релевантности:

  • Частота термина: Страница, упоминающая «deployment» 10 раз, вероятно, более релевантна, чем та, что упоминает его один раз.
  • Обратная частота документа: Термин, встречающийся только в 3 из 500 документов, более отличительный (и более полезный для ранжирования), чем термин, встречающийся в 400 документах.
  • Бустинг полей: Совпадение в заголовке стоит больше, чем совпадение в теле. В DocPlatform совпадения в заголовке получают 3-кратный буст, совпадения в тегах — 2-кратный, совпадения в теле — 1-кратный.

Формула выдаёт числовую оценку для каждого совпавшего документа, и результаты возвращаются отсортированными по оценке.

Нечёткий поиск: обработка опечаток

Реальные пользователи делают опечатки. «Kuberntes» вместо «Kubernetes». «Authentcation» вместо «Authentication». Базовый поиск ничего не возвращает для таких запросов.

Bleve поддерживает нечёткий поиск с использованием расстояния редактирования (расстояния Левенштейна). Термин запроса совпадает с термином документа, если они отличаются на N или менее символьных операций (вставка, удаление, замена). DocPlatform использует расстояние редактирования 1 по умолчанию — достаточно для отлова одиночных опечаток без слишком большого числа ложных срабатываний.

Запрос: "authentcation"
Расстояние редактирования 1: совпадает с "authentication" (одна пропущенная 'i')
Результаты: все документы, содержащие "authentication"

Это происходит прозрачно. Пользователям не нужно знать о нечётком поиске или что-то настраивать. Они просто ищут и получают результаты, даже если ошиблись при наборе.

Как DocPlatform поддерживает индекс в синхронизации

Сложная часть встроенного поиска — не сам поиск, а поддержание согласованности индекса с контентом. DocPlatform имеет два источника контента: веб-редактор и git. Оба могут создавать, обновлять и удалять страницы.

Вот поток индексирования:

Сохранение из веб-редактора: Пользователь нажимает «сохранить» → контент записывается в базу данных → поисковый индексатор обновляет индекс Bleve → движок git-синхронизации коммитит изменение.

Получение git push: Срабатывает git webhook → движок синхронизации забирает изменения → новые/изменённые страницы записываются в базу данных → поисковый индексатор обновляет индекс Bleve.

Массовые операции: При импорте репозитория с сотнями markdown-файлов индексатор обрабатывает их пакетами. Импорт 500 страниц занимает около 3 секунд на скромном оборудовании.

Удаления: При удалении страницы (из веб-интерфейса или git) соответствующий документ удаляется из индекса Bleve. Никаких осиротевших результатов поиска.

Важная деталь: индексирование синхронно с операцией записи. Когда сохранение/синхронизация завершается, поисковый индекс уже обновлён. Нет задержки «ожидайте переиндексации». Это возможно, потому что Bleve работает в том же процессе — нет сетевого хопа к отдельному кластеру Elasticsearch.

Подробнее о работе движка синхронизации — в нашей статье о том, почему git-синхронизация ломается, и паттерне Content Ledger, который решает эту проблему.

Сравнение: встроенный vs. внешний поиск

Возможность SQL LIKE SQLite FTS5 Bleve (встроенный) Elasticsearch
Токенизация Нет Да Да Да
Стемминг Нет Ограниченный Да Да
Нечёткий поиск Нет Нет Да Да
Бустинг полей Нет Нет Да Да
Ранжирование релевантности Нет Базовое TF-IDF BM25
Дополнительный сервис Нет Нет Нет Да
Накладные расходы памяти Нет ~1 МБ ~10 МБ 1-4 ГБ (JVM)
Сложность эксплуатации Нет Нет Нет Высокая

Elasticsearch выигрывает по продвинутым функциям: агрегации, запросы к вложенным документам, кастомные анализаторы, распределённый поиск по кластерам. Если вы создаёте поисковый продукт, он вам, вероятно, нужен.

Но для поиска по документации — где корпус составляет тысячи страниц, а не миллионы записей — встроенный Bleve покрывает требования с нулевыми операционными накладными расходами. Вам не нужен отдельный кластер для поиска по документации.

Как мы рассказали в статье об архитектуре одного бинарного файла, размещение всего в одном процессе устраняет целый класс операционных проблем.

Что пользователи реально ищут

Мы настроили конфигурацию поиска под то, как люди реально ищут в документации:

Точные названия функций: «RBAC», «WebAuthn», «git sync» — они должны точно совпадать в заголовках и тегах.

Концептуальные запросы: «как настроить права доступа» — они опираются на стемминг и поиск по тексту.

Частичное воспоминание: «та страница про деплой…» — нечёткий поиск и бустинг заголовков помогают найти правильный результат даже при неполном запросе.

Сообщения об ошибках: Пользователи вставляют сообщения об ошибках в поиск. Они выигрывают от точного совпадения длинных последовательностей токенов.

Конфигурация по умолчанию хорошо справляется со всем этим. Никакой настройки не требуется.

Попробуйте

Если хотите увидеть это в действии, установите DocPlatform и создайте несколько страниц. Строка поиска на опубликованном сайте документации использует именно ту систему, что описана здесь. Введите запрос, намеренно допустите опечатку и наблюдайте, как система всё равно находит нужное.

Community Edition включает полные поисковые возможности — без ограничений функций, без «обновитесь для лучшего поиска». Руководство по настройке поиска содержит детали кастомизации поискового поведения, если вам это нужно.