# Hybrid search in Manticore Search

Combine full-text and vector search in Manticore using RRF to get more precise results than either method alone

Поиск редко сводится к одному универсальному сценарию. Пользователь, вводящий "cheap running shoes", хочет точных совпадений по ключевым словам, а пользователь, задающий "comfortable footwear for jogging", выражает то же намерение другими словами. Традиционный полнотекстовый поиск хорошо справляется с первым случаем. Векторный поиск решает второй. Гибридный поиск объединяет оба в одном запросе, так что вам не приходится выбирать.

В современных поисковых системах это часто описывается как комбинирование **лексического (разреженного) поиска** с **семантическим (плотным) поиском**. Разные термины, одна идея: точное совпадение плюс смысл.

## Что такое гибридный поиск?

Гибридный поиск одновременно выполняет полнотекстовый (BM25) поиск и векторный (KNN) поиск, а затем объединяет два списка результатов в один. Документы, получившие высокий балл по любому из сигналов (или по обоим), поднимаются наверх.

Полнотекстовый поиск отлично работает с точными ключевыми словами, редкими терминами и идентификаторами. Векторный поиск понимает смысл — например, что "automobile" и "car" обозначают одну и ту же концепцию, — потому что их эмбеддинги находятся рядом в векторном пространстве.

У каждого метода есть свои слабые места:
- Полнотекстовый затрудняется с синонимами и естественным языком
- Векторный поиск затрудняется с точными токенами, такими как SKU, коды ошибок и идентификаторы

Гибридный поиск охватывает оба.

## Как гибридный поиск вписывается в современные поисковые приложения

Гибридный поиск — это **этап извлечения**: часть системы, которая находит релевантных кандидатов в ваших данных.

Вместо того чтобы полагаться на один метод, гибридный поиск комбинирует совпадения по ключевым словам и семантическое сходство, чтобы сразу получать более сильный набор результатов.

На практике это означает:
- Лучшее покрытие (recall) для запросов на естественном языке
- Точное совпадение для идентификаторов, таких как SKU или коды ошибок
- Более релевантные результаты без сложной логики запросов

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

## Когда следует использовать гибридный поиск?

Гибридный поиск подходит, когда:

- Ваши запросы сочетают намерение и детали. Поиск вроде `python error 403 forbidden` выигрывает от точности по ключевому слову в коде ошибки и семантического понимания описания проблемы.
- Вы создаёте конвейер RAG. Retrieval-Augmented Generation требует подачи самых релевантных фрагментов в LLM. Гибридное извлечение постоянно находит более релевантные документы, чем любой из методов по отдельности.
- Ваш каталог содержит как структурированные, так и неструктурированные данные. Товары электронной коммерции имеют точные названия и номера моделей (территория ключевых слов), но также описания, где смысл важнее точных формулировок.
- Вы не можете предсказать, как пользователи будут искать. Некоторые вставят точные фразы, другие опишут, что ищут, на естественном языке. Гибридный поиск элегантно справляется с обоими случаями.

## Как это работает

Manticore использует Reciprocal Rank Fusion (RRF) для объединения результатов. Идея проста: вместо попытки сравнивать сырые оценки BM25 с расстояниями KNN, которые живут на совершенно разных шкалах, RRF смотрит на позиции в ранжировании. Документ, занявший #1 в текстовых результатах и #3 в результатах KNN, получает более высокий комбинированный балл, чем документ, появившийся только в одном списке.

Вот простой пример. Предположим, что текстовый поиск и поиск KNN возвращают свои топ‑3:

Результаты текстового поиска:

| Ранг | Документ |
|------|----------|
| 1 | Doc A |
| 2 | Doc B |
| 3 | Doc C |

Результаты поиска KNN:

| Ранг | Документ |
|------|----------|
| 1 | Doc C |
| 2 | Doc A |
| 3 | Doc D |

RRF оценивает каждый документ по формуле `1 / (rank_constant + rank)`. При значении по умолчанию `rank_constant=60`:

