← Все проекты уровня 11
Уровень 11 · Поисковые сервисыВариант C

Поиск по документации

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

Техническое задание

Документы и поиск

Создайте REST-сервис с документами, уникальными по project_id, версии semver и нормализованному пути. latest выбирает максимальную опубликованную стабильную версию semver; prerelease игнорируется. Один запрос ищет только одну версию. PostgreSQL полнотекстовый поиск русским словарём индексирует заголовок и очищенный текст, требует совпадения всех токенов запроса, не учитывает регистр и не исправляет опечатки. Порядок: релевантность по убыванию, путь и ID по возрастанию.

GET /api/v1/projects/{project_id}/versions перечисляет версии. GET /api/v1/projects/{project_id}/search?version=latest&q=&cursor=&limit= выдаёт до 100 страниц, limit по умолчанию 20, q до 200 символов. Фрагмент — один plain-text fragment до 240 Unicode code points, найденные токены выделяются безопасным HTML escaping; script и style удаляются до индексации. Admin POST/PUT /api/v1/admin/projects/{project_id}/documents создаёт или меняет, DELETE /api/v1/admin/projects/{project_id}/documents/{version}/{path} удаляет. Seed создаёт docs_admin с токеном в Authorization header для этих маршрутов. Тело ограничено 1 MiB. Пустой q, invalid semver, limit или cursor дают 400; неизвестный project/version — 404.

Используйте Go, PostgreSQL, ручной SQL, миграции и GIN tsvector индекс. Схема хранит только очищенный plain text, исходный HTML не возвращается и не сохраняется. Изменение и событие в outbox фиксируются одной транзакцией; индексатор применяет не более 200 событий в секунду, подтверждает контрольную позицию после применения, а очередь ограничена 100 000. При переполнении изменение через admin API возвращает 503 до записи. Поиск сообщает index_lag_seconds, приемочная цель — не более 60 секунд. Изменение документа не должно требовать перестройки всего корпуса: обработчик применяет версии изменений документа по одной и повторная доставка той же операции не меняет итог. Удаление создаёт устойчивое состояние tombstone или эквивалент, чтобы позднее старое событие не воскресило документ. Если применяется журнал событий, храните подтверждённую позицию и диагностируйте отставание по числу или времени; при сбое обработка продолжится после перезапуска. Запросы к БД используют context.Context с deadline, а сервер ограничивает тело запроса и частоту индексирования согласно локальной конфигурации.

Структура программы

HTTP-слой обрабатывает версии и фрагменты; предметный пакет определяет поиск и очистку текста; SQL-пакет хранит документы и индекс, отдельный цикл применяет outbox. main.go только загружает конфигурацию, связывает зависимости и управляет запуском. Пакеты без циклов, общих utils и интерфейсов без потребителя. README показывает зависимости; тесты ядра без HTTP/БД, интеграционные отдельно.

Приёмка включает несколько проектов и версий, повторный путь в разных версиях, HTML с тегами script и style, спецсимволы, русский текст, замену и удаление. Офлайн-тесты подтверждают выбор версии, релевантность и стабильный курсор, пределы фрагмента, отсутствие исходной разметки, инкрементальное обновление, tombstone, повторы событий и восстановление индексатора. Подготовьте набор из 10 000 документов, команду, конфигурацию и фактический p95 без общего обещания SLA. Критерии готовности: запрос не смешивает версии; tombstone скрывает удалённый путь после контрольной позиции; старое событие не воскрешает страницу; lag видим и до 60 секунд в приёмочном запуске; изменение одной страницы не перестраивает весь индекс.

Критерии готовности

Ожидаемый результат

Проверяемый результат

  • README показывает границы и направление зависимостей; main.go только связывает конфигурацию и запуск; модульные тесты ядра обходятся без HTTP и БД, интеграционные тесты отделены.
  • Поиск принимает проект, конкретную semver-версию или latest; одна выдача не смешивает версии.
  • latest выбирает максимальную stable-версию, а список версий показывает только опубликованные значения.
  • Каждая карточка содержит короткий очищенный фрагмент текста длиной до 240 символов, не исходный HTML.
  • Повторное обновление и удаление идемпотентны, старое событие не возвращает tombstone в выдачу.
  • После рестарта индексатор продолжает с сохранённой контрольной позиции и сообщает задержку.
  • Набор 10 000 документов сопровождается повторяемой командой, конфигурацией и фактическим p95.
  • Ошибки и лимиты описаны, чистые миграции и локальный запуск не требуют доступа в сеть.
  • Отчёт содержит номер ревизии корпуса и одинаковый запросный набор для повторного сравнения p95.