GH GambleHub

Технологии и Инфраструктура → Elasticsearch и полнотекстовый поиск

Elasticsearch и полнотекстовый поиск

1) Роль Elasticsearch

Elasticsearch (ES) — распределенная поисковая и аналитическая система на базе инвертированных индексов и колоночных структур для агрегаций. Она дает:
  • Полнотекст: релевантность (BM25), морфология, fuzzy/typo tolerant.
  • Фасеты и агрегации: быстрые срезы по атрибутам.
  • Гибридный поиск: BM25 + векторные kNN (семантика).
  • Скорость разработки: Query DSL, ingest pipelines, богатая экосистема.

Для iGaming/финтех: поиск игр/провайдеров, промо и правил, быстрореагирующие фасеты (провайдер, волатильность, RTP, язык), поиск по KYC/AML журналам, разбор логов и алертов.


2) Модель данных и маппинги

2.1 Индекс и типы полей

`text` (анализируемое поле) — для полнотекста.
`keyword` — точные значения/агрегации/сортировка.
`date`, `long/double`, `boolean`, `ip`, `geo_point`.
`nested` — массивы объектов с корректной корреляцией полей.
`dense_vector` — векторные представления (эмбеддинги).

2.2 Мультиполевая стратегия

Храните поле в нескольких видах: `name.text` (анализируемое), `name.raw` (keyword), `name.ngram` (для автодополнения).

json
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ru_morph",
"fields": {
"raw": { "type": "keyword", "ignore_above": 256 },
"ngram": { "type": "text", "analyzer": "edge_ngram_2_20" }
}
},
"provider": { "type": "keyword" },
"tags":   { "type": "keyword" },
"rtp":   { "type": "float" },
"released_at": { "type": "date" },
"lang":   { "type": "keyword" },
"embedding": { "type": "dense_vector", "dims": 384, "index": true, "similarity": "cosine" }
}
},
"settings": {
"analysis": {
"filter": {
"ru_stop": { "type": "stop", "stopwords": "_russian_" },
"ru_stemmer": { "type": "stemmer", "language": "russian" },
"syn_ru": { "type": "synonym", "lenient": true, "synonyms": [
"слот,игровой автомат => слот",
"джекпот,суперприз => джекпот"
] }
},
"analyzer": {
"ru_morph": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "ru_stop", "ru_stemmer", "syn_ru"]
},
"edge_ngram_2_20": {
"type": "custom",
"tokenizer": "edge_ngram",
"filter": ["lowercase"],
"char_filter": [],
"tokenizer": "edge_ngram"
}
},
"tokenizer": {
"edge_ngram": { "type": "edge_ngram", "min_gram": 2, "max_gram": 20 }
}
}
}
}

2.3 Nested для фасетов

Атрибуты вида `features: [{name, value}]` оформляйте `nested`, иначе фасеты дадут ложные совпадения.


3) Релевантность: BM25, буст и гибрид

3.1 Классика (BM25)

Комбинируйте поля с весами (title^4, tags^2, description).
Используйте `minimum_should_match` для контроля «шумных» совпадений.

3.2 Векторы (kNN) + BM25 (rerank)

Эмбеддинги (например, 384–768) в `dense_vector`.
Сначала kNN по вектору (top 200–500), затем rescore BM25 + бизнес-бусты (новизна, RTP, лицензия региона).

Пример гибридного запроса:
json
{
"knn": {
"field": "embedding",
"query_vector": [/... /],
"k": 400, "num_candidates": 2000
},
"query": {
"bool": {
"should": [
{ "multi_match": {
"query": "египетские слоты джекпот",
"fields": ["title^4","tags^2","description"],
"type": "best_fields",
"minimum_should_match": "60%"
}}
],
"filter": [
{ "term": { "region": "TR" }},
{ "range": { "rtp": { "gte": 94.0 }}}
]
}
},
"rescore": {
"window_size": 400,
"query": {
"rescore_query": {
"function_score": {
"query": { "match_all": {} },
"boost_mode": "sum",
"functions": [
{ "gauss": { "released_at": { "scale": "180d", "offset": "30d", "decay": 0.5 } } },
{ "field_value_factor": { "field": "popularity", "factor": 0.2, "modifier": "log1p" } }
]
}
}
}
},
"highlight": { "fields": { "title": {}, "description": {} } }
}

4) Автодополнение и подсказки