| Документ | Вклад текста | Вклад KNN | Балл RRF |
|---|---|---|---|
| Doc A | 1/(60+1) = 0.0164 | 1/(60+2) = 0.0161 | 0.0325 |
| Doc C | 1/(60+3) = 0.0159 | 1/(60+1) = 0.0164 | 0.0323 |
| Doc B | 1/(60+2) = 0.0161 | — | 0.0161 |
| Doc D | — | 1/(60+3) = 0.0159 | 0.0159 |

Doc A занимает первое место, потому что появляется почти вверху обоих списков. Doc C находится рядом по той же причине. Doc B и Doc D присутствуют только в одном списке, поэтому их баллы ниже.

### Почему RRF?

Существует два распространённых способа объединения результатов:

- **Слияние на основе рангов (RRF)** — простое, надёжное, без необходимости нормализовать оценки
- **Слияние на основе оценок** — сначала нормализовать оценки, а затем объединять

Manticore использует RRF, потому что этот метод хорошо работает сразу из коробки и избавляет от проблем с калибровкой оценок.

Внутри гибридный запрос разбивается на независимые подзапросы: один для полнотекстового поиска, один или несколько для KNN. Они выполняются параллельно, а после завершения RRF объединяет их ранжированные списки результатов в один итоговый список.

## Почему нельзя просто использовать один из методов?

Рассмотрим базу знаний поддержки со статьями о разных кодах ошибок — сбоях соединения, проблемах аутентификации и ошибках синхронизации. Пользователь видит ошибку E-5020 на экране и сообщает: `"Не могу подключиться к серверу."`

Векторный поиск понимает симптом, но не код ошибки. KNN по запросу "не могу подключиться к серверу" возвращает:

| # | Заголовок | Расстояние KNN |
|---|----------|----------------|
| 1 | Ошибка E-5030: Ошибка разрешения DNS | 0.572 |
| 2 | Ошибка E-2091: Превышено время загрузки приложения | 0.583 |
| 3 | Ошибка E-5020: Несоответствие SSL‑сертификата | 0.605 |
| 4 | Ошибка E-5010: Сервис недоступен | 0.622 |
| 5 | Ошибка E-4001: Не удалось войти | 0.665 |

Правильная статья (E-5020) оказывается только на позиции #3. KNN ставит выше ошибки DNS и таймаутов, потому что их описания семантически ближе к фразе "не могу подключиться". Реальная проблема — несоответствие SSL‑сертификата — использует совсем другую лексику, поэтому получает более низкий балл.

Можно подумать: просто добавить код ошибки в запрос KNN. Но "E-5020" и "E-5010" — это произвольные идентификаторы без семантического значения, и эмбеддинги воспринимают их как почти одинаковые токены. KNN по запросу "E-5020 не могу подключиться к серверу" действительно поднимает E-5020 на позицию #1, но лишь потому, что добавленный текст меняет семантический контекст — сам код ошибки почти ничего не весит.

Гибридный поиск решает эту проблему, отправляя каждый сигнал туда, где он работает лучше всего: код ошибки — в полнотекстовый поиск, симптом — в KNN.

```sql
SELECT title, hybrid_score()
FROM support_articles
WHERE knn(embedding, 'can not connect to the server')
  AND MATCH('E-5020')
LIMIT 5
OPTION fusion_method='rrf';
```

| # | Title | Hybrid score |
|---|-------|-------------|
| 1 | Error E-5020: SSL Certificate Mismatch | 0.032 |
| 2 | Error E-5030: DNS Resolution Failed | 0.016 |
| 3 | Error E-2091: App Loading Timeout | 0.016 |
| 4 | Error E-5010: Service Unavailable | 0.016 |
| 5 | Error E-4001: Login Failed | 0.015 |

E-5020 поднимается с #3 на #1 и получает почти вдвое более высокий балл, чем остальные. Полнотекстовый поиск воспринимает «E-5020» как точную строку — не похожую на «E-5010», не "почти близкую", а просто другую. KNN при этом гарантирует, что связанные ошибки соединения всё равно остаются ниже для контекста.

Это основная ценность гибридного поиска:

- Идентификаторы → полнотекстовый поиск
- Смысл → векторный поиск

Каждый метод покрывает слепую зону другого.

## С чего начать

