Операторы исключения в Lucene Query Parser: AND NOT и —

Разбираем синтаксис Lucene и секреты оператора AND NOT. Узнайте, как легко исключать ненужные термины из поиска и избегать частых ошибок.

При работе с поисковыми движками на базе Apache Lucene (Elasticsearch, Solr, OpenSearch и др.) часто возникает необходимость не только найти документы, содержащие определенные слова, но и исключить те, которые содержат нежелательные термины; Для этого в синтаксисе Lucene Query Parser используется оператор AND NOT (или его символьный аналог -).

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

Базовый синтаксис и логика работы

Оператор AND NOT является бинарным оператором. Он требует наличия левого операнда (что ищем) и правого операнда (что исключаем).

Варианты записи:

  • Словарная форма: term1 AND NOT term2
  • Символьная форма (префиксный минус): term1 -term2

Логика: Документ попадает в результат, если он соответствует term1 И НЕ соответствует term2.

Пример: Найти документы про «яблоки», но не про «пироги».
яблоки AND NOT пироги
яблоки -пироги

Приоритет операторов (Operator Precedence)

Это самый критичный момент, который вызывает ошибки. В Lucene Query Parser приоритет операторов следующий (от высшего к низшему):

  1. — Группировка (скобки)
  2. +, - — Обязательное наличие / Исключение (унарные операторы)
  3. NOT, ! — Логическое НЕ
  4. AND, && — Логическое И
  5. OR, || — Логическое ИЛИ

Важный нюанс: AND NOT vs -

Хотя AND NOT семантически эквивалентен AND + NOT, парсер обрабатывает их по-разному из-за приоритета:

  • AND NOT парсится как комбинация AND (низкий приоритет) и NOT (средний приоритет).
  • - (минус) — это унарный оператор запрета (Prohibit) с высшим приоритетом (равным плюсу +).

Пример разницы:

Запрос: a OR b AND NOT c
Парсинг: a OR (b AND (NOT c))
Результат: Документы с 'a' ИЛИ документы с 'b' но без 'c'.

Запрос: a OR b -c
Парсинг: a OR (b -c) -> a OR (+b -c)
Результат: Документы с 'a' ИЛИ документы, которые ОБЯЗАТЕЛЬНО содержат 'b' и НЕ содержат 'c'.

Работа с фразами и полями

Оператор исключения можно применять к точным фразам и конкретным полям.

Исключение фразы (Phrase Query)

Используйте кавычки для термина справа от оператора:

title:"быстрый лис" AND NOT title:"медленный черепаха"

title:"быстрый лис" -"медленный черепаха"

Исключение в конкретном поле

Если левая часть — это запрос по полю content, а исключать нужно по полю status:

content:ошибка AND NOT status:архив

content:ошибка -status:архив

Исключение нескольких терминов/полей сразу

Используйте группировку скобками:

# Исключить документы, где status=deleted ИЛИ status=spam
текст_запроса AND NOT (status:deleted OR status:spam)

текст_запроса -(status:deleted status:spam)

Оператор NOT vs AND NOT vs -

Lucene поддерживает три способа выражения отрицания. Понимание разницы критично:

Синтаксис Тип Семантика (Must / Should / Must Not) Может ли быть единственным условием?
NOT term
!term
Унарный (Prefix) Must Not (Запрет) Нет. Запрос NOT term вернет ошибку или пустой результат (в зависимости от версии/настроек), так как Lucene не может итерировать «все документы кроме…». Нужен положительный термин (Must/Should).
term1 AND NOT term2 Бинарный term1 = Must, term2 = Must Not Да, так как есть положительное условие term1.
term1 -term2 Бинарный (с унарным минусом) term1 = Must/Should (зависит от default operator), term2 = Must Not Да.

Правило безопасности:

Никогда не используйте чистый NOT или - как начало запроса без положительного условия.

  • Плохо: NOT spam, -spam, AND NOT spam
  • Хорошо: : -spam (все документы кроме спама), title:новости -spam

Влияние на релевантность (Scoring)

Документы, отсеянные оператором исключения (Must Not clause), полностью исключаются из набора результатов и не участвуют в расчете скоринга (TF/IDF, BM25).

  • Они не получают _score.
  • Они не влияют на нормализацию длинны поля для других документов.
  • Это «жесткий фильтр» (Hard Filter).

Если вам нужно понизить релевантность, но не исключать совсем (например, «покажи новости про политику, но про спорт опусти вниз»), используйте Function Score Query или комбинацию should / boost в DSL, а не Query Parser синтаксис исключения.

Типичные ошибки и лучшие практики

Ошибка 1: Игнорирование приоритета при смешивании OR и AND NOT

Решение: Всегда ставьте скобки: (a OR b) AND NOT c или (a OR b) -c.

Ошибка 2: Использование анализатора на исключаемом термине

Если поле title использует анализатор (например, стеммер или стоп-слова), запрос title:running AND NOT title:run может не сработать так, как вы ожидаете, потому что running и run превратятся в один токен.

Решение: Для точного исключения используйте поля типа keyword (неанализируемые) или будьте уверены в токенизации.

Ошибка 3: Попытка исключить «все» через NOT

Решение: Используйте : -field:value (MatchAllDocsQuery минус условие).

Примеры для Elasticsearch / OpenSearch (Query String Query)

В Elasticsearch синтаксис Lucene Query Parser доступен через query_string и simple_query_string.

Пример 1: Поиск логов об ошибках, исключая healthcheck

GET /logs/_search
{
 "query": {
 "query_string": {
 "query": "level:ERROR AND NOT message:/healthcheck|ping/",
 "default_field": "message"
 }
 }
}

Пример 2: Электронная коммерция — товары в наличии, кроме снятых с производства

GET /products/_search
{
 "query": {
 "query_string": {
 "query": "status:active -tags:discontinued -category:archive",
 "fields": ["name^3", "description", "sku"]
 }
 }}

Пример 3: Безопасный «Match All Except» (для админок/отчетов)

GET /_search
{
 "query": {
 "query_string": {
 "query": ": -is_deleted:true",
 "allow_leading_wildcard": true
 }
 }
}

Резюме (Cheat Sheet)

  1. Синтаксис: term1 AND NOT term2 или term1 -term2.
  2. Предпочитайте минус (-): Он имеет высший приоритет, читается легче и ведет себя предсказуемее как «фильтр запрета».
  3. Скобки, ваши друзья: Обязательно группируйте сложные выражения: (a OR b) -c.
  4. Позитивное условие обязательно: Нельзя искать только «НЕ что-то». Нужен базовый запрос (:, field:value).
  5. Это фильтр, не буст: Исключенные документы полностью исчезают из результатов и не влияют на скор остальных.
  6. Поля: Указывайте поле явно для исключения, если оно отличается от поля поиска: text:query -status:hidden.

Правильное использование AND NOT / - позволяет строить точные и быстрые поисковые запросы, отсекая шум на уровне парсера запросов, не нагружая скоринг ненужными данными.