Работа с RQL-запросами
В данном разделе описано, как использовать язык запросов RQL для анализа событий и записей активных списков. Описано создание запросов, которые включают работу с проекциями, использование условий, управление выводом, сортировку и группировку результатов, а также заполнение данных. Также описывается использование фильтров и временных ограничений в поиске.
|
В данном разделе при описании выражений приняты следующие обозначения:
|
Содержание раздела:
Синтаксис запросов
RQL-запросы используются для поиска и извлечения информации о событиях безопасности и записях активных списков.
Работа с данными в RQL осуществляется только в рамках выбранных источников — хранилищ событий или активных списков. Поэтому, в отличие от SQL, в запросах не требуется указывать таблицу базы данных в компоненте FROM <имя таблицы>.
RQL-запросы используются в разделах Дашборды и Поиск. В разделе Поиск доступны два режима поиска — базовый и продвинутый, в последнем случае доступны использование проекций и группировка результатов.
[SELECT *] [<expression>]
[(INNER | LEFT) JOIN `active_list_name` [AS alias_name] ON <expression> [ENRICH target_field = source_field]]
[ORDER BY field_name [ASC | DESC] [WITH FILL [FROM <expression> TO <expression>] STEP <expression>]]
[LIMIT <m> [OFFSET <n>]]
SELECT <expression> [AS alias_name][, <expression>]
[(INNER | LEFT | RIGHT | FULL) JOIN `active_list_name` [AS alias_name] ON <expression> [ENRICH target_field = source_field]]
[WHERE <expression>]
[GROUP BY field_name [HAVING <expression>]]
[ORDER BY field_name [ASC | DESC] [WITH FILL [FROM <expression> TO <expression>] STEP <expression>]]
[LIMIT <m> [OFFSET <n>]]
| Запрос в SQL | Запрос в системе |
|---|---|
|
|
|
|
|
|
|
|
Фильтры в разделе Поиск и ограничения по времени применяются как дополнительные условия для запроса.
|
Если в RQL-запросе необходимо обратиться к полю, название которого совпадает с ключевым словом ClickHouse, например, Пример запроса к полю с ключевым словом в названии
|
Работа с проекциями (оператор SELECT)
В системе для каждого хранилища событий выбирается модель событий, а для активного списка — схема активного списка, которые определяют структуру данных. По умолчанию система возвращает результат со всеми полям текущей модели данных или схемы активного списка независимо от того, какие поля отображаются в списке. Поэтому, если в результат необходимо включить все поля, в базовом поиске компонент запроса SELECT * может опускаться.
Оператор SELECT используется при работе с проекциями, то есть для указания определенных полей, которые будут включены в результат запроса. Этот оператор часто используется в виджетах и метриках, которые создаются на дашбордах. Имена полей в запросе отделяются друг от друга запятыми.
SELECT <expression>[, <expression>]
| Не ставьте разделитель после последнего поля, включенного в запрос. |
Оператор SELECT является обязательным при использовании оператора WHERE и группировке результатов.
| Запрос в SQL | Запрос в системе |
|---|---|
|
|
|
|
Обращение к вложенным данным в полях типа JSON
В RQL-запросах доступно обращение ко вложенным объектам и массивам в полях типа JSON с помощью оператора $.
Поиск по полям внутри JSON приводит к снижению производительности, так как внутренние поля не индексируются.
|
Обращение к полям JSON
Чтобы обратиться к вложенному полю, используйте полный путь в JSON:
SELECT $<field_name>.<key_1>.<key_2>….<key_N>
Здесь:
-
<field_name>— ключ поля модели событий. -
<key_1>, …,<key_N>— ключи JSON. Ключи в пути перечисляются в порядке вложенности и разделяются точкой.
При обращении к первому уровню полей оператор $ можно опустить. Например, запросы data.referer вместо $data.referer.
|
Обращение к вложенным объектам JSON
Чтобы обратиться к вложенному объекту типа JSON, используйте оператор ^ перед путем:
SELECT $<field_name>.^<object_key>
Здесь:
-
<field_name>— ключ поля модели событий. -
<object_key>— ключ объектаJSON.
Обращение к массивам JSON
Чтобы обратиться к плоскому массиву, где все элементы — простые значения, используйте путь в JSON:
SELECT $<field_name>.<arr_key>
Здесь:
-
<arr_key> — ключ поля, которое является плоским массивом.
Чтобы обратиться к массиву со вложенными объектами, используйте оператор []:
$<field_name>.<nested_arr_key>[]
Здесь:
-
<nested_arr_key> — ключ поля-массива, которое содержит объекты.
Чтобы искать ключ во всех элементах массива, используйте следующий синтаксис:
SELECT $<field_name>.<arr_key>[].<nested_key>
Здесь:
-
<field_name>— ключ поля модели событий; -
<arr_key>— ключ массива JSON; -
<nested_key>— ключ JSON, который будет найден во всех элементах массива<key_2>.
Обращение к массиву можно сочетать с обращением к вложенному объекту:
SELECT $<field_name>.<arr_key>[].^<object_key>
| Обращение к отдельному элементу массива JSON недоступно. |
Примеры обращения к полям JSON
Рассмотрим обращение к полям JSON на примере события, которое имеет поле data со следующим содержанием:
{
"timestamp": "2025-08-15T15:07:89Z",
"source": {
"system": "firewall",
"ip": "192.0.2.0"
},
"events": [
{
"event_id": "evt-001",
"actor": {
"user": "admin"
}
},
{
"event_id": "evt-002",
"actor": {
"process": "nginx"
}
},
{
"event_id": "evt-003",
"actor": {
"process": "powershell.exe"
}
}
],
"params": [
[
"name",
"value"
],
[
"result",
"success"
]
]
}
Пример 1. Обращение к вложенному полю JSON
SELECT $data.source.ip
Этот запрос вернет значение поля ip, которое находится внутри source:
"192.0.2.0"
Пример 2. Обращение к вложенному объекту JSON
SELECT $data.^source
Этот запрос вернет объект в поле source:
{"system":"firewall","ip":"192.0.2.0"}
Пример 3. Обращение к массивам внутри массива
SELECT $data.params
Этот запрос вернет элементы массивов внутри массива params:
name,value,result,success
Пример 4. Поиск ключей внутри элементов массива
SELECT $data.events[].event_id
Этот запрос вернет массив значения полей event_id внутри элементов массива events:
evt-001,evt-002,evt-003
Пример 5. Поиск вложенных объектов внутри элементов массива
SELECT $data.events[].^actor
Этот запрос вернет массив значений полей actor внутри элементов массива events:
{"user": "admin""},{"process":"nginx"},"process":"powershell.exe"}
Использование псевдонимов (оператор AS)
При редактировании виджетов и метрик удобно использовать псевдонимы, которые позволяют сократить названия компонентов запроса в секции Сопоставление полей.
SELECT <expression> AS alias_name
Пример использования псевдонима для функции
При создании виджета типа Таблица вводится запрос:
count()SELECT collectorId, count(*) AS cnt WHERE collectorId LIKE '%' GROUP BY collectorId
Здесь для функции count(*) задается псевдоним cnt, который можно указать в секции сопоставления полей.
Использование условий (оператор WHERE)
В базовом поиске при задании условия оператор WHERE в запросе опускается.
При использовании проекций оператор WHERE позволяет применять к указанному полю запрос с выборкой строк по определенному условию.
SELECT <expression> WHERE <expression>
В условиях используются операторы сравнения, работы с множествами и проверки на пустое значение. Для проверки полей типа Array используйте функцию has.
Пример выборки строк по условию
WHERESELECT tenantId WHERE length(tenantId) = 5
Этот запрос возвращает события с количеством символов в поле tenantId, равным 5.
Операторы сравнения
| Оператор | Значение | Типы данных |
|---|---|---|
|
Равно |
Все типы |
|
Не равно |
Все типы |
|
Меньше |
Числовые типы и даты |
|
Меньше или равно |
Числовые типы и даты |
|
Больше |
Числовые типы и даты |
|
Больше или равно |
Числовые типы и даты |
|
Соответствует заданному шаблону. |
Строковые типы |
|
Не соответствует заданному шаблону. |
Строковые типы |
|
Входит в диапазон. |
Числовые типы и даты |
|
Не входит в диапазон. |
Числовые типы и даты |
|
Шаблон может включать обычные символы и следующие метасимволы:
|
Использование операторов с полями JSON
Чтобы обратиться в условии к вложенным полям JSON, используйте соответствующий синтаксис:
WHERE и вложенным полем JSONSELECT $data WHERE $data.source.ip = "192.0.2.0"
Для приведения типа используйте соответствующую функцию преобразования типов:
WHERE и вложенным полем JSONSELECT $data WHERE toUInt64($data.process_id) = 2776
Например, чтобы выполнить поиск по полю JSON с оператором LIKE, сначала приведите его к строке с помощью функции toString:
LIKE и приведением к строкеSELECT * WHERE toString(data) LIKE '192.0.2.0%'
Данный запрос вернет записи, где в поле data встречается последовательность 192.0.2.0.
LIKE и приведением к строкеSELECT * WHERE toString(data) like '%{"active":true,"ip":"192.0.2.0"}%'
Данный запрос вернет записи, где в поле data содержатся вложенные поля в указанном порядке:
{
"active": true,
"ip": "192.0.2.0"
}
Операторы работы с множествами (IN) и проверки на пустое значение (IS NULL)
| Оператор | Значение |
|---|---|
|
Входит во множество |
|
Не входит во множество |
|
Значение является пустым (NULL) |
|
Значение не является пустым (NULL) |
Поиск по подсетям с оператором IN
С помощью оператора IN можно осуществлять поиск данных по подсетям. Формат записи подсети: <netmask>/<prefix>. Здесь:
-
<subnet>— маска подсети. -
<prefix>— сетевой префикс. Допустимые значения зависят от версии IP-протокола:-
для IPv6: 0—128;
-
для IPv4: 0—32.
-
INsourceIp IN '::ffff:192.0.2.0/120'
INsourceIp IN '192.0.2.0/24'
|
В системных моделях событий нет полей, содержащих IPv4-адреса. Чтобы работать с IPv4-адресами, вы можете:
|
Поиск по подсетям также можно осуществлять с помощью функции isIPAddressInRange.
|
Проверка наличия элемента в массиве (функция has)
Чтобы проверить наличие элемента в поле с типом Array, используйте функцию has:
SELECT * WHERE has(field_name, value)
Здесь value — значение, которое нужно найти среди элементов массива. Может использоваться значение NULL.
Комбинирование условий (операторы AND, OR, NOT)
Совмещение нескольких условий происходит с использованием операторов:
| Оператор | Значение |
|---|---|
|
Конъюнкция ("И") |
|
Дизъюнкция ("ИЛИ") |
|
Логическое отрицание |
Пример запроса с комбинированным условием
Рассмотрим запрос, возвращающий события, у которых значения в поле tenantId начинаются с символа n, а количество символов в поле raw превышает 10.
SELECT * FROM table WHERE tenantId LIKE 'n%' AND length(raw) > 10
tenantId LIKE 'n%' AND length(raw) > 10
Работа с настраиваемыми полями универсальных моделей
В RQL-запросах доступно обращение к регулярным настраиваемым полям универсальной модели события. Обращение к таким полям может осуществляться как напрямую, так и посредством специального синтаксиса.
|
Обращение посредством специального синтаксиса доступно, если выполняются следующие условия:
|
<custom_field>:<custom_field_label> <expression>
Здесь:
-
<custom_field>— ключ значения настраиваемого поля, например:cn1,deviceCustomDate1.Вместо ключа конкретного поля можно использовать его семейство, зависящее от типа данных значения в этом поле:
-
cs—LCString; -
cn—UInt64; -
c6a—IPv6; -
deviceCustomDate—DateTime64.Семейство полей рекомендуется использовать в случаях, когда требуется проверять условие <expression>по всем полям этого семейства.
-
-
<custom_field_label>— поле метаданных, хранящее метку, которая указывает на содержание и назначение связанного с ним поля<custom_field>. Эта метка играет роль названия для поля<custom_field>и позволяет интерпретировать хранящиеся в нем данные.Примеры меток:
-
cn1Label— для поляcn1; -
deviceCustomDate1Label— для поляdeviceCustomDate1;
-
-
<expression>— выражение с условием поиска по полю значения, использующее операции сравнения (=,!=,<>,<,>,<=,>=) и/или операторыIN,LIKE,NOT LIKE.
|
Примеры обращения к настраиваемым полям
Пример обращения к полю напрямую
Допустим, имеется поток событий, у которых в поле cs1 хранится адрес электронной почты пользователя, совершившего подозрительное действие. Известно, что часть событий является тестовой и приходит от разных пользователей с одним и тем же электронным адресом test@example.com.
Требуется исключить из рассматриваемого потока тестовые события. Ниже представлен вариант соответствующего RQL-запроса с непосредственным обращением к полю cs1.
cs1 != "test@example.com"
Запрос выдаст события, поле cs1 которых не содержит значение test@example.com.
Пример обращения к полю по его ключу
Допустим, имеется поток событий от разных источников. Для событий от некоторых источников в поле cs1 хранится адрес электронной почты пользователя, совершившего подозрительное действие. В соответствующем поле метки cs1Label этих событий хранится значение email. Известно, что часть событий от этих источников является тестовой и приходит от разных пользователей с одним и тем же электронным адресом test@example.com.
Требуется получить поток событий, для которых указаны электронные адреса пользователей, и исключить из него тестовые события. Ниже представлен RQL-запрос, построенный на основе представленного ранее синтаксиса. Обращение к полю cs1 выполняется по его ключу.
cs1:email != "test@example.com"
Запрос выдаст события, для которых одновременно выполняются следующие условия:
-
Поле
cs1не содержит значениеtest@example.com. -
Поле
cs1Labelсодержит названиеemail.
Представленный запрос эквивалентен следующему запросу:
cs1 != "test@example.com" AND cs1Label = "email"
Пример обращения к полю по его семейству
Допустим, имеется поток событий из разных источников. Для событий некоторых источников в полях семейства cs (например, cs1, cs2, cs3) хранится адрес электронной почты пользователя, совершившего подозрительное действие. В соответствующих полях меток (например, cs1Label, cs2Label, cs3Label) этих событий хранится значение email. Известно, что часть событий от этих источников является тестовой и приходит от разных пользователей с одним и тем же электронным адресом test@example.com.
Требуется получить поток событий, для которых указаны электронные адреса пользователей, и исключить из него тестовые события. Ниже представлен RQL-запрос, построенный на основе представленного ранее синтаксиса. Обращение к настраиваемым полям универсальной модели события выполняется по их семейству cs.
cs:email != "test@example.com"
Запрос выдаст события, для которых одновременно выполняются следующие условия:
-
Ни одно из полей значений семейства
csне содержит значениеtest@example.com. -
Поле метки, соответствующее полю значений семейства
cs, содержит названиеemail.
Представленный запрос эквивалентен следующему запросу:
(cs1 != "test@example.com" AND cs1Label = "email") OR (cs2 != "test@example.com" AND cs2Label = "email") OR (cs3 != "test@example.com" AND cs3Label = "email") OR (cs4 != "test@example.com" AND cs4Label = "email") OR (cs5 != "test@example.com" AND cs5Label = "email") OR (cs6 != "test@example.com" AND cs6Label = "email")
Использование фильтров и ограничений по времени
После выполнения запроса в разделе Поиск можно отфильтровать возвращенные результаты с помощью кнопки Добавить фильтр. В окне добавления фильтра указывается поле, по которому проводится фильтрация, оператор сравнения и значение для сравнения.
Вместе с фильтрами удобно использовать функциональность ограничения запроса по времени. Это ограничение аналогично добавлению к запросу компонента, который ограничивает результаты запроса определенным временным интервалом.
Система воспринимает применяемые фильтры и ограничения по времени как дополнительные условия оператора WHERE, присоединяемые оператором AND. Переключатель Инвертировать (NOT) интерпретируется как оператор NOT.
Примеры использования фильтров и ограничения по времени
Пример 1
Вы ввели в строке поиска запрос tenantId LIKE 'n%' AND length(raw) > 10. Затем добавили фильтр по полю originalTimestamp c оператором = и значением 05.05.2023б 05:00:00. Последовательность этих действий равнозначна следующему запросу в строке поиска:
tenantId LIKE 'n%' AND length(raw) > 10 AND originalTimestamp = '2023.05.05 05:00:00'
Пример 2
Вы хотите ограничить результат запроса tenantId LIKE 'n%' AND length(raw) > 10 датами 01.01.2023 и 12.12.2023.
Для этого после выполнения запроса выберите значение Задать период в поле периода и укажите диапазон периода в полях справа. Эти действия аналогичны следующему запросу:
tenantId LIKE 'n%' AND length(raw) > 10 AND (timestamp >= 2023.01.01 AND timestamp <= 2023.12.12)
Таким образом, применение фильтров и ограничений по времени существенно упрощает создание поисковых запросов в системе.
Группировка (оператор GROUP BY)
Объединять результаты запроса по одному или нескольким полям можно с помощью оператора GROUP BY. С оператором GROUP BY могут применяться агрегатные функции: count(), sum(), avg(), max(), min().
SELECT <expression> GROUP BY field_name
При использовании оператора GROUP BY необходимо указать, к какой проекции будет применяться объединение. Например, запрос вида GROUP BY tenantID воспринимается системой как SELECT * GROUP BY tenantID, и поэтому некорректен. Корректный запрос с указанной проекцией имеет вид SELECT tenantID GROUP BY tenantID.
|
Сортировка возвращаемых записей (оператор ORDER BY)
Результаты запроса можно сортировать по любому полю с помощью оператора ORDER BY.
[SELECT <expression>] ORDER BY field_name [ASC | DESC]
Направление сортировки задается модификаторами:
-
ASC— по возрастанию; -
DESC— по убыванию.
Примеры сортировки
ORDER BY collectorId
Этот запрос выводит события с сортировкой по полю collectorId в порядке возрастания.
count()SELECT tenantId, count(*) ORDER BY tenantId ASC
Этот запрос возвращает события, упорядоченные по возрастанию значения в поле tenantId и указанием количества событий в каждой группе.
Заполнение пропусков в списке возвращаемых результатов (модификатор WITH FILL)
Чтобы заполнить пропуски в списке возвращаемых результатов при сортировке и группировке записей, используйте модификатор WITH FILL. Пропуски заполняются значениями с указанным шагом в указанном промежутке, либо по умолчанию.
WITH FILL можно применять только после имени столбца в ORDER BY. Допускается использование нескольких WITH FILL для разных столбцов.
|
SELECT <expression> ORDER BY field_name WITH FILL [FROM <expression> TO <expression>] STEP <expression>]]
Для случая с несколькими полями ORDER BY field2 WITH FILL, field1 WITH FILL порядок заполнения будет соответствовать порядку полей в секции ORDER BY.
|
Здесь:
-
field_name— имя поля, по которому происходит сортировка. -
FROM +<expression>+— начальное значение интервала, который будет заполнен. Если не указано, будет использовано наименьшее значение в полеfield_name. -
TO +<expression>+— конечное значение. Если не указано, будет использовано наибольшее значение в полеfield_name. -
STEP +<expression>+— шаг, с которым будет заполняться интервал. Если не указан, используется значение1.0для числовых типов, и 1 секунда для метки времени.
Значение параметра <expression> должен принадлежать к одному из следующих типов:
-
числовой литерал (целое или дробное число);
-
метка времени;
-
арифметическая операция с числами, меткой времени, интервалом или функцией;
-
функция с возвращаемым значением в виде числа или метки времени.
Примеры заполнения пропусков
SELECT toStartOfHour(timestamp) as ts, sum(cnt) as sum_cnt GROUP BY ts HAVING sum_cnt > 0 ORDER BY ts WITH FILL FROM now() - INTERVAL 1 day TO now() STEP INTERVAL 1 hour
Данный запрос выполняет следующую последовательность операций:
-
SELECT toStartOfHour(timestamp) as ts, sum(cnt) as sum_cnt: выбираются значения поляtimestamp, округленные до начала каждого часа, и вычисляется сумма поляcntдля каждой группы записей с одинаковым значениемts. -
GROUP BY ts: результаты группируются по полюts, которое представляет округленные значения. -
HAVING sum_cnt > 0: применяется фильтрация, чтобы оставить только те группы записей, у которых сумма поляcntбольше нуля. Это означает, что в результирующем наборе будут только записи, для которых существуют записи с положительными значениямиcntвнутри группы. -
ORDER BY ts WITH FILL FROM now() - INTERVAL 1 day TO now() STEP INTERVAL 1 hour: результаты сортируются по полюts. Здесь используется модификаторWITH FILL, который обеспечивает заполнение пропущенных интервалов с шагом в 1 час от текущего времени минус 1 день до текущего времени. То есть, если в исходных данных отсутствуют записи для определенных интервалов времени, то они будут включены в результирующий набор со значениями по умолчанию.
SELECT toStartOfHour(timestamp) as ts, sum(cnt) as sum_cnt GROUP BY ts HAVING sum_cnt > 0 AND ts >= now() - INTERVAL 1 day AND ts <= now() ORDER BY ts WITH FILL FROM now() - INTERVAL 1 day TO now() STEP INTERVAL 1 hour
Здесь выполняются те же самые операции, что и в первом запросе, но добавляется дополнительный фильтр в выражении HAVING и выражении AND. Этот фильтр ограничивает результаты только записями, у которых поле ts находится в интервале от текущего времени минус 1 день до текущего времени. Таким образом, данный запрос дополнительно фильтрует записи по временному интервалу перед применением сортировки и заполнением пропущенных интервалов.
В результате выполнения этого запроса будет получен отсортированный список, где каждая запись будет содержать округленное значение timestamp, сумму cnt для соответствующей группы записей, а также пропущенные интервалы времени со значениями по умолчанию, если таковые имеются. В результирующем наборе будут только записи, удовлетворяющие условиям фильтрации по сумме cnt и временному интервалу.
Фильтрация и обогащение данных (операторы JOIN и ENRICH)
Оператор JOIN служит для фильтрации данных в хранилище событий на основе записей из активных списков или событий из других хранилищ. Это позволяет детализировать результаты запросов на основе совпадений в значениях полей. При этом исходные данные из источника и данные из присоединяемого хранилища или активного списка рассматриваются как отдельные таблицы:
-
Левая таблица — источник данных, сформированный из выбранных хранилищ событий.
-
Правая таблица — присоединяемое хранилище событий или активный список.
В одном запросе можно использовать несколько операторов JOIN.
Для оптимальной производительности ClickHouse рекомендуется использовать не больше 3–4 JOIN в запросе.
|
Оператор ENRICH используется в комбинации с JOIN и позволяет обогащать эти результаты данными из активных списков.
JOIN в базовом поиске<JOIN_TYPE> JOIN `<right_table_name>` [AS <alias_name>] ON <storage_field> = `<right_table_name>`.<right_table_field>
ENRICH <target_field> = <source_field>
JOIN в расширенном поиске[SELECT <fields>] <JOIN_TYPE> JOIN `<right_table_name>` [AS <alias_name>] ON <storage_field> = `<right_table_name>`.<right_table_field>
ENRICH <target_field> = <source_field>
Здесь:
-
<fields>— список полей, которые будут включены в результат запроса. -
<JOIN_TYPE>— тип операцииJOIN:-
INNER JOINвыбирает строки, где присутствует совпадение по заданным условиям в обеих таблицах. -
LEFT JOINотображает все строки из левой таблицы, дополняя их данными из правой таблицы при наличии совпадений.
FULL JOINв текущей версии эквивалентенLEFT JOIN. -
-
<right_table_name>обозначает имя хранилища событий или активного списка, который используется в качестве правой таблицы для операцииJOIN.Имя хранилища событий или активного списка необходимо заключать в обратные апострофы. -
<alias_name>позволяет назначить псевдоним правой таблице, упрощая дальнейшую работу с запросом. Псевдоним не влияет на семантикуJOIN. -
<storage_field>— название поля события из текущего хранилища событий. -
<right_table_field>— название поля присоединяемого хранилища событий или активного списка. -
<target_field>— поле в записях событий, для которого будет выполнено обогащение. -
<source_field>— поле в активном списке, значения из которого используются для обогащения событий.
|
При обращении к полям таблицы без указания ее названия система автоматически начинает поиск поля в хранилищах событий, выбранных в качестве источника. Если поле в источнике не найдено, поиск поля продолжается в присоединяемом хранилище или активном списке. Обращение к полю в событиях текущего хранилища может выполняться одним из следующих способов:
Обращение к полю присоединяемого хранилища или активного списка может выполняться одним из следующих способов:
В условии объединения не поддерживается использование функций. |
Примеры использования JOIN
Пример 1. Использование JOIN в базовом поиске для фильтрации по активному списку
Пример запроса ниже демонстрирует использование INNER JOIN для фильтрации событий по списку угроз threat_list, исключая из результатов все события, не соответствующие IP-адресам, занесенным в этот список. Цель запроса — идентифицировать потенциально вредоносную активность, происходящую с IP-адресов, указанных в списке угроз.
INNER JOIN `threat_list` ON sourceIp = `threat_list`.ip_address
Этот запрос исключит из результата все события, источник которых не совпадает с IP-адресами, указанными в активном списке threat_list. Таким образом, анализ будет сосредоточен исключительно на событиях, источник которых был заранее классифицирован как потенциальная угроза. Поскольку объединение данных не происходит, в результатах запроса отображаются только поля исходных событий, соответствующих условиям фильтрации.
Пример 2. Использование JOIN в базовом поиске для фильтрации по нескольким хранилищам
INNER JOIN `storage_name_1` ON sourceIp = `storage_name_1`.ip
INNER JOIN `storage_name_2` ON userId = `storage_name_2`.user_id
Этот запрос сначала исключит из результата все события, у которых IP-адрес источника не совпадает со значениями поля ip в хранилище storage_name_1. Затем исключаются события, в которых поле userId не совпадает со значениями поля user_id в хранилище storage_name_2. Объединения данных не происходит, в результатах запроса отображаются только поля исходных событий, соответствующих условиям фильтрации.
Пример 3. Использование JOIN в продвинутом поиске для фильтрации по IP-адресу
Пример запроса ниже демонстрирует использование INNER JOIN для соединения данных событий с активным списком blacklist_ips по IP-адресу источника (sourceIp), с целью выявления событий с блокированными IP-адресами.
SELECT sourceIp, timestamp, action, ip_address
INNER JOIN `blacklist_ips` ON sourceIp = `blacklist_ips`.ip_address
Визуализация ожидаемого результата запроса:
| event.sourceIp | event.timestamp | event.action | blacklist_ips.ip_address |
|---|---|---|---|
192.0.2.1 |
2022-07-01T12:00:00Z |
Blocked |
192.0.2.1 |
192.0.2.2 |
2022-07-02T12:00:00Z |
Blocked |
192.0.2.2 |
В этой таблице отображаются события, произошедшие с IP-адресов, указанных в системе (event), и соответствующие IP-адресам в активном списке blacklist_ips. Выборка ограничивается событиями, источники которых находятся в списке блокировки. В результат попадают только поля, перечисленные после оператора SELECT.
Пример 4. Использование JOIN в продвинутом поиске для фильтрации по идентификатору пользователя
Оператор LEFT JOIN может быть использован для объединения данных о событиях входа в систему с информацией о пользователях из активного списка. Это позволяет получить расширенный контекст активности пользователей, включая отсутствие событий для определенных пользователей. В случаях, когда соответствующая запись в активном списке отсутствует, результатом объединения для данной записи события будут значения NULL в полях, предназначенных для данных из активного списка.
Пример запроса ниже демонстрирует использование LEFT JOIN для соединения событий с записями активного списка о пользователях user_info по идентификатору пользователя. Цель — включить в анализ всех пользователей, включая тех, по которым отсутствуют данные о событиях входа.
SELECT sourceIp, timestamp, ip_address, userName
LEFT JOIN `user_info` ON userId = `user_info`.user_id
Визуализация ожидаемого результата запроса:
| event.sourceIp | event.timestamp | user_info.ip_address | user_info.userName |
|---|---|---|---|
192.0.2.1 |
2022-07-01T12:00:00Z |
192.0.2.1 |
Sergey |
192.0.2.2 |
2022-07-02T12:00:00Z |
192.0.2.2 |
Ivan |
192.0.2.3 |
2022-07-03T12:00:00Z |
NULL |
NULL |
В этой таблице показаны все попытки входа пользователей, зафиксированные в системе (event), дополненные информацией о них из активного списка user_info. Последняя строка демонстрирует случай, когда в таблице событий имеются данные о входе с IP-адреса 192.0.2.3, но информация о пользователе, выполнившем вход, отсутствует в активном списке user_info.
Пример 5. Использование нескольких операторов JOIN в продвинутом поиске
SELECT src_ip, src_user_name, `storage_name_1`.correlation_severity, `storage_name_2`.user_email
INNER JOIN `storage_name_1` ON src_ip = `storage_name_1`.ip
INNER JOIN `storage_name_2` ON src_user_name = `storage_name_2`.user_name
Этот запрос сначала выбирает из результата все события, у которых IP-адрес источника совпадает со значениями поля ip в хранилище storage_name_1. Затем он исключает события, в которых поле userId не совпадает со значениями поля user_id в хранилище storage_name_2. В итоге выводятся только те поля всех хранилищ, которые указаны в блоке SELECT.
Работа с оператором ENRICH
Оператор ENRICH позволяет заменять или добавлять значения в полях событий на основе данных из присоединяемого хранилища или активного списка. ENRICH используется в связке с оператором JOIN и применяется только после того, как условие соединения, заданное в JOIN, будет выполнено. Таким образом, обогащение выполняется только для строк, соответствующих условию.
Оператор ENRICH позволяет обогащать данные из нескольких хранилищ или активных списков в рамках одного запроса. Для точного указания полей из конкретных правых таблиц следует использовать полное имя поля, включающее имя правой таблицы (хранилища событий или активного списка) и имя поля, разделенные точкой.
Примеры использования ENRICH
Обогащение данных события значениями из одного активного списка
ENRICHLEFT JOIN `user_info` ON userId = `user_info`.name
ENRICH id = `user_info`.age
В данном примере запрос обогащает значение поля id в записях событий значением поля age из активного списка user_info, если значение поля userId события совпадает с полем name в активном списке.
Обогащение данных события дополнительными данными из активных списков
Применение запросов с INNER JOIN и ENRICH в разделе Поиск позволяет обогатить данные событий дополнительными данными из активных списков при условии точного совпадения между записями событий и данными активного списка. В отличие от LEFT JOIN, INNER JOIN выбирает только те записи, для которых существуют совпадающие данные в обеих таблицах, и далее ENRICH используется для обогащения этих данных.
| Этот метод подходит для сценариев, где необходимо ограничить анализ событиями, которые имеют прямое соответствие в активных списках, и одновременно обогатить эти события дополнительной информацией из этих списков. |
INNER JOIN с ENRICHINNER JOIN `device_info` ON deviceId = `device_info`.device_id
ENRICH deviceType = `device_info`.type
Обращение к нескольким активным спискам
LEFT JOIN с ENRICHSELECT id
LEFT JOIN `admin_list` ON userId = `admin_list`.user_id
LEFT JOIN `security_events` ON eventCode = `security_events`.code
ENRICH adminName = `admin_list`.name, securityLevel = `security_events`.level
В данном примере запрос обогащает данные событий, используя информацию из двух разных активных списков — admin_list и security_events. Оператор ENRICH применяется для замены (или добавления, если такого поля нет) в записях событий: значения поля adminName на основании данных об имени администратора из списка admin_list и значения поля securityLevel на основании уровня безопасности из списка security_events. Оба обогащения основываются на совпадении условий: userId с user_id для списка администраторов и eventCode с code для событий.
Преобразование значений с помощью условной логики (функция if())
Функция if() позволяет отображать в результатах запроса преобразованные значения вместо исходных. Это удобно для категоризации результатов или более наглядного представления данных на дашбордах и в отчетах.
SELECT if(cond, then, else) AS field1,
...
[GROUP BY field1
ORDER BY field1]
Здесь:
-
cond— проверяемое условие; -
then— выражение, результат которого функция вернет, еслиcondистинно; -
else— выражение, результат которого возвращается, еслиcondложно.
В качестве else можно использовать вложенную функцию if(). Для компактного представления нескольких условий можно использовать функцию multiIf():
multiIf(cond_1, then_1, cond_2, then_2, ... else)
Здесь:
-
cond_N— условие. -
then_N— результат функции при выполнении условияcond_N. -
else— результат функции, если ни одно из условий не выполнено.
Пример использования условных функций
Рассмотрим запрос, который возвращает данные, сгруппированные по нескольким полям, вместе с количеством записей:
SELECT
ipAddress,
deviceName,
status,
count(*) AS total_checks
GROUP BY ipAddress, deviceName, status
ORDER BY ipAddress, deviceName, status;
Визуализация ожидаемого результата запроса:
| ipAddress | deviceName | status | total_checks |
|---|---|---|---|
192.0.2.0 |
DeviceA |
PASSED |
597 |
192.0.2.0 |
DeviceA |
NOT_PASSED |
543 |
192.0.2.0 |
DeviceA |
NOT_COMPLETED |
1116 |
192.0.2.1 |
DeviceB |
PASSED |
56 |
192.0.2.1 |
DeviceB |
NOT_PASSED |
68 |
192.0.2.1 |
DeviceB |
NOT_COMPLETED |
147 |
Чтобы отобразить в столбце status значения на русском языке, преобразуем запрос следующим образом:
SELECT
ipAddress,
deviceName,
if(
equals(status, "PASSED"),
"Пройденные",
if(
equals(status, "NOT_PASSED"),
"Непройденные",
if(
equals(status, "NOT_COMPLETED"),
"Неприменимые результаты",
"-"
)
)
) AS status,
count(*) AS total_checks
GROUP BY ipAddress, deviceName, status
ORDER BY ipAddress, deviceName, status;
Здесь равенство поля status и указанного значения проверяется с помощью функции equals(). Если equals() возвращает ложное значение, происходит переход ко вложенному if().
Визуализация ожидаемого результата запроса:
| ipAddress | deviceName | status | total_checks |
|---|---|---|---|
192.0.2.0 |
DeviceA |
Пройденные |
597 |
192.0.2.0 |
DeviceA |
Непройденные |
543 |
192.0.2.0 |
DeviceA |
Неприменимые результаты |
1116 |
192.0.2.1 |
DeviceB |
Пройденные |
56 |
192.0.2.1 |
DeviceB |
Непройденные |
68 |
192.0.2.1 |
DeviceB |
Неприменимые результаты |
147 |
Чтобы упростить запись, используем функцию multiIf():
SELECT
ipAddress,
deviceName,
multiIf(
equals(status, "PASSED"), "Пройденные",
equals(status, "NOT_PASSED"), "Непройденные",
equals(status, "NOT_COMPLETED"), "Неприменимые результаты",
"-"
) AS status,
count(*) AS total_checks
GROUP BY ipAddress, deviceName, status
ORDER BY ipAddress, deviceName, status;
Максимальное количество и пропуск возвращаемых записей (операторы LIMIT и OFFSET)
По умолчанию в списке результатов поискового запроса отображается 500 записей. Однако вы можете ограничить количество возвращаемых записей с помощью оператора LIMIT.
LIMIT <m> [OFFSET <n>]
Например, чтобы ограничить выдачу двадцатью записями, используется запрос:
LIMIT 20
Также можно добавить команду OFFSET, которая указывает, сколько строк необходимо пропустить перед началом вывода. Это полезно, если вы хотите начать вывод данных с определенной строки. Например, чтобы пропустить первые 10 строк и вывести следующие 20, используется запрос:
LIMIT 20 OFFSET 10
Была ли полезна эта страница?