Spec-Zone.ru › Elasticsearch 8
›Руководство по Elasticsearch [8.17] ›ES|QL

Начало работы с запросами ES|QL

В этом руководстве показано, как использовать ES|QL для запроса и агрегации данных.

Это начальное руководство также доступно в виде интерактивной Python-записной книжки в репозитории GitHub в elasticsearch-labs.

Предварительные требования

Чтобы следовать запросам в этом руководстве, вы можете настроить собственное развертывание или использовать публичную демонстрационную среду ES|QL от Elastic.

Сначала импортируйте примерные данные. В Kibana откройте главное меню и выберите Инструменты разработчика. Запустите следующие два запроса:

resp = client.indices.create(
    index="sample_data",
    mappings={
        "properties": {
            "client_ip": {
                "type": "ip"
            },
            "message": {
                "type": "keyword"
            }
        }
    },
)
print(resp)

resp1 = client.bulk(
    index="sample_data",
    operations=[
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T12:15:03.360Z",
            "client_ip": "172.21.2.162",
            "message": "Connected to 10.1.0.3",
            "event_duration": 3450233
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T12:27:28.948Z",
            "client_ip": "172.21.2.113",
            "message": "Connected to 10.1.0.2",
            "event_duration": 2764889
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T13:33:34.937Z",
            "client_ip": "172.21.0.5",
            "message": "Disconnected",
            "event_duration": 1232382
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T13:51:54.732Z",
            "client_ip": "172.21.3.15",
            "message": "Connection error",
            "event_duration": 725448
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T13:52:55.015Z",
            "client_ip": "172.21.3.15",
            "message": "Connection error",
            "event_duration": 8268153
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T13:53:55.832Z",
            "client_ip": "172.21.3.15",
            "message": "Connection error",
            "event_duration": 5033755
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-10-23T13:55:01.543Z",
            "client_ip": "172.21.3.15",
            "message": "Connected to 10.1.0.1",
            "event_duration": 1756467
        }
    ],
)
print(resp1)
response = client.indices.create(
  index: 'sample_data',
  body: {
    mappings: {
      properties: {
        client_ip: {
          type: 'ip'
        },
        message: {
          type: 'keyword'
        }
      }
    }
  }
)
puts response

response = client.bulk(
  index: 'sample_data',
  body: [
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T12:15:03.360Z',
      client_ip: '172.21.2.162',
      message: 'Connected to 10.1.0.3',
      event_duration: 3_450_233
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T12:27:28.948Z',
      client_ip: '172.21.2.113',
      message: 'Connected to 10.1.0.2',
      event_duration: 2_764_889
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T13:33:34.937Z',
      client_ip: '172.21.0.5',
      message: 'Disconnected',
      event_duration: 1_232_382
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T13:51:54.732Z',
      client_ip: '172.21.3.15',
      message: 'Connection error',
      event_duration: 725_448
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T13:52:55.015Z',
      client_ip: '172.21.3.15',
      message: 'Connection error',
      event_duration: 8_268_153
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T13:53:55.832Z',
      client_ip: '172.21.3.15',
      message: 'Connection error',
      event_duration: 5_033_755
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-10-23T13:55:01.543Z',
      client_ip: '172.21.3.15',
      message: 'Connected to 10.1.0.1',
      event_duration: 1_756_467
    }
  ]
)
puts response
const response = await client.indices.create({
  index: "sample_data",
  mappings: {
    properties: {
      client_ip: {
        type: "ip",
      },
      message: {
        type: "keyword",
      },
    },
  },
});
console.log(response);

const response1 = await client.bulk({
  index: "sample_data",
  operations: [
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T12:15:03.360Z",
      client_ip: "172.21.2.162",
      message: "Connected to 10.1.0.3",
      event_duration: 3450233,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T12:27:28.948Z",
      client_ip: "172.21.2.113",
      message: "Connected to 10.1.0.2",
      event_duration: 2764889,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T13:33:34.937Z",
      client_ip: "172.21.0.5",
      message: "Disconnected",
      event_duration: 1232382,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T13:51:54.732Z",
      client_ip: "172.21.3.15",
      message: "Connection error",
      event_duration: 725448,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T13:52:55.015Z",
      client_ip: "172.21.3.15",
      message: "Connection error",
      event_duration: 8268153,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T13:53:55.832Z",
      client_ip: "172.21.3.15",
      message: "Connection error",
      event_duration: 5033755,
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-10-23T13:55:01.543Z",
      client_ip: "172.21.3.15",
      message: "Connected to 10.1.0.1",
      event_duration: 1756467,
    },
  ],
});
console.log(response1);
PUT sample_data
{
  "mappings": {
    "properties": {
      "client_ip": {
        "type": "ip"
      },
      "message": {
        "type": "keyword"
      }
    }
  }
}

PUT sample_data/_bulk
{"index": {}}
{"@timestamp": "2023-10-23T12:15:03.360Z", "client_ip": "172.21.2.162", "message": "Connected to 10.1.0.3", "event_duration": 3450233}
{"index": {}}
{"@timestamp": "2023-10-23T12:27:28.948Z", "client_ip": "172.21.2.113", "message": "Connected to 10.1.0.2", "event_duration": 2764889}
{"index": {}}
{"@timestamp": "2023-10-23T13:33:34.937Z", "client_ip": "172.21.0.5", "message": "Disconnected", "event_duration": 1232382}
{"index": {}}
{"@timestamp": "2023-10-23T13:51:54.732Z", "client_ip": "172.21.3.15", "message": "Connection error", "event_duration": 725448}
{"index": {}}
{"@timestamp": "2023-10-23T13:52:55.015Z", "client_ip": "172.21.3.15", "message": "Connection error", "event_duration": 8268153}
{"index": {}}
{"@timestamp": "2023-10-23T13:53:55.832Z", "client_ip": "172.21.3.15", "message": "Connection error", "event_duration": 5033755}
{"index": {}}
{"@timestamp": "2023-10-23T13:55:01.543Z", "client_ip": "172.21.3.15", "message": "Connected to 10.1.0.1", "event_duration": 1756467}

Набор данных, используемый в этом руководстве, предварительно загружен в публичную демонстрационную среду ES|QL от Elastic. Перейдите по ссылке ela.st/ql, чтобы начать работу.

Запуск запроса ES|QL

В Kibana вы можете использовать Консоль или Discover для запуска запросов ES|QL:

Чтобы начать работу с ES|QL в Консоли, откройте главное меню и выберите Инструменты разработчика.

Общая структура запроса ES|QL — это:

POST /_query?format=txt
{
  "query": """

  """
}

Введите фактический запрос ES|QL между двумя наборами тройных кавычек. Например:

POST /_query?format=txt
{
  "query": """
FROM sample_data
  """
}

Чтобы начать работу с ES|QL в Discover, откройте главное меню и выберите Discover. Далее выберите Попробовать ES|QL из строки меню приложения.

Отрегулируйте фильтр времени так, чтобы он включал временные метки в примере данных (23 октября 2023 года).

После переключения в режим ES|QL строка запроса отображает пример запроса. Вы можете заменить этот запрос запросами из этого руководства по началу работы.

Для облегчения написания запросов автодополнение предлагает варианты с возможными командами и функциями:

esql kibana auto complete

Вы можете настроить высоту редактора, перетащив его нижнюю границу.

Ваш первый запрос ES|QL

Каждый запрос ES|QL начинается с команды источника. Команда источника генерирует таблицу, как правило, с данными из Elasticsearch.

A source command producing a table from Elasticsearch

Команда FROM возвращает таблицу с документами из потока данных, индекса или псевдонима. Каждая строка в результирующей таблице представляет собой документ. Этот запрос возвращает до 1000 документов из индекса sample_data:

FROM sample_data

Каждый столбец соответствует полю и может быть доступен по имени этого поля.

Ключевые слова ES|QL регистронезависимы. Следующий запрос идентичен предыдущему:

from sample_data

Команды обработки

Команда источника может быть дополнена одной или несколькими командами обработки, разделенными символом "|" (pipe): |. Команды обработки изменяют входную таблицу, добавляя, удаляя или изменяя строки и столбцы. Команды обработки могут выполнять фильтрацию, проекцию, агрегацию и многое другое.

