Перколятор: обратный поиск по сохранённым запросам
Во всех статьях этого раздела поиск работал одинаково: документы лежат в индексе, запрос приходит снаружи, ES возвращает совпавшие документы. Перколятор переворачивает эту схему.
Обычный поиск: вы отправляете запрос → ES ищет по документам → возвращает подходящие.
Перколятор: вы подаёте документ → ES прогоняет его через сохранённые запросы → возвращает совпавшие запросы.
Это не просто другая точка зрения — это другой класс задач. Перколятор нужен там, где нужно «подписаться» на появление нужного контента, а не искать по уже накопленному. Три типичных сценария:
- Алерты: пользователь описывает, что его интересует; при появлении нового документа он получает уведомление.
- Авторазметка: правила-запросы описывают категории; новый документ автоматически получает теги.
- Маршрутизация событий: входящие данные (логи, транзакции) проверяются на совпадение с набором фильтров.
flowchart LR
subgraph NORMAL[Обычный поиск]
Q1[Запрос из приложения] --> IDX1[(Индекс документов)]
IDX1 --> R1[Список документов]
end
subgraph PERC[Перколятор]
D1[Входящий документ] --> IDX2[(Индекс запросов)]
IDX2 --> R2[Список запросов]
endТип поля percolator
Для хранения запросов в ES предусмотрен специальный тип поля — percolator. Когда вы индексируете документ с таким полем, ES компилирует Query DSL-запрос в структуры Lucene и сохраняет их. Именно поэтому маппинг перколятор-индекса должен заранее описывать все поля, на которые ссылаются сохранённые запросы — без схемы ES не сможет скомпилировать запрос.
Создадим индекс для алертов на статьи:
PUT /article-alerts
{
"mappings": {
"properties": {
"alert_query": { "type": "percolator" },
"description": { "type": "text" },
"title": { "type": "text" },
"body": { "type": "text" },
"tags": { "type": "keyword" }
}
}
}description — поле самого алерта (текст для отображения пользователю). title, body, tags — схема документов, которые будут перколироваться. Именно на эти поля будут ссылаться сохранённые запросы — поэтому они обязаны присутствовать в маппинге.
Сохранение запросов в индексе
Каждый алерт — обычный документ. В поле alert_query кладём Query DSL-запрос. Подходит любой запрос, который поддерживает ES: bool, match, term, range и т. д.
PUT /article-alerts/_doc/alert-es
{
"description": "Всё об Elasticsearch",
"alert_query": {
"match": { "title": "elasticsearch" }
}
}
PUT /article-alerts/_doc/alert-vector
{
"description": "Векторный и семантический поиск",
"alert_query": {
"bool": {
"should": [
{ "match": { "body": "kNN" } },
{ "match": { "body": "эмбеддинги" } },
{ "match": { "body": "семантический поиск" } }
],
"minimum_should_match": 1
}
}
}Обратите внимание: запросы в alert_query ссылаются на поля title и body — именно они описаны в маппинге выше. Если попытаться сохранить запрос, ссылающийся на неизвестное поле, ES вернёт ошибку при индексации.
Percolate-запрос: прогоняем документ через индекс
Используем запрос типа percolate. Передаём имя поля-перколятора и сам документ:
GET /article-alerts/_search
{
"query": {
"percolate": {
"field": "alert_query",
"document": {
"title": "Введение в Elasticsearch: векторный поиск и kNN",
"body": "В статье разбираются kNN и эмбеддинги для семантического поиска."
}
}
}
}ES вернёт документы из article-alerts, чьи сохранённые запросы совпали с переданным документом. Здесь сработают оба: alert-es (слово «elasticsearch» в title) и alert-vector (слова «kNN» и «семантический поиск» в body).
Каждый совпавший результат — обычный ES-документ с _id, _score и _source. Вы получаете сразу и ID алерта, и его description. Поле _score отражает, насколько плотно документ совпал с условиями сохранённого запроса.
Подсветка совпавших фрагментов
Стандартный highlight работает с перколятором. Добавьте секцию highlight к запросу:
GET /article-alerts/_search
{
"query": {
"percolate": {
"field": "alert_query",
"document": {
"title": "Введение в Elasticsearch: векторный поиск и kNN",
"body": "В статье разбираются kNN и эмбеддинги для семантического поиска."
}
}
},
"highlight": {
"fields": {
"title": {},
"body": {}
}
}
}Подсветка показывает, какие именно фрагменты перколируемого документа совпали с условиями каждого алерта. В ответе рядом с alert-vector в секции highlight появятся фрагменты с выделенными <em>kNN</em> и <em>эмбеддинги</em>. В уведомлении пользователю можно написать: «Ваша подписка сработала, потому что статья содержит kNN и семантический поиск».
Перколяция уже проиндексированного документа
Если документ уже есть в каком-то индексе — не передавайте его inline, укажите ссылку:
GET /article-alerts/_search
{
"query": {
"percolate": {
"field": "alert_query",
"index": "articles",
"id": "article-42"
}
}
}ES сам вытащит документ из индекса articles по ID article-42 и прогонит его через перколятор.
Паттерн: система подписок
Архитектура алертной системы на перколяторе выглядит так:
1. Пользователь создаёт подписку → вы сохраняете его Query DSL-запрос в /article-alerts.
2. Публикуется новая статья → вы отправляете percolate-запрос с её содержимым.
3. Получаете список совпавших алертов → рассылаете уведомления владельцам подписок.
Перколятор эффективно работает с сотнями тысяч сохранённых запросов — это реальный рабочий объём для современного кластера. Когда пользователь меняет подписку, достаточно сделать PUT с новым телом документа: ES перекомпилирует запрос при переиндексации.
Несколько важных деталей
- Обновление запроса: сделайте
PUT /article-alerts/_doc/<id>с новым телом — ES перекомпилирует запрос. Работает иPOST _updateс частичным патчем поляalert_query. - Несколько документов за раз: используйте параметр
documents(массив объектов) вместоdocument. В ответе появится поле_percolator_document_slot, указывающее, какой именно из переданных документов совпал. - Percolate в filter context: если скор не нужен, оберните в
bool.filter— условие кешируется и не тратит ресурсы на подсчёт_score. - Маппинг — это контракт: добавляете новые поля в перколируемые документы — обновляйте маппинг перколятор-индекса через API
_mapping.
См. также
- Query DSL: структура запроса и контекст query vs filter
- Составные запросы: bool и комбинирование условий
- Полнотекстовые запросы: match, match_phrase, multi_match
- Подсветка, сортировка и пагинация результатов
- Маппинг: явный, динамический и шаблоны индексов