Поиск в ClickStack и Elastic
ClickHouse — это движок с нативной поддержкой SQL, изначально созданный для высокопроизводительных аналитических рабочих нагрузок. Elasticsearch, напротив, предоставляет SQL-подобный интерфейс, транслируя SQL в базовый DSL запросов Elasticsearch, — то есть SQL в нем не является средством первого класса, а функциональный паритет ограничен.
ClickHouse не только поддерживает полноценный SQL, но и расширяет его набором функций, ориентированных на обсервабилити, таких как argMax, histogram и quantileTiming, которые упрощают выполнение запросов к структурированным журналам, метрикам и трассировкам.
Для простого анализа журналов и трассировок интерфейс ClickStack (HyperDX) предоставляет синтаксис в стиле Lucene для интуитивной текстовой фильтрации по запросам вида поле-значение, диапазонам, подстановочным шаблонам и другим конструкциям. Это сопоставимо с синтаксисом Lucene в Elasticsearch и элементами Kibana Query Language.

Интерфейс поиска поддерживает этот знакомый синтаксис, но за кулисами преобразует его в эффективные SQL-условия WHERE, что делает работу привычной для пользователей Kibana и при этом позволяет при необходимости использовать всю мощь SQL. Это позволяет задействовать весь набор функций поиска по строкам, функций сходства и функций даты и времени в ClickHouse.

Ниже мы сравним языки запросов Lucene в ClickStack и Elasticsearch.
Поисковый синтаксис ClickStack и строка запроса Elasticsearch
И ClickStack, и Elasticsearch предоставляют гибкие языки запросов для удобной фильтрации журналов и трассировок. Если строка запроса Elasticsearch тесно связана с его DSL и движком индексирования, то ClickStack поддерживает синтаксис в стиле Lucene, который под капотом переводится в ClickHouse SQL. В таблице ниже показано, как распространённые шаблоны поиска работают в обеих системах, с акцентом на сходство синтаксиса и различия в их внутреннем выполнении.
| Возможность | Синтаксис ClickStack | Синтаксис Elasticsearch | Примечания |
|---|---|---|---|
| Поиск по свободному тексту | error |
error |
Выполняет поиск по всем индексируемым полям; в ClickStack это преобразуется в многостолбцовый SQL ILIKE. |
| Совпадение по полю | level:error |
level:error |
Синтаксис идентичен. В ClickStack выполняется поиск точных значений полей в ClickHouse. |
| Поиск по фразе | "disk full" |
"disk full" |
Текст в кавычках соответствует точной последовательности; ClickHouse использует сравнение строк или ILIKE. |
| Совпадение фразы по полю | message:"disk full" |
message:"disk full" |
Преобразуется в SQL ILIKE или точное совпадение. |
| Условия OR | error OR warning |
error OR warning |
Логическое OR для терминов; обе системы поддерживают это нативно. |
| Условия AND | error AND db |
error AND db |
В обеих системах соответствует пересечению; для пользователя синтаксис не отличается. |
| Отрицание | NOT error or -error |
NOT error or -error |
Поддерживается одинаково; ClickStack преобразует это в SQL NOT ILIKE. |
| Группировка | (error OR fail) AND db |
(error OR fail) AND db |
Стандартная булева группировка в обеих системах. |
| Подстановочные шаблоны | error* or *fail* |
error*, *fail* |
ClickStack поддерживает подстановочные шаблоны в начале и конце строки; в ES шаблоны в начале строки по умолчанию отключены из-за производительности. Подстановочные шаблоны внутри терминов не поддерживаются, например f*ail. Подстановочные шаблоны должны использоваться с совпадением по полю. |
| Диапазоны (числовые/дата) | duration:[100 TO 200] |
duration:[100 TO 200] |
ClickStack использует SQL BETWEEN; Elasticsearch разворачивает это в range-запросы. Неограниченный * в диапазонах не поддерживается, например duration:[100 TO *]. При необходимости используйте Unbounded ranges ниже. |
| Неограниченные диапазоны (числовые/дата) | duration:>10 or duration:>=10 |
duration:>10 or duration:>=10 |
ClickStack использует стандартные SQL-операторы |
| Включающие/исключающие границы | duration:{100 TO 200} (исключая границы) |
То же самое | {} обозначает исключающие границы. * в диапазонах не поддерживается. Например, duration:[100 TO *] |
| Проверка существования | N/A | _exists_:user or field:* |
_exists_ не поддерживается. Используйте LogAttributes.log.file.path: * для столбцов Map, например LogAttributes. Для корневых столбцов они должны существовать и будут иметь значение по умолчанию, если не включены в событие. Чтобы искать значения по умолчанию или отсутствующие столбцы, используйте тот же синтаксис, что и в Elasticsearch: ServiceName:* or ServiceName != ''. |
| Regex | функция match |
name:/joh?n(ath[oa]n)/ |
В настоящее время не поддерживается в синтаксисе Lucene. Можно использовать SQL и функцию match или другие функции поиска по строкам. |
| Нечеткое совпадение | editDistance('quikc', field) = 1 |
quikc~ |
В настоящее время не поддерживается в синтаксисе Lucene. В SQL можно использовать Функции расстояния, например editDistance('rror', SeverityText) = 1, или другие функции сходства. |
| Поиск по близости | Не поддерживается | "fox quick"~5 |
В настоящее время не поддерживается в синтаксисе Lucene. |
| Повышение веса | quick^2 fox |
quick^2 fox |
В настоящее время не поддерживается в ClickStack. |
| Подстановочный шаблон поля | service.*:error |
service.*:error |
В настоящее время не поддерживается в ClickStack. |
| Экранированные специальные символы | Экранируйте зарезервированные символы с помощью \ |
То же самое | Для зарезервированных символов требуется экранирование. |
Различия между exists/missing
В отличие от Elasticsearch, где поле может полностью отсутствовать в событии и, следовательно, действительно «не существовать», в ClickHouse все столбцы должны быть определены в схеме таблицы. Если поле не передано в событии вставки:
- Для полей
Nullableбудет установлено значениеNULL. - Для полей, не допускающих
NULL(по умолчанию), будет подставлено значение по умолчанию (часто это пустая строка, 0 или эквивалентное значение).
В ClickStack мы используем второй вариант, так как Nullable не рекомендуется.
Это означает, что проверка того, «существует» ли поле, в смысле Elasticsearch напрямую не поддерживается.
Вместо этого можно использовать field:* или field != '', чтобы проверить наличие непустого значения. Таким образом, невозможно отличить действительно отсутствующие поля от явно пустых.
На практике это различие редко вызывает проблемы в сценариях обсервабилити, но о нём важно помнить при переносе запросов между системами.