A processing command changing an input table

Например, вы можете использовать команду LIMIT, чтобы ограничить количество возвращаемых строк, максимум до 10 000 строк:

FROM sample_data
| LIMIT 3

Для лучшей читаемости вы можете поместить каждую команду на отдельную строку. Однако это необязательно. Следующий запрос идентичен предыдущему:

FROM sample_data | LIMIT 3

Сортировка таблицы

A processing command sorting an input table

Еще одной командой обработки является команда SORT. По умолчанию строки, возвращаемые FROM, не имеют определенного порядка сортировки. Используйте команду SORT, чтобы отсортировать строки по одному или нескольким столбцам:

FROM sample_data
| SORT @timestamp DESC

Запрос данных

Используйте команду WHERE для запроса данных. Например, чтобы найти все события с продолжительностью более 5 мс:

FROM sample_data
| WHERE event_duration > 5000000

WHERE поддерживает несколько операторов. Например, вы можете использовать LIKE, чтобы выполнить запрос с подстановкой по столбцу message:

FROM sample_data
| WHERE message LIKE "Connected*"

Дополнительные команды обработки

Существует много других команд обработки, таких как KEEP и DROP для сохранения или удаления столбцов, ENRICH для обогащения таблицы данными из индексов в Elasticsearch, а также DISSECT и GROK для обработки данных. Обратитесь к командам обработки для обзора всех команд обработки.

Цепочки команд обработки

Вы можете объединять команды обработки, разделяя их символом "|": |. Каждая команда обработки работает с выходной таблицей предыдущей команды. Результатом запроса является таблица, созданная последней командой обработки.

Processing commands can be chained

В следующем примере сначала сортируется таблица по @timestamp, а затем результат ограничивается 3 строками:

FROM sample_data
| SORT @timestamp DESC
| LIMIT 3

Порядок команд обработки важен. Сначала ограничение результата 3 строками, а затем сортировка этих 3 строк, скорее всего, приведет к результату, отличающемуся от примера, где сортировка выполняется до ограничения.

Вычисление значений

Используйте команду EVAL для добавления столбцов в таблицу с вычисленными значениями. Например, следующий запрос добавляет столбец duration_ms. Значения в столбце вычисляются путем деления event_duration на 1 000 000. Другими словами: event_duration преобразуется из наносекунд в миллисекунды.

FROM sample_data
| EVAL duration_ms = event_duration/1000000.0

EVAL поддерживает несколько функций. Например, чтобы округлить число до ближайшего числа с заданным количеством знаков, используйте функцию ROUND:

FROM sample_data
| EVAL duration_ms = ROUND(event_duration/1000000.0, 1)

Вычисление статистики

ES|QL можно использовать не только для запроса данных, но и для агрегации данных. Используйте команду STATS для вычисления статистики. Например, медианной продолжительности:

FROM sample_data
| STATS median_duration = MEDIAN(event_duration)

Вы можете вычислить несколько статистик одной командой:

FROM sample_data
| STATS median_duration = MEDIAN(event_duration), max_duration = MAX(event_duration)

Используйте BY для группировки вычисленных статистик по одному или нескольким столбцам. Например, для вычисления медианной продолжительности по IP-адресу клиента:

FROM sample_data
| STATS median_duration = MEDIAN(event_duration) BY client_ip

Доступ к столбцам

