Skip to content

Commit 5e3c464

Browse files
mashhursyaauiejsvd
authored
ES|QL support (#194)
* ESQL and DSL executors are introduced. param can accept ES|QL query shape now. is introduced for initial step but needs team's feedback. DSL logics moved into DSL executors. * Apply suggestions from code review Separate DSL and ESQL interface in the client. Co-authored-by: Rye Biesemeyer <[email protected]> * Rebase against upstream main after target support added. Separate unit test for DSL. Address comments: do not save ES version in client, add apply target method in executors, set to target if target is defined, docs update. Co-authored-by: Rye Biesemeyer <[email protected]> * Introduce query_type option which accepts dsl or esql to define a query shape. Remove multi-depth nested named_params and keep only top-level query_params which aligns with placeholder structure in the ES|QL. * Separate event referenced and static valued fields at initialization of the ESQL executor. * query_params now supports both Array and Hash types. * Add tech preview section under ESQL. * Place the query results based on the target specified. If not specified, first result will be set to event's top level. * Apply suggestions from code review Doc corrections. Co-authored-by: João Duarte <[email protected]> * ES|QL result mapping to event doc correction. * Integration tests to run with credentials enabled and SSL configs. --------- Co-authored-by: Rye Biesemeyer <[email protected]> Co-authored-by: João Duarte <[email protected]>
1 parent f1e4e11 commit 5e3c464

File tree

11 files changed

+1446
-462
lines changed

11 files changed

+1446
-462
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 4.3.0
2+
- ES|QL support [#194](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/194)
3+
14
## 4.2.0
25
- Add `target` configuration option to store the result into it [#196](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/196)
36

docs/index.asciidoc

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ if [type] == "end" {
5454

5555
The example below reproduces the above example but utilises the query_template.
5656
This query_template represents a full Elasticsearch query DSL and supports the
57-
standard Logstash field substitution syntax. The example below issues
57+
standard {ls} field substitution syntax. The example below issues
5858
the same query as the first example but uses the template shown.
5959

6060
[source,ruby]
@@ -118,6 +118,110 @@ Authentication to a secure Elasticsearch cluster is possible using _one_ of the
118118
Authorization to a secure Elasticsearch cluster requires `read` permission at index level and `monitoring` permissions at cluster level.
119119
The `monitoring` permission at cluster level is necessary to perform periodic connectivity checks.
120120

121+
[id="plugins-{type}s-{plugin}-esql"]
122+
==== {esql} support
123+
124+
.Technical Preview
125+
****
126+
The {esql} feature that allows using ES|QL queries with this plugin is in Technical Preview.
127+
Configuration options and implementation details are subject to change in minor releases without being preceded by deprecation warnings.
128+
****
129+
130+
{es} Query Language ({esql}) provides a SQL-like interface for querying your {es} data.
131+
132+
To use {esql}, this plugin needs to be installed in {ls} 8.17.4 or newer, and must be connected to {es} 8.11 or newer.
133+
134+
To configure {esql} query in the plugin, set your {esql} query in the `query` parameter.
135+
136+
IMPORTANT: We recommend understanding {ref}/esql-limitations.html[{esql} current limitations] before using it in production environments.
137+
138+
The following is a basic {esql} query that sets the food name to transaction event based on upstream event's food ID:
139+
[source, ruby]
140+
filter {
141+
elasticsearch {
142+
hosts => [ 'https://..']
143+
api_key => '....'
144+
query => '
145+
FROM food-index
146+
| WHERE id == ?food_id
147+
'
148+
query_params => {
149+
"food_id" => "[food][id]"
150+
}
151+
}
152+
}
153+
154+
Set `config.support_escapes: true` in `logstash.yml` if you need to escape special chars in the query.
155+
156+
In the result event, the plugin sets total result size in `[@metadata][total_values]` field.
157+
158+
[id="plugins-{type}s-{plugin}-esql-event-mapping"]
159+
===== Mapping {esql} result to {ls} event
160+
{esql} returns query results in a structured tabular format, where data is organized into _columns_ (fields) and _values_ (entries).
161+
The plugin maps each value entry to an event, populating corresponding fields.
162+
For example, a query might produce a table like:
163+
164+
[cols="2,1,1,1,2",options="header"]
165+
|===
166+
|`timestamp` |`user_id` | `action` | `status.code` | `status.desc`
167+
168+
|2025-04-10T12:00:00 |123 |login |200 | Success
169+
|2025-04-10T12:05:00 |456 |purchase |403 | Forbidden (unauthorized user)
170+
|===
171+
172+
For this case, the plugin creates two JSON look like objects as below and places them into the `target` field of the event if `target` is defined.
173+
If `target` is not defined, the plugin places the _only_ first result at the root of the event.
174+
[source, json]
175+
[
176+
{
177+
"timestamp": "2025-04-10T12:00:00",
178+
"user_id": 123,
179+
"action": "login",
180+
"status": {
181+
"code": 200,
182+
"desc": "Success"
183+
}
184+
},
185+
{
186+
"timestamp": "2025-04-10T12:05:00",
187+
"user_id": 456,
188+
"action": "purchase",
189+
"status": {
190+
"code": 403,
191+
"desc": "Forbidden (unauthorized user)"
192+
}
193+
}
194+
]
195+
196+
NOTE: If your index has a mapping with sub-objects where `status.code` and `status.desc` actually dotted fields, they appear in {ls} events as a nested structure.
197+
198+
[id="plugins-{type}s-{plugin}-esql-multifields"]
199+
===== Conflict on multi-fields
200+
201+
{esql} query fetches all parent and sub-fields fields if your {es} index has https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/multi-fields[multi-fields] or https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/subobjects[subobjects].
202+
Since {ls} events cannot contain parent field's concrete value and sub-field values together, the plugin ignores sub-fields with warning and includes parent.
203+
We recommend using the `RENAME` (or `DROP` to avoid warning) keyword in your {esql} query explicitly rename the fields to include sub-fields into the event.
204+
205+
This is a common occurrence if your template or mapping follows the pattern of always indexing strings as "text" (`field`) + " keyword" (`field.keyword`) multi-field.
206+
In this case it's recommended to do `KEEP field` if the string is identical and there is only one subfield as the engine will optimize and retrieve the keyword, otherwise you can do `KEEP field.keyword | RENAME field.keyword as field`.
207+
208+
To illustrate the situation with example, assuming your mapping has a time `time` field with `time.min` and `time.max` sub-fields as following:
209+
[source, ruby]
210+
"properties": {
211+
"time": { "type": "long" },
212+
"time.min": { "type": "long" },
213+
"time.max": { "type": "long" }
214+
}
215+
216+
The {esql} result will contain all three fields but the plugin cannot map them into {ls} event.
217+
To avoid this, you can use the `RENAME` keyword to rename the `time` parent field to get all three fields with unique fields.
218+
[source, ruby]
219+
...
220+
query => 'FROM my-index | RENAME time AS time.current'
221+
...
222+
223+
For comprehensive ES|QL syntax reference and best practices, see the https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-syntax.html[{esql} documentation].
224+
121225
[id="plugins-{type}s-{plugin}-options"]
122226
==== Elasticsearch Filter Configuration Options
123227

@@ -143,6 +247,8 @@ NOTE: As of version `4.0.0` of this plugin, a number of previously deprecated se
143247
| <<plugins-{type}s-{plugin}-password>> |<<password,password>>|No
144248
| <<plugins-{type}s-{plugin}-proxy>> |<<uri,uri>>|No
145249
| <<plugins-{type}s-{plugin}-query>> |<<string,string>>|No
250+
| <<plugins-{type}s-{plugin}-query_type>> |<<string,string>>, one of `["dsl", "esql"]`|No
251+
| <<plugins-{type}s-{plugin}-query_params>> |<<hash,hash>> or <<hash,hash>>|No
146252
| <<plugins-{type}s-{plugin}-query_template>> |<<string,string>>|No
147253
| <<plugins-{type}s-{plugin}-result_size>> |<<number,number>>|No
148254
| <<plugins-{type}s-{plugin}-retry_on_failure>> |<<number,number>>|No
@@ -339,11 +445,30 @@ environment variables e.g. `proxy => '${LS_PROXY:}'`.
339445
* Value type is <<string,string>>
340446
* There is no default value for this setting.
341447

342-
Elasticsearch query string. More information is available in the
343-
{ref}/query-dsl-query-string-query.html#query-string-syntax[Elasticsearch query
344-
string documentation].
345-
Use either `query` or `query_template`.
448+
The query to be executed.
449+
The accepted query shape is DSL query string or ES|QL.
450+
For the DSL query string, use either `query` or `query_template`.
451+
Read the {ref}/query-dsl-query-string-query.html[{es} query
452+
string documentation] or {ref}/esql.html[{es} ES|QL documentation] for more information.
453+
454+
[id="plugins-{type}s-{plugin}-query_type"]
455+
===== `query_type`
456+
457+
* Value can be `dsl` or `esql`
458+
* Default value is `dsl`
459+
460+
Defines the <<plugins-{type}s-{plugin}-query>> shape.
461+
When `dsl`, the query shape must be valid {es} JSON-style string.
462+
When `esql`, the query shape must be a valid {esql} string and `index`, `query_template` and `sort` parameters are not allowed.
463+
464+
[id="plugins-{type}s-{plugin}-query_params"]
465+
===== `query_params`
466+
467+
* The value type is <<hash,hash>> or <<array,array>>. When an array provided, the array elements are pairs of `key` and `value`.
468+
* There is no default value for this setting
346469

470+
Named parameters in {esql} to send to {es} together with <<plugins-{type}s-{plugin}-query>>.
471+
Visit {ref}/esql-rest.html#esql-rest-params[passing parameters to query page] for more information.
347472

348473
[id="plugins-{type}s-{plugin}-query_template"]
349474
===== `query_template`
@@ -540,8 +665,9 @@ Tags the event on failure to look up previous log event information. This can be
540665

541666
Define the target field for placing the result data.
542667
If this setting is omitted, the target will be the root (top level) of the event.
668+
It is highly recommended to set when using `query_type=>'esql'` to set all query results into the event.
543669

544-
The destination fields specified in <<plugins-{type}s-{plugin}-fields>>, <<plugins-{type}s-{plugin}-aggregation_fields>>, and <<plugins-{type}s-{plugin}-docinfo_fields>> are relative to this target.
670+
When `query_type=>'dsl'`, the destination fields specified in <<plugins-{type}s-{plugin}-fields>>, <<plugins-{type}s-{plugin}-aggregation_fields>>, and <<plugins-{type}s-{plugin}-docinfo_fields>> are relative to this target.
545671

546672
For example, if you want the data to be put in the `operation` field:
547673
[source,ruby]

0 commit comments

Comments
 (0)