使用 ES|QL REST API
使用 ES|QL 搜索和过滤教程提供了对 ES|QL _query API 的实操介绍。
_query API 在 query 参数中接受一个 ES|QL 查询字符串,运行它并返回结果。例如
POST /_query?format=txt
{
"query": "FROM library | KEEP author, name, page_count, release_date | SORT page_count DESC | LIMIT 5"
}
返回
author | name | page_count | release_date
-----------------+--------------------+---------------+------------------------
Peter F. Hamilton|Pandora's Star |768 |2004-03-02T00:00:00.000Z
Vernor Vinge |A Fire Upon the Deep|613 |1992-06-01T00:00:00.000Z
Frank Herbert |Dune |604 |1965-06-01T00:00:00.000Z
Alastair Reynolds|Revelation Space |585 |2000-03-15T00:00:00.000Z
James S.A. Corey |Leviathan Wakes |561 |2011-06-02T00:00:00.000Z
我们建议使用 Console 来运行 ES|QL 查询 API,因为它具有丰富的自动完成功能。
创建查询时,使用三引号 (""") 可以让你使用诸如引号 (") 之类的特殊字符而无需对其进行转义。它们还使得编写多行请求变得更加容易。
POST /_query?format=txt
{
"query": """
FROM library
| KEEP author, name, page_count, release_date
| SORT page_count DESC
| LIMIT 5
"""
}
ES|QL 可以用以下人类可读格式和二进制格式返回数据。你可以通过在 URL 中指定 format 参数或通过设置 Accept 或 Content-Type HTTP 头来设置格式。
例如
POST /_query?format=yaml
{
"query": """
FROM library
| KEEP author, name, page_count, release_date
| SORT page_count DESC
| LIMIT 5
"""
}
URL 参数优先于 HTTP 头。如果两者都未指定,则响应将以与请求相同的格式返回。
包含元数据的完整响应。适用于自动解析。
format |
HTTP 头 | 描述 |
|---|---|---|
json |
application/json |
JSON (JavaScript Object Notation) 人类可读格式 |
yaml |
application/yaml |
YAML (YAML Ain’t Markup Language) 人类可读格式 |
仅查询结果,不包含元数据。适用于快速、手动的数据预览。
format |
HTTP 头 | 描述 |
|---|---|---|
csv |
text/csv |
逗号分隔值 |
tsv |
text/tab-separated-values |
制表符分隔值 |
txt |
text/plain |
类似 CLI 的表示形式 |
md |
text/markdown |
Markdown / GitHub 风格的管道表格 |
csv 格式接受一个格式化 URL 查询属性 delimiter,用于指示应用哪个字符来分隔 CSV 值。它的默认值为逗号 (,) 且不能取以下任何值:双引号 (")、回车符 (\r) 和换行符 (\n)。制表符 (\t) 也不能使用。请改用 tsv 格式。
md 格式始终包含表头行。请求 header=absent(例如通过 Accept: text/markdown; header=absent)会返回 400 错误,这与支持 header=present 和 header=absent 的 csv、tsv 和 txt 不同。
紧凑的二进制编码。供应用程序使用。
format |
HTTP 头 | 描述 |
|---|---|---|
cbor |
application/cbor |
简明二进制对象表示法 |
smile |
application/smile |
类似于 CBOR 的 Smile 二进制数据格式 |
arrow |
application/vnd.apache.arrow.stream |
实验性。 Apache Arrow 数据帧,IPC 流式传输格式 |
在 filter 参数中指定一个 Query DSL 查询,以过滤 ES|QL 查询所运行的文档集。
POST /_query?format=txt
{
"query": """
FROM library
| KEEP author, name, page_count, release_date
| SORT page_count DESC
| LIMIT 5
""",
"filter": {
"range": {
"page_count": {
"gte": 100,
"lte": 200
}
}
}
}
返回
author | name | page_count | release_date
---------------+------------------------------------+---------------+------------------------
Douglas Adams |The Hitchhiker's Guide to the Galaxy|180 |1979-10-12T00:00:00.000Z
当 filter 参数跳过整个索引时,它可以从结果集中剔除列。这对于解决不同索引属性之间的类型冲突非常有用。
例如,如果数据流中的几天数据使用了不正确的类型进行索引,你可以使用过滤器来排除不正确的范围。这允许 ES|QL 在不更改源模式的情况下,对剩余数据使用正确的类型。
考虑查询具有 f1 属性的 index-1 和具有 f2 属性的 index-2。
使用过滤器时,以下查询仅返回 f1 列
POST /_query?format=txt
{
"query": "FROM index-*",
"filter": {
"term": {
"f1": "*"
}
}
}
使用 WHERE 子句会同时返回 f1 和 f2 列
POST /_query?format=txt
{
"query": "FROM index-* WHERE f1 is not null"
}
默认情况下,ES|QL 以行的形式返回结果。例如,FROM 将每个单独的文档返回为一行。对于 json、yaml、cbor 和 smile 格式,ES|QL 可以以列式方式返回结果,其中一行代表结果中某列的所有值。
POST /_query?format=json
{
"query": """
FROM library
| KEEP author, name, page_count, release_date
| SORT page_count DESC
| LIMIT 5
""",
"columnar": true
}
返回
{
"took": 28,
"is_partial": false,
"documents_found": 5,
"values_loaded": 20,
"columns": [
{"name": "author", "type": "text"},
{"name": "name", "type": "text"},
{"name": "page_count", "type": "integer"},
{"name": "release_date", "type": "date"}
],
"values": [
["Peter F. Hamilton", "Vernor Vinge", "Frank Herbert", "Alastair Reynolds", "James S.A. Corey"],
["Pandora's Star", "A Fire Upon the Deep", "Dune", "Revelation Space", "Leviathan Wakes"],
[768, 613, 604, 585, 561],
["2004-03-02T00:00:00.000Z", "1992-06-01T00:00:00.000Z", "1965-06-01T00:00:00.000Z", "2000-03-15T00:00:00.000Z", "2011-06-02T00:00:00.000Z"]
]
}
要为查询设置默认时区,请在请求体中使用 time_zone 参数。如果未指定,默认时区为 UTC。
该参数既接受偏移量(例如 +01:00),也接受时区 ID(例如 Europe/Paris)。
这将影响以下内容
- 处理日期的函数(如
DATE_DIFF)将在可能的情况下使用它。- 如果函数具有自定义的
time_zone参数,则该参数优先
- 如果函数具有自定义的
- 日期值的 API 响应格式将根据指定的时区进行格式化。这取决于所使用的格式。
例如,此查询
POST /_query
{
"time_zone": "Europe/Paris",
"query": """
ROW date_string = "2023-01-15T00:00:00.000"
| EVAL date = date_parse(date_string)
"""
}
将返回
{
"took": 28,
"is_partial": false,
"documents_found": 2,
"values_loaded": 2,
"columns": [
{"name": "date_string", "type": "keyword"},
{"name": "date", "type": "date"},
],
"values": [
["2023-01-15T00:00:00.000", "2023-01-15T00:00:00.000+01:00"]
]
}
在请求体中使用 locale 参数以根据区域设置的惯例返回格式化的结果(尤其是日期)。如果未指定 locale,则默认为 en-US(英语)。请参考 JDK 支持的区域设置。
语法:locale 参数接受格式为 xy 和 xy-XY 的语言标记(不区分大小写)。
例如,要返回法语的月份名称
POST /_query
{
"locale": "fr-FR",
"query": """
ROW birth_date_string = "2023-01-15T00:00:00.000Z"
| EVAL birth_date = date_parse(birth_date_string)
| EVAL month_of_birth = DATE_FORMAT("MMMM",birth_date)
| LIMIT 5
"""
}
使用 approximation 参数为 STATS 查询启用快速近似计算。如果未指定,则默认为 false。
例如
POST /_query
{
"approximation": true,
"query": """
FROM web_traffic
| STATS total_hits = COUNT(), avg_load_time = AVG(page_load_ms)
"""
}
对于更高级的设置,请使用查询近似设置对象。
你可以使用参数将查询逻辑与其数据分离,而不是将值直接嵌入到查询字符串中。当查询包含用户输入时,这种方法可以防止注入攻击,并使查询能够使用不同的值重复使用。
ES|QL 支持值参数和标识符参数
这些参数可以是命名的、位置的或匿名的
- 命名 (
?name,??name) 按名称与参数匹配。 - 位置 (
?1,??2) 按数组中的位置与参数匹配。 - 匿名 (
?,??) 按它们在查询中出现的顺序与参数匹配。
不在同一个查询中混用参数风格。例如,不能将命名的 ?name 与位置的 ??1 混用。选择一种风格并在值参数和标识符参数中一致地使用它。
我们建议在 9.1 及更高版本中使用 ?? 语法替代。
语法
| 风格 | 占位符 | params 格式 |
|---|---|---|
| 命名 | ?name |
[{"name": value}, ...] |
| 位置 | ?1, ?2 |
[value1, value2, ...] |
| 匿名 | ? |
[value1, value2, ...](按顺序消耗) |
POST /_query
{
"query": """
FROM library
| WHERE page_count > ?min_pages AND author == ?author
| KEEP author, name, page_count
| SORT page_count DESC
""",
"params": [{"min_pages" : 300}, {"author" : "Frank Herbert"}]
}
- 命名占位符
?min_pages和?author标记了值被替换的位置 params中的每个对象将名称映射到其值
你还可以按位置引用参数
POST /_query
{
"query": """
FROM library
| WHERE page_count > ?1 AND author == ?2
| KEEP author, name, page_count
| SORT page_count DESC
""",
"params": [300, "Frank Herbert"]
}
?1指代第一个参数,?2指代第二个参数- 值作为简单的数组提供,按位置匹配
?? 占位符允许你在 params 中将字段名和函数名作为纯字符串传递,而无需将其标注为标识符。
我们建议使用此语法代替原有的 ? 语法。
语法
| 风格 | 占位符 | params 格式 |
|---|---|---|
| 命名 | ??name |
[{"name": "field_name"}, ...] |
| 位置 | ??1, ??2 |
["field_name", ...] |
| 匿名 | ?? |
["field_name", ...](按顺序消耗) |
此查询为聚合函数、字段和分组使用了命名的标识符参数
POST /_query?format=txt
{
"query": """
FROM sample_data
| STATS result = ??agg_fn(??field) BY ??group_by
| SORT ??group_by
""",
"params": [{"agg_fn": "avg"}, {"field": "event.duration"}, {"group_by": "client.ip"}]
}
??agg_fn作为函数名插入,??field和??group_by作为字段名插入- 参数值作为标识符替换,而不是作为带引号的字符串
对于位置参数,占位符通过其在数组中的位置引用参数
POST /_query?format=txt
{
"query": """
FROM sample_data
| STATS result = ??1(??2) BY ??3
| SORT ??3
""",
"params": ["avg", "event.duration", "client.ip"]
}
??1是第一个参数(函数名),??2是第二个(字段),??3是第三个(分组字段)- 标识符名称的简单数组
对于匿名参数,每个 ?? 按顺序消耗下一个参数
POST /_query?format=txt
{
"query": """
FROM sample_data
| STATS result = ??(??) BY ??
| SORT ??
""",
"params": ["avg", "event.duration", "client.ip", "client.ip"]
}
- 每个
??被数组中的下一个参数替换 client.ip出现两次,因为SORT ??消耗了一个单独的参数
对于像 car.make 这样由点分隔的字段名,你可以分别对每个段进行参数化。当相同的命名空间前缀适用于多个字段时,这非常有用
POST /_query?format=txt
{
"query": """
FROM persons
| WHERE ??namespace.??field == ?value
| KEEP ??namespace.??field, ??namespace.??model, ??sort_field
| SORT ??sort_field
""",
"params": [{"namespace": "car"}, {"field": "make"}, {"value": "Tesla"}, {"model": "model"}, {"sort_field": "first_name"}]
}
??namespace.??field被作为限定字段名car.make插入- 每个标识符参数提供点分隔名称的一个段
你可以在同一个查询中混合使用这两种参数类型。当要过滤的字段和过滤值都是动态的时,这非常有用。
POST /_query?format=txt
{
"query": """
FROM sample_data
| WHERE ??field == ?value
| KEEP ??field
""",
"params": [{"field": "client.ip"}, {"value": "192.168.1.1"}]
}
??field是一个标识符参数(字段名),?value是一个值参数(过滤值)- 这两种参数类型都可以被命名并一起提供
ES|QL 异步查询 API 允许你异步执行查询请求、监控其进度并在结果可用时检索结果。
执行 ES|QL 查询通常非常快,但在跨大型数据集或冻结数据进行查询时可能需要一些时间。为避免长时间等待,请运行异步 ES|QL 查询。
异步查询 API 发起的查询可能会也可能不会返回结果。wait_for_completion_timeout 属性决定等待结果的时长。如果在此时间内结果不可用,则会返回一个查询 ID,稍后可用于检索结果。例如
POST /_query/async
{
"query": """
FROM library
| EVAL year = DATE_TRUNC(1 YEARS, release_date)
| STATS MAX(page_count) BY year
| SORT year
| LIMIT 5
""",
"wait_for_completion_timeout": "2s"
}
如果在给定的超时期间内(本例中为 2 秒)结果不可用,则不返回结果,而是返回包含以下内容的响应
- 查询 ID
- 值为 true 的
is_running,表示查询正在进行中
查询继续在后台运行,不会阻塞其他请求。
{
"id": "FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=",
"is_running": true
}
要检查异步查询的进度,请使用带有查询 ID 的 ES|QL 异步查询获取 API。在 wait_for_completion_timeout 参数中指定你希望等待完整结果的时长。
GET /_query/async/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=?wait_for_completion_timeout=30s
如果响应的 is_running 值为 false,则表示查询已完成并返回了结果,同时附带查询的耗时 (took)。
{
"is_running": false,
"took": 48,
"columns": ...
}
要停止正在运行的异步查询并返回到目前为止计算出的结果,请使用带有查询 ID 的 异步停止 API。
POST /_query/async/FmNJRUZ1YWZCU3dHY1BIOUhaenVSRkEaaXFlZ3h4c1RTWFNocDdnY2FSaERnUTozNDE=/stop
查询将被停止,响应将包含到目前为止计算出的结果。响应格式与 get API 相同。
{
"is_running": false,
"took": 48,
"is_partial": true,
"columns": ...
}
即使查询已经完成,只要在 keep_alive 窗口内,也可以使用此 API 检索结果。is_partial 字段指示结果的完整性。值为 true 意味着结果可能不完整。
在 keep_alive 期间结束之前,使用 ES|QL 异步查询删除 API 删除异步查询。如果查询仍在运行,Elasticsearch 将将其取消。
DELETE /_query/async/FmdMX2pIang3UWhLRU5QS0lqdlppYncaMUpYQ05oSkpTc3kwZ21EdC1tbFJXQToxOTI=
你还将在响应的 X-Elasticsearch-Async-Id 和 X-Elasticsearch-Async-Is-Running HTTP 头中分别收到异步 ID 和运行状态。如果你使用诸如 txt、csv 或 tsv 之类的表格文本格式,这会非常有用,因为你在响应体中收不到这些字段。
除了 Elasticsearch 传统的耗时 took 之外,ES|QL 还返回 documents_found 和 values_loaded,你可以将它们视为 Elasticsearch 为运行查询所必须付出努力的粗略代理指标。通常,数字越大意味着查询花费的精力越多。
documents_found 是运行查询必须查找的文档数量。如果该值较低,则说明 Elasticsearch 能够有效地利用其搜索索引来返回结果。少数查询(如 FROM idx | STATS COUNT(*))无需查看任何文档即可运行。它们会针对每个计数俏皮地报告一个“找到的”文档。
values_loaded 是运行查询所加载的值的数量。如果 Elasticsearch 必须为每个文档加载一个值(就像 FROM idx | STATS BY DATE_TRUNC(1 hour, @timestamp) 那样),这将与 documents_found 相同。如果 Elasticsearch 必须为每个文档加载许多字段,它将是 documents_found 的许多倍。同样,更大的数字通常意味着查询花费了更多的精力。
尽管 documents_found 和 values_loaded 很高,许多查询仍然会很快。由于各种原因,有些查询即使在 documents_found 和 values_loaded 较低的情况下也会很慢
- 查询超级快或超级慢。
- 某些值加载非常快(如
@timestamp),而其他值加载慢得多(如text字段)。 - 某些函数相当快(如
+、DATE_TRUNC、BUCKET等)。 - 某些命令相当重(如
GROK)。