Вы можете получить доступ к столбцам по их имени. Если имя содержит специальные символы, нужно использовать кавычки с обратными апострофами (`).

Присвоение явного имени столбцу, созданному с помощью EVAL или STATS, является необязательным. Если вы не укажете имя, новое имя столбца будет равно выражению функции. Например:

FROM sample_data
| EVAL event_duration/1000000.0

В этом запросе EVAL добавляет новый столбец под названием event_duration/1000000.0. Поскольку его имя содержит специальные символы, для доступа к этому столбцу нужно использовать кавычки:

FROM sample_data
| EVAL event_duration/1000000.0
| STATS MEDIAN(`event_duration/1000000.0`)

Создание гистограммы

Для отслеживания статистики во времени ES|QL позволяет создавать гистограммы с использованием функции BUCKET. BUCKET создает удобные для восприятия размеры ведер и возвращает значение для каждой строки, соответствующее ведру, в которое попадает эта строка.

Комбинируйте BUCKET с STATS для создания гистограммы. Например, для подсчета количества событий в час:

FROM sample_data
| STATS c = COUNT(*) BY bucket = BUCKET(@timestamp, 24, "2023-10-23T00:00:00Z", "2023-10-23T23:59:59Z")

Или медианной продолжительности в час:

FROM sample_data
| KEEP @timestamp, event_duration
| STATS median_duration = MEDIAN(event_duration) BY bucket = BUCKET(@timestamp, 24, "2023-10-23T00:00:00Z", "2023-10-23T23:59:59Z")

Обогащение данных

ES|QL позволяет обогатить таблицу данными из индексов в Elasticsearch, используя команду ENRICH.

esql enrich

Прежде чем вы сможете использовать ENRICH, вам сначала необходимо создать и выполнить политику обогащения.

Следующие запросы создают и выполняют политику под названием clientip_policy. Политика связывает IP-адрес со средой ("Разработка", "QA" или "Производство"):

resp = client.indices.create(
    index="clientips",
    mappings={
        "properties": {
            "client_ip": {
                "type": "keyword"
            },
            "env": {
                "type": "keyword"
            }
        }
    },
)
print(resp)

resp1 = client.bulk(
    index="clientips",
    operations=[
        {
            "index": {}
        },
        {
            "client_ip": "172.21.0.5",
            "env": "Development"
        },
        {
            "index": {}
        },
        {
            "client_ip": "172.21.2.113",
            "env": "QA"
        },
        {
            "index": {}
        },
        {
            "client_ip": "172.21.2.162",
            "env": "QA"
        },
        {
            "index": {}
        },
        {
            "client_ip": "172.21.3.15",
            "env": "Production"
        },
        {
            "index": {}
        },
        {
            "client_ip": "172.21.3.16",
            "env": "Production"
        }
    ],
)
print(resp1)

resp2 = client.enrich.put_policy(
    name="clientip_policy",
    match={
        "indices": "clientips",
        "match_field": "client_ip",
        "enrich_fields": [
            "env"
        ]
    },
)
print(resp2)

resp3 = client.enrich.execute_policy(
    name="clientip_policy",
    wait_for_completion=False,
)
print(resp3)
response = client.indices.create(
  index: 'clientips',
  body: {
    mappings: {
      properties: {
        client_ip: {
          type: 'keyword'
        },
        env: {
          type: 'keyword'
        }
      }
    }
  }
)
puts response

response = client.bulk(
  index: 'clientips',
  body: [
    {
      index: {}
    },
    {
      client_ip: '172.21.0.5',
      env: 'Development'
    },
    {
      index: {}
    },
    {
      client_ip: '172.21.2.113',
      env: 'QA'
    },
    {
      index: {}
    },
    {
      client_ip: '172.21.2.162',
      env: 'QA'
    },
    {
      index: {}
    },
    {
      client_ip: '172.21.3.15',
      env: 'Production'
    },
    {
      index: {}
    },
    {
      client_ip: '172.21.3.16',
      env: 'Production'
    }
  ]
)
puts response

response = client.enrich.put_policy(
  name: 'clientip_policy',
  body: {
    match: {
      indices: 'clientips',
      match_field: 'client_ip',
      enrich_fields: [
        'env'
      ]
    }
  }
)
puts response

response = client.enrich.execute_policy(
  name: 'clientip_policy',
  wait_for_completion: false
)
puts response
const response = await client.indices.create({
  index: "clientips",
  mappings: {
    properties: {
      client_ip: {
        type: "keyword",
      },
      env: {
        type: "keyword",
      },
    },
  },
});
console.log(response);

const response1 = await client.bulk({
  index: "clientips",
  operations: [
    {
      index: {},
    },
    {
      client_ip: "172.21.0.5",
      env: "Development",
    },
    {
      index: {},
    },
    {
      client_ip: "172.21.2.113",
      env: "QA",
    },
    {
      index: {},
    },
    {
      client_ip: "172.21.2.162",
      env: "QA",
    },
    {
      index: {},
    },
    {
      client_ip: "172.21.3.15",
      env: "Production",
    },
    {
      index: {},
    },
    {
      client_ip: "172.21.3.16",
      env: "Production",
    },
  ],
});
console.log(response1);

const response2 = await client.enrich.putPolicy({
  name: "clientip_policy",
  match: {
    indices: "clientips",
    match_field: "client_ip",
    enrich_fields: ["env"],
  },
});
console.log(response2);

const response3 = await client.enrich.executePolicy({
  name: "clientip_policy",
  wait_for_completion: "false",
});
console.log(response3);
PUT clientips
{
  "mappings": {
    "properties": {
      "client_ip": {
        "type": "keyword"
      },
      "env": {
        "type": "keyword"
      }
    }
  }
}

PUT clientips/_bulk
{ "index" : {}}
{ "client_ip": "172.21.0.5", "env": "Development" }
{ "index" : {}}
{ "client_ip": "172.21.2.113", "env": "QA" }
{ "index" : {}}
{ "client_ip": "172.21.2.162", "env": "QA" }
{ "index" : {}}
{ "client_ip": "172.21.3.15", "env": "Production" }
{ "index" : {}}
{ "client_ip": "172.21.3.16", "env": "Production" }

PUT /_enrich/policy/clientip_policy
{
  "match": {
    "indices": "clientips",
    "match_field": "client_ip",
    "enrich_fields": ["env"]
  }
}

PUT /_enrich/policy/clientip_policy/_execute?wait_for_completion=false

В демо-среде по адресу ela.st/ql уже создана и выполнена политика обогащения под названием clientip_policy. Политика связывает IP-адрес со средой ("Разработка", "QA" или "Производство").

После создания и выполнения политики вы можете использовать её с командой ENRICH:

FROM sample_data
| KEEP @timestamp, client_ip, event_duration
| EVAL client_ip = TO_STRING(client_ip)
| ENRICH clientip_policy ON client_ip WITH env

Вы можете использовать новый столбец env, добавленный командой ENRICH, в последующих командах. Например, чтобы вычислить медианную продолжительность по каждой среде:

FROM sample_data
| KEEP @timestamp, client_ip, event_duration
| EVAL client_ip = TO_STRING(client_ip)
| ENRICH clientip_policy ON client_ip WITH env
| STATS median_duration = MEDIAN(event_duration) BY env

Для получения дополнительной информации об обогащении данных с помощью ES|QL, обратитесь к разделу Обогащение данных.

Обработка данных

Ваши данные могут содержать неструктурированные строки, которые вы хотите структурировать, чтобы упростить анализ данных. Например, пример данных содержит сообщения журнала, такие как:

"Connected to 10.1.0.3"

Извлекая IP-адрес из этих сообщений, вы можете определить, какой IP-адрес принял наибольшее количество подключений клиентов.

Для структурирования неструктурированных строк во время запроса вы можете использовать команды ES|QL DISSECT и GROK. DISSECT работает, разбивая строку с использованием шаблона на основе разделителя. GROK работает аналогично, но использует регулярные выражения. Это делает GROK более мощным, но, как правило, также медленнее.

В этом случае регулярные выражения не нужны, так как шаблон message простой: "Connected to ", за которым следует IP-адрес сервера. Чтобы соответствовать этой строке, можно использовать следующую команду DISSECT:

FROM sample_data
| DISSECT message "Connected to %{server_ip}"

Это добавляет столбец server_ip к строкам, у которых message соответствует этому шаблону. Для других строк значение server_ip равно null.

Вы можете использовать новый столбец server_ip, добавленный командой DISSECT, в последующих командах. Например, чтобы определить, сколько подключений принял каждый сервер:

FROM sample_data
| WHERE STARTS_WITH(message, "Connected to")
| DISSECT message "Connected to %{server_ip}"
| STATS COUNT(*) BY server_ip

Для получения дополнительной информации об обработке данных с помощью ES|QL, обратитесь к разделу Обработка данных с помощью DISSECT и GROK.

Дополнительная информация

Для получения дополнительной информации об ES|QL, обратитесь к Справочнику по ES|QL и Использование ES|QL.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/esql-getting-started.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API