Самый простой способ выполнить гибридный поиск — использовать `hybrid_match()`. Если в вашей таблице настроено автоматическое создание эмбеддингов, одна строка делает всё: поиск по тексту, генерацию эмбеддингов, поиск KNN и объединение через RRF.

```sql
SELECT id, hybrid_score()
FROM products
WHERE hybrid_match('running shoes');
```

JSON-эквивалент:

```json
POST /search
{
  "table": "products",
  "hybrid": { "query": "running shoes" }
}
```

Manticore:
- генерирует эмбеддинги
- выполняет оба поиска параллельно
- объединяет результаты

### Полный контроль: явный MATCH + KNN

Если вы хотите передавать собственные векторы или настраивать отдельные подзапросы, используйте явную форму с `MATCH()` и `KNN()` в условии WHERE:

```sql
SELECT id, hybrid_score()
FROM products
WHERE match('running shoes')
  AND knn(embedding, (0.12, 0.45, 0.78, ...))
OPTION fusion_method='rrf';
```

```json
POST /search
{
  "table": "products",
  "knn": {
    "field": "embedding",
    "query_vector": [0.12, 0.45, 0.78, "..."]
  },
  "query": { "match": { "title": "running shoes" } },
  "options": { "fusion_method": "rrf" }
}
```

Каждый результат включает:
- `hybrid_score()` — объединённый балл (используется для сортировки по умолчанию)
- `weight()` — балл BM25
- `knn_dist()` — векторное расстояние

Фильтры по атрибутам (`AND category = 'footwear'`) применяются к обоим подзапросам.

## Настройка

Поведение объединения настраивается тремя опциями:

- `rank_constant` — контролирует, насколько верхние позиции доминируют в объединённом балле. Низкие значения (например, 10) делают позицию #1 значительно более значимой, чем позицию #5. Высокие значения сглаживают кривую. См. [rank_constant](https://manual.manticoresearch.com/Searching/Options#rank_constant).
- `fusion_weights` — позволяет задать разную важность каждому подзапросу. Если релевантность текста важнее, чем векторное сходство, задайте больший вес. См. [fusion_weights](https://manual.manticoresearch.com/Searching/Options#fusion_weights).
- `window_size` — сколько результатов каждый подзапрос получает до объединения. По умолчанию Manticore вычисляет это автоматически из параметров KNN и LIMIT запроса. См. [window_size](https://manual.manticoresearch.com/Searching/Options#window_size).

## Слияние нескольких векторов

Гибридный поиск не ограничивается одним текстовым поиском и одним поиском KNN. Можно объединять несколько векторных поисков — это полезно, когда в данных есть несколько разных семантических измерений. Например, у товара в e-commerce есть текстовое описание и фотография. Пользователь, ищущий «минималистичные белые кроссовки», опирается сразу на оба сигнала: название должно соответствовать стилю, а изображение товара — выглядеть так, как он это представляет. Если закодировать название и изображение в отдельных векторных пространствах, их можно искать одновременно и позволить RRF выводить наверх товары, которые соответствуют всем трём сигналам — ключевым словам, смыслу текста и визуальному сходству:

```sql
SELECT id, hybrid_score()
FROM products
WHERE match('running shoes') AS text
  AND knn(title_vec, (0.12, 0.45, ...)) AS title_sim
  AND knn(image_vec, (0.88, 0.21, ...)) AS image_sim
OPTION fusion_method='rrf',
       fusion_weights=(text=0.5, title_sim=0.3, image_sim=0.2);
```

Все подзапросы выполняются параллельно и объединяются вместе через RRF.

## Заключение

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

Поиск по ключевым словам даёт точность для точных терминов и идентификаторов. Векторный поиск даёт гибкость для естественного языка и смысла. По отдельности у каждого метода есть пробелы. Вместе они стабильно дают лучшие результаты для широкого спектра запросов.

С гибридным поиском в Manticore вам не нужно выбирать между двумя подходами или строить сложную логику запросов для разных случаев. Вы можете запускать оба сигнала параллельно и получать единый, унифицированный набор результатов.

Если ваш поиск должен обрабатывать и точные совпадения, и намерение пользователя, — а это характерно для большинства реальных приложений, — гибридный поиск даёт простой способ повысить релевантность без лишней сложности.
