> На чем реализовывали поиск (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):
GOquery := 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.
- Игнорирование морфологии русского языка - если сказал "просто стандартный анализатор", значит, не сталкивался с реальными данными.
> Похожие задачи по Go
С какими паттернами проектирования приходилось работать в Go?
Готов ли ты писать код сейчас?
Как происходит балансировка нагрузки в планировщике Go
Что такое starvation локальной очереди и когда она возникает
> Похожие задачи по backend
С какими паттернами проектирования приходилось работать в Go?
Готов ли ты писать код сейчас?
Как происходит балансировка нагрузки в планировщике Go
Что такое starvation локальной очереди и когда она возникает
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью