query_string и match в Elasticsearch работа со спецсимволами

Дата подготовки: 08:24:07 ()


В Elasticsearch существует несколько способов формировать запросы к полям документа. Два наиболее часто используемых – query_string и match. Оба позволяют искать по тексту, но различаются в способе обработки запроса, поддержке специальных символов и влиянии на производительность. В этой статье мы подробно рассмотрим, как каждый из них ведёт себя при наличии спецсимволов (например, дефиса, точки, слэша, кавычек и т.п.), какие нюансы следует учитывать и когда предпочтительнее использовать тот или иной метод.

Краткий обзор каждого типа запроса

query_string

query_string – это мощный парсер запросов, который позволяет использовать синтаксис Lucene query language. Он поддерживает:

  • логические операторы (AND, OR, NOT);
  • группировку скобками;
  • wildcard‑ и regexp‑поиск;
  • фразовый поиск с кавычками;
  • указание полей через префикс field:;
  • множество дополнительных параметров (default_operator, analyze_wildcard, lenient и др.).

Из‑за того, что запрос проходит через полноценный парсер, специальные символы могут интерпретироваться как часть синтаксиса (например, дефис – оператор NOT, точка – любой символ в regexp и т.д.). Поэтому при работе с пользовательским вводом, содержащим такие символы, часто требуется экранирование или предварительная обработка.

match query

match относится к семейству «term‑level» запросов, но на самом деле он работает на уровне analysed text: запрос сначала проходит через тот же анализатор, что и поле при индексации, а затем формируется запрос типа bool с соответствующими термами. Основные особенности:

  • не поддерживает синтаксис Lucene (нет операторов AND/OR, wildcard, regexp и т.п.);
  • автоматически разбивает входную строку на токены согласно анализатору поля;
  • по умолчанию использует оператор OR (можно изменить через operator);
  • не требует экранирования специальных символов, если они не являются частью синтаксиса query_string (т.к. такого синтаксиса нет).

Таким образом, match более «дружелюбен» к произвольному тексту, содержащему спецсимволы, потому что он просто передаёт строку анализатору, а не пытается интерпретировать её как запрос на языке Lucene.

Как спецсимволы влияют на работу каждого запроса

Примеры спецсимволов

Рассмотрим набор типичных символов, которые могут вызывать проблемы:

  • дефис - (часто используется как оператор NOT);
  • точка . (в regexp – любой символ);
  • слеш / (открывает/закрывает regexp);
  • кавычки " и одинарные ' (фразовый поиск);
  • двоеточие : (указание поля);
  • скобки (группировка);
  • звёздочка * и вопросительный знак ? (wildcard).

query_string и спецсимволы

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

Неэкранированный запрос:

GET my_index/_search
{
  «query»: {
    «query_string»: {
      «query»: «error-34567332»,
      «default_field»: «message»
    }
  }
}

Здесь дефис будет интерпретироваться как оператор NOT, и Elasticsearch будет искать документы, содержащие терм error и не содержащие 34567332. Результат, скорее всего, будет пустым или неожиданным.

Экранированный запрос:

GET my_index/_search
{
  «query»: {
    «query_string»: {
      «query»: «error\-34567332»,
      «default_field»: «message»,
      «lenient»: true
    }
  }
}

Теперь дефис воспринимается как обычный символ, и поиск вернёт документы, содержащий exact терм error-34567332 (при условии, что поле не анализирует дефис как разделитель).

Важно также отметить параметр analyze_wildcard: если он установлен в true, то wildcard‑символы (*, ?) будут анализироваться, что может изменить результат при наличии спецсимволов внутри шаблона.

match query и спецсимволы

