bool-запросы: must, should, must_not, filter

Учимся собирать сложные запросы из простых условий через bool — главный конструктор запросов Elasticsearch.

bool-запрос комбинирует несколько условий через четыре секции: must, should, must_not и filter.

Зачем

Реальный поиск редко состоит из одного условия. «Ноутбуки бренда Lenovo, дешевле 80000, в наличии, желательно с SSD» — это пять условий разной природы. bool позволяет собрать их в один запрос, как AND/OR/NOT в SQL, но с учётом релевантности.

Четыре секции

СекцияСмыслВлияет на score?
mustобязано совпасть (логическое И)да
shouldжелательно совпасть (повышает score)да
must_notне должно совпасть (логическое НЕ)нет (только исключает)
filterобязано совпасть, но без подсчёта релевантностинет (только фильтрует)

Пример

{
  "query": {
    "bool": {
      "must":     [ { "match": { "title": "ноутбук lenovo" } } ],
      "should":   [ { "match": { "description": "ssd" } } ],
      "must_not": [ { "term":  { "status": "discontinued" } } ],
      "filter":   [
        { "term":  { "in_stock": true } },
        { "range": { "price": { "lt": 80000 } } }
      ]
    }
  }
}

Читается так: документ обязан подходить под «ноутбук lenovo»; если в описании есть «ssd» — поднимется в выдаче; не должен быть со статусом discontinued; обязан быть в наличии и дешевле 80000 — но эти два условия только отсеивают, не влияя на ранжирование.

filter против must — важнейшее различие

И must, и filter делают условие обязательным. Разница в двух вещах:

  • Релевантность. must участвует в подсчёте _score, filter — нет (для него важно только «да/нет»).
  • Кеширование. Результаты filter кешируются и переиспользуются между запросами. Поэтому точные условия («в наличии», «цена в диапазоне», «дата за месяц») лучше класть в filter — это и быстрее, и логически правильнее.

Как работает под капотом

ES вычисляет каждую секцию как набор подходящих документов (постинг-листы) и комбинирует их: must и filter пересекаются, must_not вычитается, should добавляет очки совпавшим. Для filter движок не считает TF-IDF/BM25 — он только проверяет принадлежность, поэтому может закешировать результат как битовую маску документов и мгновенно переиспользовать на следующих запросах. Это делает фильтры очень дешёвыми.

Частые ошибки

  • Класть точные фильтры в must. «Цена < 80000» и «в наличии» не нужны для релевантности — в filter они быстрее и кешируются.
  • Думать, что should обязателен. Если в bool есть must или filter, should по умолчанию необязателен и лишь повышает score. Поведением управляет параметр minimum_should_match.

Итоги

  • bool собирает запрос из секций: must (И), should (буст), must_not (НЕ), filter (И без score).
  • filter не влияет на релевантность и кешируется — туда кладут точные условия.
  • must и should участвуют в ранжировании, must_not и filter — только отбирают документы.
Проверьте себя
1. Чем filter отличается от must в bool-запросе?
Afilter необязателен, а must обязателен
BОба делают условие обязательным, но filter не влияет на _score и кешируется, а must участвует в релевантности
Cfilter работает только с текстом
DОни полностью идентичны
2. Какая секция bool исключает документы, не влияя на релевантность?
Amust
Bshould
Cmust_not
Dmatch