﻿---
title: Using ES|QL
description: Step-by-step tutorial for querying data with Elasticsearch Query Language (ES|QL) in Discover using piped commands to filter, transform, and aggregate data with sample data and visualizations.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/discover/try-esql
products:
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Generally available
---

# Using ES|QL
Elasticsearch Query Language (ES|QL) helps you explore and analyze your Elasticsearch data directly in **Discover**, without a [data view](/elastic/docs-content/pull/5143/explore-analyze/discover/discover-get-started#find-the-data-you-want-to-use). ES|QL uses a piped syntax where you chain commands together to filter, transform, and aggregate data without needing to switch between different query interfaces. This tutorial walks you through querying sample data with ES|QL, from basic field selection to complex filtering and visualization.

## Prerequisites

- The `enableESQL` setting must be enabled in Kibana's **Advanced Settings** (enabled by default).
- You must have data in Elasticsearch.
  The examples on this page use the Kibana sample web logs to explore data and create visualizations. You can install sample data by following [Add sample data](/elastic/docs-content/pull/5143/explore-analyze#gs-get-data-into-kibana).

<tip>
  For the complete ES|QL documentation, including all supported commands, functions, and operators, refer to the [ES|QL reference](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/query-languages/esql/esql-syntax-reference). For a more detailed overview of ES|QL in Kibana, refer to [Use ES|QL in Kibana](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/query-filter/languages/esql-kibana).
</tip>


## Get started with ES|QL in Discover

1. Go to **Discover**.
2. Select `code` **ES|QL** or **Try ES|QL** from the application menu.
   <tip>
   If you've entered a KQL or Lucene query in the default mode of Discover, it automatically converts to ES|QL.
   </tip>
   Let’s say we want to find out what operating system users have and how much RAM is on their machine.
3. Set the time range to **Last 7 days**.
4. Copy the following query. To make queries more readable, you can put each processing command on a new line.
   ```esql
   FROM kibana_sample_data_logs 
   | KEEP machine.os, machine.ram 
   ```
   <note>
   ES|QL keywords are not case sensitive.
   </note>
5. Click **▶Run**.
   ![An image of the query result](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/kibana-esql-machine-os-ram.png)

Let’s add `geo.dest` to our query to find out the geographical destination of the visits and limit the results.
1. Copy the query below:
   ```esql
   FROM kibana_sample_data_logs
   | KEEP machine.os, machine.ram, geo.dest
   | LIMIT 10
   ```
2. Click **▶Run** again. You can notice that the table is now limited to 10 results. The visualization also updated automatically based on the query, and broke down the data for you.
   <note>
   When you don’t specify any specific fields to retain using `KEEP`, the visualization isn’t broken down automatically. Instead, an additional option appears above the visualization and lets you select a field manually.
   </note>
   ![An image of the extended query result](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/kibana-esql-limit.png)

We will now take it a step further to sort the data by machine RAM and filter out the `GB` destination.
1. Copy the query below:
   ```esql
   FROM kibana_sample_data_logs
   | KEEP machine.os, machine.ram, geo.dest
   | SORT machine.ram desc
   | WHERE geo.dest != "GB"
   | LIMIT 10
   ```
2. Click **▶Run** again. The table and visualization no longer show results for which the `geo.dest` field value is "GB", and the results are now sorted in descending order in the table based on the `machine.ram` field.
   ![An image of the full query result](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/kibana-esql-full-query.png)
3. Click **Save** to save the query and visualization to a dashboard.


## Edit the ES|QL visualization

You can make changes to the visualization by clicking the pencil icon. This opens additional settings that let you adjust the chart type, axes, breakdown, colors, and information displayed to your liking. If you’re not sure which route to go, check one of the suggestions available in the visualization editor.
If you’d like to keep the visualization and add it to a dashboard, you can save it using the floppy disk icon.

## ES|QL and time series data

By default, ES|QL identifies time series data when an index contains a `@timestamp` field. This enables the time range selector and visualization options for your query.
If your index doesn’t have an explicit `@timestamp` field, but has a different time field, you can still enable the time range selector and visualization options by calling the `?_tstart` and `?_tend` parameters in your query.
For example, the eCommerce sample data set doesn’t have a `@timestamp` field, but has an `order_date` field.
By default, when querying this data set, time series capabilities aren’t active. No visualization is generated and the time picker is disabled.
```esql
FROM kibana_sample_data_ecommerce
| KEEP customer_first_name, email, products._id.keyword
```

![ESQL query without time series capabilities enabled](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/kibana-esql-no-time-series.png)

While still querying the same data set, by adding the `?_tstart` and `?_tend` parameters based on the `order_date` field, **Discover** enables times series capabilities.
```esql
FROM kibana_sample_data_ecommerce
| WHERE order_date >= ?_tstart and order_date <= ?_tend
```

![ESQL query with a custom time field enabled](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/kibana-esql-custom-time-series.png)


## Create and edit lookup indices from queries

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Preview since 9.2
</applies-to>

In **Discover**, LOOKUP JOIN commands include interactive options that let you create or edit lookup indices directly from the editor.
<note>
  This section describes how to use the Kibana UI to create and edit lookup indices. You can also create and manage indices using the Elasticsearch APIs for [version 9](https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-create) and [Serverless](https://www.elastic.co/docs/api/doc/elasticsearch-serverless/operation/operation-indices-create).
</note>


### Create a lookup index from the editor

You can create a lookup index directly from the ES|QL editor. To populate this index, you can type in data manually or upload a CSV file up to 500 MB.
To create lookup indices, you need the [`create_index`](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/elasticsearch/security-privileges#privileges-list-indices) Elasticsearch privilege on the corresponding pattern.
1. In your ES|QL query, add a `LOOKUP JOIN` command. For example:
   ```esql
   FROM kibana_sample_data_logs
   | LOOKUP JOIN
   ```
   Add a space after the command. The editor suggests existing lookup indices and offers to create one. You can also type an index name in your query. If it doesn't exist, the editor suggests creating it.
2. Select the **Create lookup index** suggestion that appears in the autocomplete menu.
3. Define a name for the lookup index.
   - The name must not contain spaces or any of the following characters: `\`, `/`, `*`, `?`, `<`, `>`, `|`, `:`, and `#`.
- The name must not start with `-`, `_`, or `+`.
4. Provide data for the lookup index. You can either:
   - **Upload a CSV file up to 500 MB**. When you upload a file, you can preview its data, inspect its contents, and review any detected issues before importing it. Refer to [Using ES|QL > Load data into a lookup index from a CSV file](#esql-lookup-index-from-file) for more details.
- **Add data manually**. You can add fields and populate data directly. When adding a field, you must set its name and [data type](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/elasticsearch/mapping-reference/field-data-types).
  <note>
  Some Elasticsearch data types aren't supported in Kibana.
  </note>
- **Using a combination of both methods**. You can upload a file after adding data manually, and edit or expand the data imported from a file.
5. Check your index and its data. You can explore your index using the search field, or open it in a new Discover session by selecting **Open in Discover**. If you choose to open it in Discover, a new browser tab opens with a prefilled ES|QL query on the index.
6. **Save** any unsaved changes, then **Close** the index editor to return to your query.

Your new index is automatically added to your query. You can then specify the field to join using `ON <field_to_join>`.

##### Load data into a lookup index from a CSV file

When you are editing a lookup index from the ES|QL editor, you can add data to it by uploading CSV files up to 500 MB.
<applies-switch>
  <applies-item title="{ serverless:, stack: ga 9.3+ }" applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Generally available since 9.3">
    1. Drag the files you want to upload from your computer. You can add several files at a time and can repeat the operation multiple times.
       <note>
       If your index has unsaved changes, a message informs you that these changes will be lost. To keep those changes, cancel the upload and save your index, then start a new upload.
       </note>
    2. Preview the data for each file you're importing, then select **Continue**. If issues are detected, a message appears with more details. Typical issues include differences between the fields of the index and those of the imported files.
       - New fields coming from imported files will be added to the index.
    - Fields that exist in the index but are missing from the imported file will be kept but not filled with any data.
    3. Review and adjust the field names and data types to match the needs of your lookup index. After the import, you can no longer edit them.
    4. Select **Import** to validate the configuration and proceed with the import, then **Finish** to finalize the operation and return to the lookup index.
    Data coming from the files is appended to the index, and the index is automatically saved.
  </applies-item>

  <applies-item title="stack: ga =9.2" applies-to="Elastic Stack: Generally available in 9.2">
    1. Select `download` **Upload file**.
    2. Select the CSV file to import on your machine. You can select several files to import at once.
       <note>
       If your index has unsaved changes, a message informs you that these changes will be lost. To keep those changes, cancel the upload and save your index, then select `download` **Upload file** again.
       </note>
    3. Preview the data for each file you're importing. Field data types are automatically detected and set. If issues are detected, a **File issues** tab with more details appears before you validate the import. Common issues include differences between the fields in the index and in the imported files.
       - New fields coming from imported files will be added to the index.
    - Fields that exist in the index but are missing from the imported file will be kept but not filled with any data.
    4. Select **Import** to finalize the operation.
    Data coming from the files is appended to the index, and the index is automatically saved.
  </applies-item>
</applies-switch>


### View or edit a lookup index from the editor

You can view and modify existing lookup indices referenced in an ES|QL query directly from the editor, depending on your privileges:
- To edit lookup indices, you need the [`write`](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/elasticsearch/security-privileges#privileges-list-indices) Elasticsearch privilege.
- To view lookup indices in read-only mode, you need the [`view_index_metadata`](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/elasticsearch/security-privileges#privileges-list-indices) Elasticsearch privilege.

To view or edit an index:
1. In the ES|QL query, hover over the lookup index name.
2. Select the **Edit lookup index** or **View lookup index** option that appears. A flyout showing the index appears.
3. Depending on your permissions and needs, explore or edit the index. When editing the index, you have the same options described in [Using ES|QL > Create a lookup index from the editor](#create-lookup-esql).
   <note>
   Editing a lookup index affects all ES|QL queries that reference it. Make sure that your changes are compatible with existing queries that use this index.
   </note>
4. If you made changes, select **Save** before closing the flyout.


### Reset the lookup index configuration

At any time, you can delete all the index data and fields.
<applies-switch>
  <applies-item title="{ serverless:, stack: ga 9.3+ }" applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Generally available since 9.3">
    1. Select all the index data using the checkbox in the header of the table.
    2. Select **Delete selected** from the contextual menu that appears upon selecting entries.
    3. Once all entries are deleted, a **Reset index** button appears. Select it to remove all fields configured in the index.
    The lookup index is fully reset and saved automatically.
  </applies-item>

  <applies-item title="stack: ga =9.2" applies-to="Elastic Stack: Generally available in 9.2">
    In this version, you cannot fully reset the index configuration. For example, you can't remove columns. However, you can delete the index data. To do that, select the entries to delete, then select **Delete selected** from the contextual menu that appears.
  </applies-item>
</applies-switch>


## Add variable controls to your Discover queries

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Preview since 9.2
</applies-to>

Variable controls help you make your queries more dynamic instead of having to maintain several versions of almost identical queries.
![Variable control in Discover](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/variable-control-discover.png)
You can add them from your Discover ES|QL query.
1. While you edit your ES|QL query, the autocomplete menu suggests adding a control when relevant or when you type `?` in the query. Select **Create control**.
   ![ES|QL query prompting to add a control](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/esql-visualization-control-suggestion.png)
2. A menu opens to let you configure the control. This is where you can specify:
   - The type of the control.
  - For controls with **Static values**, enter available controls manually or select them from the dropdown list.
- For controls with **Values from a query**, write an ES|QL query to populate the list of options. This option is useful for dynamically retrieving control values or perform advanced actions such as [defining chaining controls](/elastic/docs-content/pull/5143/explore-analyze/dashboards/add-controls#chain-variable-controls).
  <tip>
  By linking the control to the global time range, the control only shows values that exist within the time range selected in the dashboard or Discover session. You can do that by specifying `WHERE @timestamp <= ?_tend AND @timestamp > ?_tstart` in the control's query, or [custom time parameters](/elastic/docs-content/pull/5143/explore-analyze/query-filter/languages/esql-kibana#_custom_time_parameters) if your indices don't have a `@timestamp` field.
  </tip>
- The name of the control. You use this name to reference the control in ES|QL queries.
  - Start the name with `?` if you want the options to be simple static values.
- <applies-to>Elastic Stack: Generally available since 9.1</applies-to> Start the name with `??` if you want the options to be fields or functions.
- The values users can select for this control. You can add multiple values from suggested fields, or type in custom values. If you selected **Values from a query**, you must instead write an ES|QL query at this step.
- The label of the control. This is the label displayed in **Discover** or in the dashboard.
- The width of the control.
- Whether the control should allow selecting a single value or multiple values. This [requires using the `MV_CONTAINS` function in your query](#esql-multi-values-controls). <applies-to>Elastic Stack: Preview since 9.3</applies-to> <applies-to>Elastic Cloud Serverless: Preview</applies-to>
   ![ES|QL control settings](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/esql-visualization-control-settings.png "title")
3. Save the control.

The variable is inserted into your query, and the control appears.
**Examples**
- Integrate filtering into your ES|QL experience
  ```esql
  | WHERE field == ?value
  ```
- Fields in controls for dynamic group by
  ```esql
  | STATS count=COUNT(*) BY ??field
  ```
- Variable time ranges? Bind function configuration settings to a control
  ```esql
  | BUCKET(@timestamp, ?interval),
  ```
- Make the function itself dynamic
  ```esql
  | STATS metric = ??function
  ```


### Allow multi-value selections for ES|QL-based variable controls

<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Preview since 9.3
</applies-to>

You can create controls that let users select multiple values. To do that:
1. Add the [`MV_CONTAINS`](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/query-languages/esql/functions-operators/mv-functions#esql-mv_contains) function to your query, and [create a variable](#add-variable-control) as one of its parameters. For example:
   ```esql
   FROM logs-* | WHERE MV_CONTAINS(?values, field)
   ```
   <note>
   Multi-selection is only available for `?values` variables. It is not available for `??fields` and `??functions` variables.
   </note>
2. When defining the control, select the **Allow multiple selections** option.
3. Save the control.

The newly configured control becomes available and allows users to select multiple values.

#### Edit a variable control

Once a control is active for your query, you can still edit it by hovering over it and by selecting the `pencil` **Edit** option that appears.
You can edit all of the options described in [Using ES|QL > Add variable controls to your Discover queries](#add-variable-control).
When you save your edits, the control is updated for your query.

### Import a Discover query along with its controls into a dashboard

To add the results of your Discover explorations to a dashboard in a way that preserves the [controls created from Discover](#add-variable-control) and also adds them to the dashboard, you have two methods:
**Method 1: Adding the Discover session's results**
This method allows you to add the result table of your Discover ES|QL query to any dashboard.
1. Save the ES|QL query containing the variable control into a Discover session. If your Discover session contains several tabs, only the first tab will be imported to the dashboard.
2. Go to **Dashboards** and open or create one.
3. Select **Add**, then **From library**.
4. Find and select the Discover session you saved earlier.

A new panel appears on the dashboard with the results of the query along with any attached controls.
![Importing Discover session with controls into a dashboard](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/import-discover-control-dashboard.png)
**Method 2: Adding the Discover visualization** <applies-to>Elastic Cloud Serverless: Generally available</applies-to> <applies-to>Elastic Stack: Generally available since 9.3</applies-to>
This method allows you to add the visualization of your Discover ES|QL query to any dashboard.
1. Next to the Discover visualization, select `save` **Save visualization**.
   ![Importing Discover visualization with controls into a dashboard](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/save-discover-viz-to-dashboard.png)
2. Select the dashboard to add the visualization to. You can choose an existing dashboard or create one.

The selected dashboard opens. It now includes a new panel that shows the visualization imported from Discover. Existing controls from the initial query in Discover are also added. You can find them at the top of the dashboard.

## Refine an ES|QL query by interacting with the results table

Certain interactions with the results table of your ES|QL query in Discover apply additional filters to your query. When hovering over a value cell, contextual options appear:
- Selecting `plus_in_circle` **Filter for this...** adds or completes the `WHERE` command of the query to specifically look for the selected value. For example, `WHERE host.keyword == "www.elastic.co"`.
- Selecting `minus_in_circle` **Filter out this...** adds or completes the `WHERE` command of the query to specifically exclude the selected value. For example, `WHERE host.keyword != "www.elastic.co"`.

<note applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Generally available since 9.3">
  Up to and including version 9.2, filtering for multi-value fields isn't supported. On later versions, filtering for multi-value fields translates into `WHERE MATCH` or `WHERE NOT MATCH` clauses. For example, `WHERE MATCH(tags.keyword, "error") AND MATCH(tags.keyword, "security")`.
</note>

Other interactions with the results table do not update the query, such as dragging fields onto the table or sorting the table in a specific order.

## Revert to Discover's classic mode

You can go back to the classic data view and KQL mode in Discover at any time. When you switch from ES|QL mode to classic mode, your ES|QL query is lost.
<applies-switch>
  <applies-item title="{serverless:, stack: ga 9.4+ }" applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Planned">
    1. Open the Discover tab that you want to switch to classic mode.
    2. From your tab's contextual menu, select **Switch to classic**. This affects only the selected Discover tab.
    ![Tab contextual menu with an option to switch from ES|QL to classic mode](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/5143/explore-analyze/images/discover-switch-to-classic.png)
    <tip>
      The **Switch to classic** option only appears for the currently active tab. To see it for another tab, you must load that tab first.
    </tip>
  </applies-item>

  <applies-item title="stack: ga 9.2-9.3" applies-to="Elastic Stack: Generally available from 9.2 to 9.3">
    From the application menu, select **Switch to classic**. This only affects your current Discover tab.
  </applies-item>

  <applies-item title="stack: ga 9.0-9.1" applies-to="Elastic Stack: Generally available from 9.0 to 9.1">
    From the application menu, select **Switch to classic**.
  </applies-item>
</applies-switch>