В случае match специальные символы просто передаются анализатору. Если поле использует стандартный анализатор (standard), то:

  • дефис обычно считается частью токена (если не настроен иначе);
  • точка, слэш и другие символы могут быть удалены или заменены пробелом в зависимости от настроек токенизатора;
  • кавычки не имеют особого значения – они воспринимаются как обычные символы.
  • Пример:

    GET my_index/_search
    {
      «query»: {
        «match»: {
          «message»: «error-34567332»
        }
      }
    }

    Запрос будет разбит на токены согласно анализатору поля message. Если анализатор оставляет дефис внутри токена, то будет выполнен поиск по терму error-34567332. Если же анализатор разбивает по дефису, получим два терма error и 34567332 и поиск будет работать как OR (по умолчанию). Чтобы получить точное совпадение, можно:

    • использовать поле с keyword‑типом (не анализируемое);
    • применить параметр operator: "and" для match, если нужно, чтобы оба токена присутствовали;
    • воспользоваться match_phrase для поиска фразы (сохраняет порядок токенов).
    • Сравнение производительности

      С точки зрения нагрузки на кластер:

      • query_string требует парсинга входной строки, построения абстрактного синтаксического дерева и последующего преобразования в низкоуровневые запросы (term, prefix, wildcard и т.д.). Этот этап добавляет небольшую, но измеримую задержку, особенно при сложных выражениях с множеством операторов и скобок.
      • match bypasses парсинг Lucene query language; он сразу передаёт строку анализатору и формирует простой bool‑запрос из полученных термов. Поэтому, как правило, match работает чуть быстрее при простых поисках по одному полю.
      • Однако, если нужно воспользоваться расширенным синтаксисом (логические операторы, wildcard, regexp, fuzzy и т.п.), то query_string остаётся единственным практичным вариантом, и его производительность становится приемлемой за счёт кэширования разобранного запроса (query cache).

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

      Когда использовать query_string, а когда match

      Рекомендации для query_string

      • Нужен сложный поиск с логическими операторами, скобками, группами полей (field1:value OR field2:value).
      • Требуется поддержка wildcard, regexp, fuzzy, proximity search (~), boosting внутри запроса.
      • Вы контролируете ввод (например, формируете запрос в админ‑панели) и можете гарантировать корректное экранирование спецсимволов.
      • Хотите использовать параметры lenient, default_operator, analyze_wildcard для тонкой настройки.

      Рекомендации для match

      • Поиск по пользовательскому вводу, где нельзя гарантировать отсутствие спецсимволов (форма поиска на сайте, лог‑поиск и т.п.).
      • Необходимо простое совпадение по одному или нескольким полям без сложного синтаксиса.
      • Требуется высокая предсказуемость результатов и минимальная вероятность синтаксических ошибок.
      • Вы используете поля типа keyword для точного совпадения или text с анализатором, который уже обрабатывает дефисы, точки и т.д. так, как вам нужно.

      Практические примеры с экранированием

      Пример 1: Поиск логов с дефисом и точкой

      Предположим, у нас есть индекс logs с полем message типа text (анализатор standard). Нужно найти все записи, содержащие строку ERROR-2026.08.07.

      С использованием query_string (с экранированием):

      GET logs/_search
      {
        «query»: {
          «query_string»: {
            «query»: «ERROR\-2026\.08\.07»,
            «default_field»: «message»,
            «lenient»: true
          }
        }
      }

      С использованием match (без экранирования):

      GET logs/_search
      {
        «query»: {
          «match»: {
            «message»: «ERROR-2026.08.07»
          }
        }
      }

      Если анализатор оставляет дефис и точки внутри токена, оба запроса вернут одинаковый результат. Если же анализатор разбивает по этим символам, то match вернёт документы, где присутствуют любые из токенов ERROR, 2026, 08, 07 (по умолчанию OR). Чтобы получить точное совпадение в этом случае, лучше использовать поле keyword или match_phrase.

      Пример 2: Поиск product‑кода со звёздочкой (wildcard)

      Нужно найти все товары, код которых начинается с ABC- и заканчивается на три цифры, например ABC-123.

      Только query_string поддерживает wildcard напрямую:

      GET products/_search
      {
        «query»: {
          «query_string»: {
           "query": "ABC-*",
           "default_field": "sku"
          }
        }
      }

      Здесь звёздочка воспринимается как wildcard, а дефис – как обычный символ (предполагаем, что он не экранирован, так как не является оператором NOT в данном контексте).

      Match не может выполнить такой запрос напрямую; для подобных задач потребуется либо:

      • использовать wildcard или prefix запрос отдельно;
      • или добавить отдельное поле sku.edge_ngram для поиска по префиксу.

      Оба метода – query_string и match – имеют своё место в арсенале Elasticsearch:

      • query_string – мощный, гибкий, но требует осторожного обращения со специальными символами и чуть более тяжёл в обработке.
      • match – проще и безопаснее для произвольного пользовательского ввода, особенно когда нужно простое совпадение без сложного синтаксиса.

      При наличии спецсимволов в поисковом запросе ключевым моментом является понимание того, как эти символы интерпретируются выбранным парсером. Если вы можете гарантировать корректное экранирование – query_string даст вам доступ к расширенным возможностям Lucene. Если же вы работаете с открытыми формами ввода, где пользователь может вводить любые символы, то match (или match_phrase) обычно является более надёжным выбором.

      Надеемся, данная сравнительная статья поможет вам выбрать оптимальный метод поиска для ваших сценариев использования Elasticsearch при работе со специальными символами.