Подходы:
  • Edge N-gram на подполе `title.ngram` (быстро, просто).
  • Completion suggesters (`completion` поле) — быстрые подсказки, но отдельный путь индексации.
  • Search-as-you-type — объединяет токенизацию для начала слова и словосочетаний.
Пример подсказок:
json
{ "suggest": { "game-suggest": { "prefix": "book o", "completion": { "field": "title_suggest", "fuzzy": { "fuzziness": 1 }}}}}

5) Синонимы, опечатки и мультиязычие

Синонимы: загружайте файл/список через `synonym` фильтр; разделяйте домены (казино/спорт).
Опечатки: `fuzziness: AUTO` в `multi_match`, ограничивайте длиной и полями. Для подсказок — `fuzzy` режиме completion.

Мультиязычие:
  • Индекс-per-локаль (ru/en/tr/pt-BR) или многоанализаторная схема: `title_ru`, `title_en`.
  • Разные analyzers: `russian`, `english`, `turkish`, `portuguese`.
  • Перекладывайте язык в ключ маршрутизации (routing), чтобы держать «горячие» локали ближе к пользователю.

6) Фильтры, фасеты и агрегации

Для фасетов используйте `keyword` и `nested` агрегации.
Избегайте кардинальных полей (уникальные ID) в агрегациях — вынесите в `runtime fields` или предварительные витрины.

Пример фасетов:
json
{
"size": 20,
"aggs": {
"by_provider": { "terms": { "field": "provider", "size": 20 } },
"by_volatility": { "terms": { "field": "volatility" } },
"rtp_hist": { "histogram": { "field": "rtp", "interval": 1 } }
}
}

7) Ввод данных и очистка текста

Ingest pipelines: нормализация, извлечение полей, гео-энкодинг, удаление HTML.
Attachment/ingest-ocr (по необходимости): индексация PDF/изображений (внимательно к PII).
Лемматизация: через анализаторы или внешние пайплайны (precompute токены).


8) Шарды, реплики и ILM

8.1 Размеры и шардинг

Меньше шардов — лучше. Цель: 10–50 ГБ на шард для смешанных нагрузок.
Начните с `number_of_shards: 1–3`, масштабируйте по факту. Реплики — минимум 1 в проде.

8.2 ILM (Lifecycle)

hot → warm → cold → delete для логов/истории промо.
Сжатие (force merge) для «холодных» сегментов.
Для каталогов и поиска по продукту — «бессрочный» hot с периодической оптимизацией.

8.3 Алгоритм миграций без даунтайма

Новый индекс `games_v2` → alias `games` переключается после `reindex` и backfill. Deprecated поля — убирайте постепенно.


9) Снапшоты, DR и обновления

Snapshots в объектное хранилище (S3/GCS), расписание и проверка восстановления.
Роллинг-обновления нод, проверка shard allocation awareness (по зонам).
Планы DR: кросс-регион репликация (CCR) для критичных индексов (справочники, каталоги).


10) Безопасность и PII

TLS/mTLS между клиентом и кластером.
RBAC: роли на индекс/операции; Dev/Stage/Prod — раздельно.
PII/PCI: не индексируйте поля с персональными данными без необходимости; используйте ingest-маскирование.
Right to be forgotten: храните ссылки на документы для удаления по user_id; soft-delete + reindex/анонализация.


11) Наблюдаемость и SLO поиска

Метрики:
  • P50/P95/P99 latency на query, ошибки 4xx/5xx.
  • Cache hit (query cache / shard request cache).
  • Heap usage, GC паузы, segment merges, threadpools (search/write).
  • Hot shards/hot nodes, rejections.
  • KNN: `graph_hits`, `search_k`, latency, recall@k.
SLO-примеры:
  • Поиск игр: P95 ≤ 200 мс, ошибок < 0.5% в 30-мин окне.
  • Подсказки: P95 ≤ 80 мс.
  • KNN гибрид: P95 ≤ 350 мс для top-20 результатов.

12) FinOps: стоимость и производительность

Размер индекса: экономьте токенизацией, отключайте ненужные `fielddata`, используйте `doc_values` только там, где надо.
Сегменты: планируйте merge-политику, не допускайте «дробления».
KNN дороже по RAM/CPU: ограничьте dims, `num_candidates`, pre-filter на BM25.
Горячие поля в RAM: мониторьте field data/heap; уводите «тяжелые» агрегации в отдельные индексы.


13) Примеры запросов

13.1 Мультиполевая полнотекстовая с бустом

json
{
"query": {
"multi_match": {
"query": "book of",
"fields": ["title^4","title.ngram^2","tags^2","description"]
}
},
"sort": ["_score", { "released_at": "desc" }]
}

13.2 Фильтры + фасеты

json
{
"query": {
"bool": {
"must": [{ "match": { "title": "египет" }}],
"filter": [
{ "terms": { "provider": ["Novomatic","PragmaticPlay"]}},
{ "range": { "rtp": { "gte": 95 }}}
]
}
},
"aggs": {
"by_provider": { "terms": { "field": "provider" } },
"by_year": { "date_histogram": { "field": "released_at", "calendar_interval": "year" } }
}
}

13.3 Nested фильтрация атрибутов

json
{
"query": {
"nested": {
"path": "features",
"query": { "bool": {
"must": [
{ "term": { "features.name": "volatility" }},
{ "term": { "features.value": "high" }}
]
}}
}
}
}

13.4 Поиск по логам (ECS) с хайлайтом

json
{
"query": {
"bool": {
"must": [{ "match_phrase": { "message": "payment declined" }}],
"filter": [
{ "term": { "service.name": "payments" }},
{ "range": { "@timestamp": { "gte": "now-1h" }}}
]
}
},
"highlight": { "fields": { "message": { "number_of_fragments": 0 } } }
}

14) Мульти-тенант и изоляция

Индекс на тенанта (лучше) или поле `tenant_id` + фильтр ACL (дороже на агрегациях).
Routing по `tenant_id` для локализации шардов.
Ограничивайте запросы тенанта лимитами/таймаутами, `query.phase` guard-rails.


15) Чек-лист внедрения

1. Схема: `text/keyword/nested` + мультиполя, `dense_vector` при необходимости.
2. Анализаторы per-язык, синонимы, edge-ngram для автодополнения.
3. Релевантность: BM25 бусты + гибрид kNN→rescore.
4. Фасеты: keyword/nested, агрегации только по «здоровым» полям.
5. Индексация: ingest pipelines (нормализация), батч-загрузка.
6. Шардирование: начните с малого, alias для переездов, ILM для «длинных» логов.
7. DR: snapshots расписание, проверка восстановления, CCR для критичных индексов.
8. Безопасность: TLS, RBAC, маскирование PII, политика удаления.
9. Наблюдаемость: latency, heap/GC, cache hit, hot shards, rejections.
10. FinOps: размер индекса, kNN параметризация, отключение лишних `doc_values/fielddata`.


16) Анти-паттерны

Один индекс «на все»: разные домены (каталог, логи, транзакции) требуют разных настроек.
Бездумное `fuzziness: AUTO` на всех полях → медленно и шумно.
Синонимы «съедают смысл»: не разделять домены словарей.
Без nested там, где нужны связки полей → ложные фасеты.
Слишком много шардов (по одному на документ) — накладные расходы на cluster state.
Неиспользование alias при миграциях — простои и «битые» ссылки.
Индексация PII «как есть» — регуляторные риски и дорогие реиндексы.


17) Контекст iGaming/финтех: быстрые рецепты

Поиск игр: `multi_match` с бустом `title^4`, `tags^2`, фасеты по провайдеру/волатильности, фильтры по региону/валюте, гибрид с векторами для «тематики» (например, «египет», «fruit classic»).
Промо/бонусы: синонимы («фриспины», «free spins»), дата-фильтры `active_from/active_to`, подсказки через completion.
KYC/AML журналы: ECS-схема, полнотекст по `message`, агрегации по `rule_name`, `country`, аномалии по `@timestamp` гистограмме.
Справочник провайдеров: keyword поля для фасетов и сортировок; текстовые описания — `text` с морфологией.
Регуляторные страницы: мультиязычные поля, `search_as_you_type` для мягких подсказок.


Итог

Эффективный поиск на Elasticsearch — это не только «включить BM25»: это правильные анализаторы и маппинги, мультиполя и nested, гибрид BM25+векторы, аккуратные фасеты и агрегации, дисциплина шардирования и ILM, четкие SLO и наблюдаемость, а также безопасность и FinOps. С этими принципами ваш поиск будет быстрым, релевантным и предсказуемым — и выдержит пики трафика продуктовой платформы.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.