Apache Lucene — это мощная и гибкая библиотека полнотекстового поиска, лежащая в основе таких популярных решений, как Elasticsearch, Solr и OpenSearch. Одной из ключевых возможностей её синтаксиса запросов (Lucene Query Syntax) является возможность не только искать документы, содержащие определенные термины, но и исключать документы, соответствующие нежелательным критериям.
В этой статье мы подробно разберем оператор AND NOT, его синтаксис, нюансы работы, отличия от альтернативных способов исключения и лучшие практики применения.
Базовый синтаксис и принцип работы
Оператор AND NOT (часто используемый синоним — NOT или символ -) позволяет отфильтровать результаты поиска, отбрасывая документы, которые соответствуют термину или подзапросу, стоящему справа от оператора.
Основные формы записи:
- Явная форма:
term1 AND NOT term2 - Сокращенная форма (ключевое слово):
term1 NOT term2 - Символьная форма (префикс):
term1 -term2
Важно: В Lucene операторы AND, OR, NOT должны писаться заглавными буквами, иначе они будут восприняты как обычные поисковые термины.
Примеры базового использования:
# Найти документы, содержащие "java", но НЕ содержащие "script"
java AND NOT script
java NOT script
java -script
Приоритет операторов и группировка
Понимание приоритета операторов критически важно для составления правильных запросов. В Lucene приоритет (от высшего к низшему) следующий:
- Группировка скобками
- Префиксные операторы:
+,-,NOT ANDOR
Примеры влияния приоритета:
| Запрос | Интерпретация Lucene | Результат |
|---|---|---|
a AND NOT b OR c | (a AND (NOT b)) OR c | Документы с «a» без «b» ИЛИ любые документы с «c». |
a AND (NOT b OR c) | a AND ((NOT b) OR c) | Документы с «a», в которых либо нет «b», либо есть «c». |
-a b | (NOT a) AND b (неявный AND) | Документы с «b», в которых нет «a». |
Рекомендация: Всегда используйте скобки для явного указания логики, особенно при комбинировании AND NOT с OR.
Исключение фраз и сложных подзапросов
Оператор AND NOT работает не только с отдельными термами, но и с фразами (в кавычках) и целыми подзапросами в скобках.
Исключение точной фразы:
# Исключить документы, содержащие точную фразу "deprecated api"
java AND NOT "deprecated api"
Исключение по полю (Field-level exclusion):
Можно исключать термины только в конкретном поле.
# Найти "lucene" в заголовке, но исключить, если в поле "category" стоит "draft"
title:lucene AND NOT category:draft
title:lucene -category:draft
Исключение сложных логических блоков:
# Исключить документы, которые ИМЕЮТ ("error" ИЛИ "exception") В ПОЛЕ "log_level"
message:timeout AND NOT (log_level:error OR log_level:exception)
Критически важный нюанс: "Pure Negative" запросы (Чисто отрицательные запросы)
Это самая частая ловушка для новичков.
Lucene не поддерживает запросы, состоящие только из отрицательных условий (так называемые "pure negative queries"). Запрос вроде NOT term или -term без положительного условия вернет ошибку или пустой результат (в зависимости от версии парсера и настроек allowLeadingWildcard / QueryParser конфигурации).
-status:deleted
NOT category:spam ✅ Верно (нужно добавить положительное условие, например, MatchAllDocsQuery или 
: -status:deleted
: NOT category:spam В программном коде (Java) для этого часто используют MatchAllDocsQuery в комбинации с BooleanQuery:
BooleanQuery.Builder builder = new BooleanQuery.Builder;
builder.add(new MatchAllDocsQuery, Occur.MUST);
builder.add(new TermQuery(new Term("status", "deleted")), Occur.MUST_NOT);
Query query = builder.build;
AND NOT vs MUST_NOT (BooleanQuery API)
При программном построении запросов через Java API (не через строковый парсер) используется класс BooleanQuery и перечисление Occur.
| Строковый синтаксис (QueryParser) | Java API (BooleanClause.Occur) | Семантика |
|---|---|---|
term1 AND term2 | MUST, MUST | Оба обязательны (пересечение). |
term1 AND NOT term2 | MUST, MUST_NOT | term1 обязателен, term2 запрещен. |
term1 OR term2 | SHOULD, SHOULD | Хотя бы одно (объединение). |
-term2 (без MUST) | MUST_NOT (одиночный) | Ошибка / Пусто (Pure Negative). |
Оператор AND NOT в парсере ровно соответствует комбинации Occur.MUST (левая часть) + Occur.MUST_NOT (правая часть).
Влияние на релевантность (Scoring)
Клаузылы, помеченные как MUST_NOT (через AND NOT, NOT или -), не влияют на счет релевантности (score) документов.
- Они работают как фильтр (filter context): документ либо проходит, либо отбрасывается.
- Они не увеличивают и не уменьшают
_scoreсовпавших документов. - Это делает их очень производительными, так как Lucene может кэшировать набор исключенных документов (bitset) и быстро применять их при последующих поисках.
Распространенные ошибки и лучшие практики
Ошибка 1: Путаница с оператором - внутри фразы
Символ дефиса - является оператором только в начале термина (после пробела или скобки). Внутри слова или фразы он является частью термина (если анализатор не разбивает его).
# Это исключение термина "script"
java -script
java-script
Ошибка 2: Игнорирование анализатора (Analyzer)
Запрос проходит через тот же анализатор, что и индексация. Если поле проиндексировано с StandardAnalyzer (приводит к нижнему регистру, удаляет стоп-слова), то и запрос будет приведен к нижнему регистру.
# В индексе терм сохранен как "java"
Java AND NOT SCRIPT
Но если вы используете KeywordAnalyzer (точное совпадение), регистр имеет значение.
Лучшая практика: Использование Filter Context (в Elasticsearch/OpenSearch)
Если вы пишете запрос для Elasticsearch (DSL), для исключения лучше использовать bool.filter с must_not, а не query_string с AND NOT.
GET /_search
{
"query": {
"bool": {
"must": [ { "match": { "title": "lucene" } } ],
"filter": [
{ "bool": { "must_not": { "term": { "status": "archived" } } } }
]
}
}
} Это гарантирует кэширование и отсутствие влияния на скор.
Резюме: Шпаргалка по синтаксису
| Задача | Синтаксис QueryParser | Примечание |
| :--- | :--- | :--- |
| Исключить термин | `term1 -term2` | Самый краткий и читаемый вариант. |
| Исключить фразу | `term1 -"exact phrase"` | Кавычки обязательны для фразы. |
| Исключить в поле | `title:search -status:draft` | Префикс поля применяется к термину после минуса. |
| Исключить группу | `term1 -(term2 term3)` | Исключить документы, содержащие И term2 И term3 (неявный AND). |
| Исключить ИЛИ-группу | `term1 -(term2 OR term3)` | Явное OR внутри исключения. |
| Только исключение | `: -term` | Обязательно добавить `:` или `MatchAllDocsQuery`. |
Оператор AND NOT (и его аналоги NOT, -) — это фундаментальный инструмент для точной настройки релевантности поиска в Lucene. Он позволяет отсекать шумовые данные, устаревшие записи или нежелательные категории без ущерба для производительности, так как работает в контексте фильтрации и не влияет на расчет скорa.
Главные правила запомнить:
- Пишите операторы ЗАГЛАВНЫМИ буквами.
- Используйте скобки для управления приоритетом при смешивании с
OR. - Никогда не используйте чисто отрицательные запросы без положительного условия (добавляйте `:`).
- Для программной генерации запросов предпочитайте
BooleanQuery.BuilderсOccur.MUST_NOTстроковому парсеру, это безопаснее и быстрее.
Правильное применение исключений — залог быстрого и релевантного поиска в ваших приложениях на базе Lucene, Solr или Elasticsearch.