> На чем реализовывали поиск (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: YADRO

Стек: Go

> Пример ответа

Короткий ответ

Поиск реализовывали на основе полнотекстового индекса Elasticsearch (OpenSearch) с синхронизацией данных из PostgreSQL через CDC-подход на Debezium. Для простых фильтров и автокомплита использовали частично PostgreSQL с триграммным индексом (pg_trgm), но основная нагрузка по поиску лежала на Elasticsearch. В качестве транспортного слоя - REST API с пагинацией на основе search_after, без глубоких offset-запросов.

Подробное объяснение

Выбор Elasticsearch был обусловлен требованиями: полнотекстовый поиск с морфологией русского языка, фасетные фильтры, сортировка по релевантности и скорость ответа < 300 мс при индексе ~50 млн документов. PostgreSQL использовали как систему записи (source of truth), Elasticsearch - как read model.

Архитектура:

  • Основной индекс - документы с denormalized полями (название, описание, теги, цена, рейтинг).
  • Анализ текста - стандартный анализатор с кастомным фильтром для русской морфологии (lemmatization через Elasticsearch analysis-lemmagen или snowball).
  • Синхронизация - Debezium слушает WAL PostgreSQL и публикует события в Kafka, консьюмер обновляет индекс. Это давало near-real-time задержку ~1-2 секунды.
  • Для автокомплита использовали completion suggester, для опечаток - fuzzy match с edit distance = 1.
  • Сортировка по умолчанию - комбинация _score и recency decay function (свежие объекты получали небольшой буст).

Альтернативы, которые рассматривали:

  • PostgreSQL full-text search (tsvector) - не тянул по производительности на объёме и сложных запросах с фасетами.
  • ClickHouse - хорош для аналитики, но слаб в полнотекстовом поиске и релевантности.
  • Механический поиск в памяти (map + regex) - только для справочников до 100k записей.

На практике

Основные сценарии:

  • Поиск по названию и описанию с поддержкой словоформ ("платье" находило "платья", "платьем").
  • Фильтрация по категории, цене, наличию, рейтингу - через bool query с filter context (без влияния на score).
  • Пагинация через search_after, потому что from+size на глубоких страницах деградирует.
  • Для административных отчётов использовали отдельный агрегационный индекс с меньшей детализацией.

Проблемы, с которыми сталкивались:

  • Расхождение данных между PostgreSQL и Elasticsearch при репликации - решали периодической полной переиндексацией раз в сутки и dead-letter queue для ошибок консьюмера.
  • Тяжёлые запросы с большим количеством should-условий - оптимизировали через constant_score и предварительное вычисление фильтров.
  • Русская морфология - стандартный snowball давал ошибки на редких словах, добавили словарь исключений.

Пример кода

Типичный запрос в Elasticsearch (Go, библиотека olivere/elastic):

GO
query := elastic.NewBoolQuery().
// фильтры - не влияют на релевантность
Filter(
elastic.NewTermQuery("category_id", categoryID),
elastic.NewRangeQuery("price").Gte(minPrice).Lte(maxPrice),
elastic.NewTermQuery("in_stock", true),
).
// полнотекстовый поиск
Must(
elastic.NewMultiMatchQuery(searchText, "title^3", "description").
Type("best_fields").
Fuzziness("AUTO"),
)
searchResult, err := client.Search().
Index("products").
Query(query).
SearchAfter(sortValues...). // пагинация
Sort("_score", false).
Sort("id", true). // стабильный порядок
Size(20).
Do(ctx)

Как отвечать на собеседовании

Начни с краткого ответа: назови стек и причину выбора. Затем опиши архитектуру синхронизации - это показывает понимание distributed systems. Упомяни trade-off: Elasticsearch даёт скорость и релевантность, но добавляет сложность в эксплуатации (кластер, репликация, переиндексация). Если спросят про альтернативы - объясни, почему не подошли PostgreSQL full-text или ClickHouse. Не уходи в детали реализации анализаторов, если не спрашивают. Заверши практическим примером: как решали проблему рассинхронизации или деградации скорости.

Что проверяет интервьюер

  • Понимание разницы между OLTP-хранилищем и поисковым движком.
  • Умение обосновать выбор технологии под требования (объём, latency, морфология).
  • Знание паттернов синхронизации данных (CDC, outbox, двойная запись).
  • Практический опыт с пагинацией, фасетами, релевантностью.
  • Осознание ограничений и способов их обхода (search_after вместо offset, constant_score для фильтров).

Типичные ошибки

  • Утверждение, что "поиск в PostgreSQL через LIKE" - это нормально для продакшена на больших объёмах.
  • Отсутствие упоминания синхронизации - если сказал "использовали Elasticsearch", но не объяснил, как данные туда попадают, это красный флаг.
  • Использование offset-пагинации на глубоких страницах без оговорки о деградации.
  • Смешивание filter и must без понимания влияния на score.
  • Игнорирование морфологии русского языка - если сказал "просто стандартный анализатор", значит, не сталкивался с реальными данными.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью