GH GambleHub

Фильтрация и полнотекстовый поиск

1) Зачем нужен поисковый слой

Фильтрация и полнотекстовый поиск (FTS) обеспечивают быстрый доступ к данным «по смыслу», а не только по первичным ключам. Правильно спроектированный слой поиска сочетает:
  • Строгие фильтры (категории, даты, цены, права доступа)
  • Полнотекст (лексический матч и ранжирование)
  • Фасеты (агрегаты для навигации)
  • Гибридное ранжирование (BM25/TF-IDF + векторные эмбеддинги)
  • Надежные протоколы (пагинация курсорами, TTL токенов, кросс-шардинг)

2) Архитектурная картина

Компоненты:

1. Ingest/ETL → нормализация, дедупликация, обогащение, построение полей для индекса.

2. Индексатор → обратный индекс (лексемы → документы), колоночные структуры, векторный индекс (HNSW/IVF-PQ).

3. Query Layer → парсер запросов, применение фильтров/прав доступа, планировщик шардов, k-way merge.

4. Ranker → BM25 + (опц.) LTR/Neural re-rank.

5. Serving → кэш, курсоры, фасеты, хайлайты, автокомплит.

6. Наблюдаемость → метрики латентности, качества, A/B-эксперименты.

3) Модель данных и индекса

3.1 Поля и анализаторы

Типы: keyword (ровное совпадение), text (анализируется), numeric/date/geo, vector.
Анализаторы: токенизация, нормализация (lowercase, Unicode NFKC), фильтры (стоп-слова, стемминг/лемматизация).
Мультиязычность: per-field анализаторы (ru, uk, en); ICU-анализ; транслитерация; учет диакритики.

3.2 Обратный индекс (sparse)

Структура: term → posting list (docID, term freq, позиции).
Ранжирование: BM25 (или классический TF-IDF) с полевыми бустами.

3.3 Векторный индекс (dense)

Эмбеддинги текста (например, 384–1024-мерные).
ANN-структуры: HNSW, IVF-PQ, Flat (для малых наборов).
Косинусная близость/inner product; калибровка с BM25 (гибрид).

3.4 Фасеты и агрегаты

Прекомпьют/колоночное хранение значений для быстрых count-ов.
Иерархические фасеты (категория/подкатегория).
Диапазоны (ценовые бины, даты).

4) Запросы: фильтры + полнотекст + сортировка

4.1 Контракты API (REST)

Запрос:

GET /v1/search? q = classic slots & limit = 20 & cursor =... & sort = score: desc, created _ at: desc
&filters=brand:("NetEnt","EGT"); price:[10 TO 50];published_at:[2024-01-01 TO ]
&facets=brand,year,price:range(0,10,20,50,100)
Ответ (фрагмент):
json
{
"items": [ { "id":"...", "title":"...", "score": 12. 3, "highlight": { "content": ["..."] } } ],
"facets": { "brand": [{"value":"NetEnt","count":123},...] },
"page": { "limit":20, "has_more":true, "next_cursor":"opaque-token" }
}

4.2 GraphQL (упрощенно)

graphql type Query {
search(query: String!, filter: SearchFilter, first: Int, after: String, sort: [Sort!]): SearchConnection!
}

4.3 gRPC

proto message SearchRequest {
string query = 1;
map<string,string> filters = 2;
int32 page_size = 3;
string page_token = 4; // курсор repeated string facets = 5;
}

5) Обработка естественного языка (NLP)

Токенизация/нормализация: Unicode-безопасно, учет дефисов/апострофов.
Стоп-слова: настроечные списки по языкам.
Стемминг vs лемматизация: для ru/uk лучше лемматизация (качество > скорость).
Синонимы: двунаправленные/направленные словари; версии словарей с TTL.
Опечатки (fuzzy): Damerau-Levenshtein с ограничением расстояния и бустами точного совпадения.
N-грамы/edge-ngrams: для автокомплита и подсказок.
Транслитерация: «shch» ↔ «щ», «kiev/kyiv» — правила соответствий.

6) Релевантность и ранжирование

6.1 Базовый лексический скоринг

BM25 с настройкой `k1`, `b` по коллекции.
Бусты по полям (title^3, tags^1.5, body^1).
Свежесть: `score += freshness_boost(decay(created_at))`.

6.2 Поведенческие сигналы

Click-through rate, dwell time, сохранения в избранное (с анти-позиционным байасом).
Дедупликация: склейка документов с ~идентичным содержимым (MinHash/SimHash).

6.3 Learning-to-Rank (LTR)

Фичи: BM25 по полям, длина, свежесть, популярность, совпадение по фразе, позиционный скор.
Модели: LambdaMART/XGBoost; офлайн-метрики NDCG@k, MAP, Precision@k; онлайн A/B.

6.4 Нейро-переранжирование

Двухэтапка: recall (BM25/ANN) → top-N (например, 200) → cross-encoder rerank.
Учет стоимости: тайм-бюджет, fallback без нейро-этапа при нагрузке.

6.5 Гибридный поиск (sparse + dense)

Либо fusion (нормализация скоров и сумма), либо multi-stage (dense как rerank).
Важна калибровка: min-max/ z-score/ quantile mapping.

7) Фильтрация, фасеты и доступ

7.1 Фильтры

Операторы: `=`, `IN`, диапазоны, префиксы, geo-bounding box/geo-distance.
Комбинации: `AND` по фильтрам, `OR` внутри множества значений (brand IN …).
Типобезопасность: числовые поля не анализируются как текст.

7.2 Фасеты

Дешевые count-ы по предвычисленным структурам.
«Примененные» фасеты показывают оставшиеся варианты (post-filter faceting).

7.3 Доступ/мульти-тенантность

Фильтры безопасности интегрируются до ранжирования (pre-filter).
ABAC/RBAC поля в документе (`tenant_id`, `visibility`, `acl`).
Токен запроса подписан; при мульти-тенанте — автоматический `tenant_id` фильтр.

8) Пагинация, курсоры и консистентность

Пагинация seek-курсором по `(score, tie-breaker)` или по `(created_at, id)` при сортировке по времени.
Непрозрачные `page_token` c HMAC и TTL.
Консистентность: near-real-time (NRT) индекса: задержка 0.5–2 с между записью и видимостью. Документируйте это в SLA.
Кросс-шард: локальный поиск → k-way merge по глобальному порядку, per-shard курсоры в токене.

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

Suggesters: prefix-trie / edge-ngrams по полю `title`.
Popular queries: лог кликов → подсказки по популярности + персонализация (сегменты).
Spell-as-you-type: быстрый fuzzy-поиск с ограничением расстояния `<=1`.

Пример REST:

GET /v1/suggest? q=kaz&limit=8&locale=ru
→ ["casino," "casual games,..."]

10) Хайлайты и сниппеты

Позиционный индекс → извлечение фраз с совпадениями.
Экранирование HTML, лимит длины, объединение соседних фрагментов.
Ранжирование сниппетов по плотности релевантных терминов.

11) Производительность, кэш и SLO

Индексы: горячие сегменты в памяти; компрессия postings; doc values для фасетов.
Кэш: L1 (процесс), L2 (Redis), кэш фасетов/агрегатов; инвалидировать по версии индекса.
SLO: P95 < 150–200 мс при `k<=20`, P99 < 500 мс; доступность 99.9%.
Backpressure: уменьшение `k`, отключение нейро-этапа при перегрузе.
Rate limiting на ключ API/пользователя/тенанта.

12) Наблюдаемость и метрики качества

Техметрики:
  • `search_latency_ms` (P50/P95/P99), `qps`, `timeouts`, `error_rate`
  • `cache_hit_ratio`, `facet_cache_hit`, `rerank_share`
  • `shard_fanout`, `merge_time_ms`, `ann_recall@k`
Качество (оффлайн):
  • NDCG@k, MAP, MRR, Recall@k, Precision@k на размеченных выборках.
Онлайн:
  • CTR@k, sCTR (satisfied clicks), dwell time, отказ (pogostick rate).

A/B: фиксируйте «guardrail» метрики (латентность, ошибки) + целевые (NDCG proxy).

13) Тестирование

Relevance unit tests: проверка ожидаемых матчей по ключевым запросам.
Property-based: устойчивость к опечаткам/синонимам/языкам.
Пагинация: отсутствие дубликатов на границе страниц (seek-контракты).
Безопасность: фильтры доступа применяются всегда (даже на faset-count).
Регрессы словарей: версионирование синонимов и fuzzy-правил.

14) Безопасность и приватность

Поля с PII не индексировать как text; хранить отдельно/шифровать.
Минимизировать хранимые исходные тексты (store=false, только поля для сниппетов).
Query privacy: не логировать сырые запросы с PII; анонимизация/хэширование.
Мульти-тенант: строгая изоляция индексов или обязательный `tenant_id` фильтр.

15) Миграции и совместимость

Версионирование схемы индекса (v1→v2) с двойной записью и постепенным переключением.
Совместимость анализаторов: держать старые цепочки пока не переиндексируете.
Ротация словарей синонимов/стоп-слов: `version`, `activated_at`, rollback.

16) Практические рецепты

16.1 Классический лексический поиск (BM25)

Поля: `title^3`, `tags^2`, `body^1`.
Анализаторы: язык-специфичные + лемматизация.
Fuzzy для коротких запросов (`<=3` токенов), `fuzziness=1`.

16.2 Гибрид sparse+dense

1. ANN-поиск по эмбеддингу запроса (k=200)

2. Объединить с top-200 BM25

3. Калибровать и слить (Reciprocal Rank Fusion)

4. Взять top-N (N=20), опционально — rerank cross-encoder-ом при достаточном бюджете.

16.3 Фасетная навигация каталога

Жесткий pre-filter по правам/тенанту

Пост-фильтровые фасеты (counts с учетом активных фильтров)

Сортировка: по релевантности или по бизнес-полю (цена/новизна)

17) Примеры запросов (псевдо-DSL)

Фильтры и сортировка:
json
{
"query": "live casino,"
"filters": {
"country": ["EE","LV","LT"],
"license": ["MGA","UKGC"],
"launched_at": {"gte": "2023-01-01"}
},
"sort": ["_score:desc","launched_at:desc"],
"facets": ["country","license"],
"page": {"limit": 20, "cursor": "opaque"}
}
Геопоиск:
json
{
"query": "casino",
"geo": {"lat": 59. 437, "lon": 24. 753, "radius_km": 50}
}
Автокомплит:
json
{ "prefix": "evo", "field": "brand_suggest", "limit": 8 }

18) UX-паттерны

Чипы активных фильтров + «сбросить все».
Пустые результаты: показать «попробуйте…» (синонимы, убрать фильтр).
«Подсказки при нуле»: популярные запросы/категории.
Пагинация курсором (кнопка «Еще») и бесконечный скролл; фиксированный индикатор примененных фильтров.
Отдельные переключатели «учитывать опечатки», «точное совпадение фразы».

19) Частые ошибки и анти-паттерны

Отсутствие tie-breaker при сортировке → дубли/скачки.
Фасеты без учета активных фильтров → «ложные» counts.
Применение фильтров доступа после ранжирования.
Смешивание разных языков одним анализатором.
Глубокая пагинация OFFSET/LIMIT вместо seek-курсора.
Неограниченный fuzzy → взрыв по латентности.

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

1. Определите поля и их типы, назначьте анализаторы per-locale.
2. Спроектируйте обратный индекс + (опц.) векторный ANN.
3. Реализуйте парсер запросов и безопасные фильтры доступа (pre-filter).
4. Настройте BM25 и полевые бусты; подключите фасеты.
5. Введите курсоры (opaque, HMAC, TTL) и k-way merge по шардам.
6. Добавьте автокомплит, хайлайты, безопасное экранирование.
7. Метрики: латентность, NDCG@k, CTR; кэш L1/L2.
8. A/B-фреймворк для тюнинга релевантности.
9. Документируйте SLA: NRT-задержку, лимиты `limit`, гарантию консистентности.
10. План миграций: версии индекса, словарей и анализаторов.

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

Contact

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

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

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

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

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

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