1. 概述
- 逻辑视图(Logical Views)------将常用查询定义为命名视图,一处定义,处处引用。
- 子查询(Subqueries) ------在单个
FROM中组合多个索引,各自拥有独立的处理管道,即使它们的 schema 互不兼容。 - Schema-on-Read ------无需修改映射(mapping)或重建索引(reindex),即可查询那些从未被映射过的字段,直接从
_source中按需读取。
这些特性共同构建了一种更灵活、更敏捷的数据建模与查询方式。
2. 三大核心能力概览:
在深入每个特性之前,先理解它们之间的组合关系 至关重要。这三者设计为分层叠加(layer together)的架构:
#mermaid-svg-SMU1YjcOlfQfRWmA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SMU1YjcOlfQfRWmA .error-icon{fill:#552222;}#mermaid-svg-SMU1YjcOlfQfRWmA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SMU1YjcOlfQfRWmA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SMU1YjcOlfQfRWmA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SMU1YjcOlfQfRWmA .marker.cross{stroke:#333333;}#mermaid-svg-SMU1YjcOlfQfRWmA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SMU1YjcOlfQfRWmA p{margin:0;}#mermaid-svg-SMU1YjcOlfQfRWmA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster-label text{fill:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster-label span{color:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster-label span p{background-color:transparent;}#mermaid-svg-SMU1YjcOlfQfRWmA .label text,#mermaid-svg-SMU1YjcOlfQfRWmA span{fill:#333;color:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA .node rect,#mermaid-svg-SMU1YjcOlfQfRWmA .node circle,#mermaid-svg-SMU1YjcOlfQfRWmA .node ellipse,#mermaid-svg-SMU1YjcOlfQfRWmA .node polygon,#mermaid-svg-SMU1YjcOlfQfRWmA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SMU1YjcOlfQfRWmA .rough-node .label text,#mermaid-svg-SMU1YjcOlfQfRWmA .node .label text,#mermaid-svg-SMU1YjcOlfQfRWmA .image-shape .label,#mermaid-svg-SMU1YjcOlfQfRWmA .icon-shape .label{text-anchor:middle;}#mermaid-svg-SMU1YjcOlfQfRWmA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SMU1YjcOlfQfRWmA .rough-node .label,#mermaid-svg-SMU1YjcOlfQfRWmA .node .label,#mermaid-svg-SMU1YjcOlfQfRWmA .image-shape .label,#mermaid-svg-SMU1YjcOlfQfRWmA .icon-shape .label{text-align:center;}#mermaid-svg-SMU1YjcOlfQfRWmA .node.clickable{cursor:pointer;}#mermaid-svg-SMU1YjcOlfQfRWmA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SMU1YjcOlfQfRWmA .arrowheadPath{fill:#333333;}#mermaid-svg-SMU1YjcOlfQfRWmA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SMU1YjcOlfQfRWmA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SMU1YjcOlfQfRWmA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMU1YjcOlfQfRWmA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SMU1YjcOlfQfRWmA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMU1YjcOlfQfRWmA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster text{fill:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA .cluster span{color:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SMU1YjcOlfQfRWmA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SMU1YjcOlfQfRWmA rect.text{fill:none;stroke-width:0;}#mermaid-svg-SMU1YjcOlfQfRWmA .icon-shape,#mermaid-svg-SMU1YjcOlfQfRWmA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SMU1YjcOlfQfRWmA .icon-shape p,#mermaid-svg-SMU1YjcOlfQfRWmA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SMU1YjcOlfQfRWmA .icon-shape .label rect,#mermaid-svg-SMU1YjcOlfQfRWmA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SMU1YjcOlfQfRWmA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SMU1YjcOlfQfRWmA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SMU1YjcOlfQfRWmA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 逻辑视图
(命名查询)
子查询管道
(每个索引独立处理)
Schema-on-Read
(读取未映射字段)
统一结果集
(合并输出)
- 子查询 负责为每个索引定义独立的管道(
WHERE、EVAL、KEEP等),处理各自特有的字段和逻辑。 - 视图将子查询打包,对外暴露一个单一名称,消费者(仪表板、告警、临时查询)只需引用视图名,无需关心内部复杂性。
- Schema-on-Read 赋予这些管道访问从未在映射中声明 的字段的能力,数据在写入时无需预知所有字段,查询时按需从
_source提取。
结果 :一条 FROM view_name 查询,就能融合多个服务的日志,统一规范化它们的 schema,并直接使用那些你当初忘记在 ingest 阶段映射的字段------所有这一切,无需重新索引。
3. 逻辑视图(Logical Views,Tech Preview)
3.1 逻辑视图
逻辑视图可以理解为虚拟索引 (virtual indices)。你通过 _query/view REST API 在集群级别定义一个 ES|QL 查询,并为它赋予一个名称。之后,在任何 FROM 子句中,你可以像使用真实索引一样引用这个名称。
#mermaid-svg-3e4W9lL1xj8XqL4C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3e4W9lL1xj8XqL4C .error-icon{fill:#552222;}#mermaid-svg-3e4W9lL1xj8XqL4C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3e4W9lL1xj8XqL4C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3e4W9lL1xj8XqL4C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3e4W9lL1xj8XqL4C .marker.cross{stroke:#333333;}#mermaid-svg-3e4W9lL1xj8XqL4C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3e4W9lL1xj8XqL4C p{margin:0;}#mermaid-svg-3e4W9lL1xj8XqL4C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster-label text{fill:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster-label span{color:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster-label span p{background-color:transparent;}#mermaid-svg-3e4W9lL1xj8XqL4C .label text,#mermaid-svg-3e4W9lL1xj8XqL4C span{fill:#333;color:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C .node rect,#mermaid-svg-3e4W9lL1xj8XqL4C .node circle,#mermaid-svg-3e4W9lL1xj8XqL4C .node ellipse,#mermaid-svg-3e4W9lL1xj8XqL4C .node polygon,#mermaid-svg-3e4W9lL1xj8XqL4C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3e4W9lL1xj8XqL4C .rough-node .label text,#mermaid-svg-3e4W9lL1xj8XqL4C .node .label text,#mermaid-svg-3e4W9lL1xj8XqL4C .image-shape .label,#mermaid-svg-3e4W9lL1xj8XqL4C .icon-shape .label{text-anchor:middle;}#mermaid-svg-3e4W9lL1xj8XqL4C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3e4W9lL1xj8XqL4C .rough-node .label,#mermaid-svg-3e4W9lL1xj8XqL4C .node .label,#mermaid-svg-3e4W9lL1xj8XqL4C .image-shape .label,#mermaid-svg-3e4W9lL1xj8XqL4C .icon-shape .label{text-align:center;}#mermaid-svg-3e4W9lL1xj8XqL4C .node.clickable{cursor:pointer;}#mermaid-svg-3e4W9lL1xj8XqL4C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3e4W9lL1xj8XqL4C .arrowheadPath{fill:#333333;}#mermaid-svg-3e4W9lL1xj8XqL4C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3e4W9lL1xj8XqL4C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3e4W9lL1xj8XqL4C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3e4W9lL1xj8XqL4C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3e4W9lL1xj8XqL4C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3e4W9lL1xj8XqL4C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster text{fill:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C .cluster span{color:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3e4W9lL1xj8XqL4C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3e4W9lL1xj8XqL4C rect.text{fill:none;stroke-width:0;}#mermaid-svg-3e4W9lL1xj8XqL4C .icon-shape,#mermaid-svg-3e4W9lL1xj8XqL4C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3e4W9lL1xj8XqL4C .icon-shape p,#mermaid-svg-3e4W9lL1xj8XqL4C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3e4W9lL1xj8XqL4C .icon-shape .label rect,#mermaid-svg-3e4W9lL1xj8XqL4C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3e4W9lL1xj8XqL4C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3e4W9lL1xj8XqL4C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3e4W9lL1xj8XqL4C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 使用
定义
视图定义存储于集群
PUT _query/view/error_triage
FROM svc-gateway-*
| WHERE ...
| KEEP ...
FROM error_triage
| STATS ...
| SORT ...
关键特性:
- 自动更新:修改视图定义后,所有引用该视图的仪表板、告警和临时查询立即生效,无需逐个更新。
- 可嵌套:视图可以引用其他视图,支持组合复用。
- 跨集群搜索:视图可跨多个集群定义。
- 专用 RBAC 权限:可以为视图设置独立的访问控制。
3.2 用法示例
定义视图:
esql
PUT _query/view/error_triage
{
"query": """
FROM svc-gateway-*
| WHERE http.response.status_code >= 500
| KEEP @timestamp, http.response.status_code, url.path, source.ip
"""
}
使用视图:
esql
FROM error_triage
| STATS error_count = COUNT(*) BY url.path
| SORT error_count DESC
在 Kibana Discover 中,自动完成会识别出 error_triage 是一个逻辑视图,并提供语法提示。
3.3 简析
视图本质上是查询定义的存储,并不实际存储任何数据。执行时,Elasticsearch 会将视图内部的查询文本展开,并合并外部查询,生成最终的执行计划。因此,视图不会带来额外的存储开销,但在执行时会有微小的解析开销(可忽略)。
4. 子查询(Subqueries in FROM,Tech Preview)
4.1 为什么需要子查询?
在实际场景中,不同服务的日志往往写入不同的索引,它们的 schema 可能完全不同(例如网关日志有 http.response.status_code,支付日志有 transaction.status,认证日志有 event.outcome)。以往,要合并分析这些数据,要么在 ingest 阶段统一 schema,要么在查询后手动合并------前者笨重,后者低效。
ES|QL 的子查询 提供了组合原语(composition primitive):你可以在单个 FROM 中列出多个子查询,每个子查询针对一个索引(或索引模式)独立处理,然后所有结果以 UNION ALL 语义合并,进入后续管道。
4.2 工作流程关键点
- 独立过滤 :每个分支有自己的
WHERE,优化器会分别将过滤条件下推到各索引,充分利用索引的统计信息和缓存。 - 灵活投影 :
EVAL可以在分支内计算新字段,统一输出结构(例如每个分支都产出service、error_detail等公共字段)。 - 性能:各分支并行执行,最终结果合并,整体延迟取决于最慢的分支。
4.3 完整示例
esql
FROM
(FROM svc-gateway-*
| WHERE http.response.status_code >= 500
| EVAL service = "gateway",
error_detail = CONCAT("HTTP ", http.response.status_code::string)
| KEEP @timestamp, service, error_detail, source.ip),
(FROM svc-payments-*
| WHERE transaction.status IN ("failed", "timeout")
| EVAL service = "payments",
error_detail = transaction.status
| KEEP @timestamp, service, error_detail, source.ip),
(FROM svc-auth-*
| WHERE event.action == "login" AND event.outcome == "failure"
| EVAL service = "auth",
error_detail = CONCAT(event.action, " ", event.outcome)
| KEEP @timestamp, service, error_detail, source.ip)
| SORT @timestamp DESC
| LIMIT 20
这个查询将三个不同服务的异常事件合并,按时间倒序返回最新的 20 条记录,每条记录都带有统一的 service 和 error_detail 字段。
5. Schema-on-Read:未映射字段与 JSON 提取(Tech Preview)
5.1 传统困境
在 Elasticsearch 中,字段必须在映射(mapping)中定义才能被索引和查询。如果 ingest 时遗漏了某个字段,或者 schema 后期发生变化,通常需要重新索引(reindex)整个数据,这在生产环境中代价高昂。
Schema-on-Read 打破了这一限制。它允许你查询从未被映射的字段 ,直接从 _source(原始 JSON 文档)中按需读取,无需任何事前准备。
5.2 两种访问方式
ES|QL 提供了两个层次的支持:
| 方式 | 适用场景 | 使用方法 |
|---|---|---|
SET unmapped_fields="load" |
统一开启未映射字段访问,所有未映射字段自动从 _source 读取 |
在查询开头设置一次,之后即可像普通字段一样使用未映射字段 |
JSON_EXTRACT |
精细化提取,从原始 JSON 字符串或 flattened 类型字段中抽取特定值 |
在 EVAL 或 WHERE 中使用 JSON_EXTRACT(field, '$.path') |
5.3 行为模式
unmapped_fields 支持三种模式:
#mermaid-svg-ChPedDxLUcaDWS0X{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ChPedDxLUcaDWS0X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ChPedDxLUcaDWS0X .error-icon{fill:#552222;}#mermaid-svg-ChPedDxLUcaDWS0X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ChPedDxLUcaDWS0X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ChPedDxLUcaDWS0X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ChPedDxLUcaDWS0X .marker.cross{stroke:#333333;}#mermaid-svg-ChPedDxLUcaDWS0X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ChPedDxLUcaDWS0X p{margin:0;}#mermaid-svg-ChPedDxLUcaDWS0X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ChPedDxLUcaDWS0X .cluster-label text{fill:#333;}#mermaid-svg-ChPedDxLUcaDWS0X .cluster-label span{color:#333;}#mermaid-svg-ChPedDxLUcaDWS0X .cluster-label span p{background-color:transparent;}#mermaid-svg-ChPedDxLUcaDWS0X .label text,#mermaid-svg-ChPedDxLUcaDWS0X span{fill:#333;color:#333;}#mermaid-svg-ChPedDxLUcaDWS0X .node rect,#mermaid-svg-ChPedDxLUcaDWS0X .node circle,#mermaid-svg-ChPedDxLUcaDWS0X .node ellipse,#mermaid-svg-ChPedDxLUcaDWS0X .node polygon,#mermaid-svg-ChPedDxLUcaDWS0X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ChPedDxLUcaDWS0X .rough-node .label text,#mermaid-svg-ChPedDxLUcaDWS0X .node .label text,#mermaid-svg-ChPedDxLUcaDWS0X .image-shape .label,#mermaid-svg-ChPedDxLUcaDWS0X .icon-shape .label{text-anchor:middle;}#mermaid-svg-ChPedDxLUcaDWS0X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ChPedDxLUcaDWS0X .rough-node .label,#mermaid-svg-ChPedDxLUcaDWS0X .node .label,#mermaid-svg-ChPedDxLUcaDWS0X .image-shape .label,#mermaid-svg-ChPedDxLUcaDWS0X .icon-shape .label{text-align:center;}#mermaid-svg-ChPedDxLUcaDWS0X .node.clickable{cursor:pointer;}#mermaid-svg-ChPedDxLUcaDWS0X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ChPedDxLUcaDWS0X .arrowheadPath{fill:#333333;}#mermaid-svg-ChPedDxLUcaDWS0X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ChPedDxLUcaDWS0X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ChPedDxLUcaDWS0X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ChPedDxLUcaDWS0X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ChPedDxLUcaDWS0X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ChPedDxLUcaDWS0X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ChPedDxLUcaDWS0X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ChPedDxLUcaDWS0X .cluster text{fill:#333;}#mermaid-svg-ChPedDxLUcaDWS0X .cluster span{color:#333;}#mermaid-svg-ChPedDxLUcaDWS0X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ChPedDxLUcaDWS0X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ChPedDxLUcaDWS0X rect.text{fill:none;stroke-width:0;}#mermaid-svg-ChPedDxLUcaDWS0X .icon-shape,#mermaid-svg-ChPedDxLUcaDWS0X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ChPedDxLUcaDWS0X .icon-shape p,#mermaid-svg-ChPedDxLUcaDWS0X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ChPedDxLUcaDWS0X .icon-shape .label rect,#mermaid-svg-ChPedDxLUcaDWS0X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ChPedDxLUcaDWS0X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ChPedDxLUcaDWS0X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ChPedDxLUcaDWS0X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} unmapped_fields 模式
默认: 忽略未映射字段
(查询中引用会报错)
nullify: 未映射字段显示为 null
(保留列,值为空)
load: 从 _source 读取实际值
(查询时加载)
- 默认(未设置):引用未映射字段会导致错误,确保 schema 严格。
"nullify":未映射字段在结果中作为null列出现,不报错,但也不实际读取。"load":核心模式 ,查询引擎会在需要时从_source加载这些字段的值,并缓存以供后续使用。
5.4 使用示例
esql
SET unmapped_fields="load";
FROM otel-logs-*
| WHERE log.level IN ("error", "warn")
| STATS errors = COUNT(*), latest = MAX(@timestamp)
BY service.name, resource.cost_center
| SORT errors DESC
假设 service.name 和 resource.cost_center 并未在映射中定义,开启 load 后,查询仍然成功执行,ES|QL 会从每个文档的 _source 中提取这些字段的值。
5.5 原理剖析
- 读取时机 :仅在查询执行期间,当管道需要访问该字段时,才会从
_source中解析 JSON 并提取值。这带来了灵活性,但会增加查询的 CPU 和 I/O 开销 (因为需要读取和解析_source)。 - 缓存:对于多次引用的字段,ES|QL 会在内部缓存提取结果,避免重复解析。
- 性能考量 :适用于探索性分析 或低基数查询,对于高频、高性能要求的场景,仍建议将常用字段映射为常规字段(并重新索引)。
注 :
JSON_EXTRACT是更底层的工具,适合从嵌套 JSON 或flattened字段中提取特定路径。未来,原生flattened字段支持将进一步优化这一流程。
6. 时区支持(Timezone Support,GA)
在时序数据分析中,时区处理一直是个痛点。以前,你需要在每个日期函数中单独指定时区,或者在后处理中转换。现在,SET time_zone 成为全局配置,一次设置,整个查询中的所有日期时间操作自动转换。
6.1 使用方法
esql
SET time_zone="America/Los_Angeles";
FROM error_triage
| EVAL hour = DATE_TRUNC(1 hour, @timestamp)
| STATS errors = COUNT(*) BY hour, service
| SORT hour DESC
效果:
DATE_TRUNC按洛杉矶时区截断时间。- 聚合结果中的
@timestamp输出会带上时区偏移量(例如2026-04-09T11:00:00.000-07:00而非原始的...T18:00:00.000Z)。 - 所有日期比较、聚合、排序都基于同一时区。
6.2 原理解释
ES|QL 在内部将 @timestamp 这样的日期时间字段视为 UTC 时间戳。设置 time_zone 后,所有日期操作在内部先进行时区转换,然后再处理。这避免了每个函数单独传递时区参数的繁琐,也保证了结果的一致性。
7. LIMIT BY:原生分组 Top‑N(Tech Preview)
7.1 痛点
传统 SORT ... LIMIT N 只能返回全局前 N 条记录。如果要"按服务分组,分别返回每个服务中错误数最多的 3 种错误类型",你必须用复杂的子查询或后处理脚本。LIMIT BY 解决了这个原生需求。
7.2 语法与示例
esql
FROM error_triage
| STATS cnt = COUNT(*) BY service, error_detail
| SORT cnt DESC
| LIMIT 3 BY service
语义 :按 service 字段分组,对每组内按 cnt 降序排序,取每组前 3 行。数字 3 在 BY 之前。
7.3 内部机制
#mermaid-svg-LGVo8RbIoeQ1oMWe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LGVo8RbIoeQ1oMWe .error-icon{fill:#552222;}#mermaid-svg-LGVo8RbIoeQ1oMWe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LGVo8RbIoeQ1oMWe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .marker.cross{stroke:#333333;}#mermaid-svg-LGVo8RbIoeQ1oMWe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LGVo8RbIoeQ1oMWe p{margin:0;}#mermaid-svg-LGVo8RbIoeQ1oMWe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster-label text{fill:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster-label span{color:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster-label span p{background-color:transparent;}#mermaid-svg-LGVo8RbIoeQ1oMWe .label text,#mermaid-svg-LGVo8RbIoeQ1oMWe span{fill:#333;color:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .node rect,#mermaid-svg-LGVo8RbIoeQ1oMWe .node circle,#mermaid-svg-LGVo8RbIoeQ1oMWe .node ellipse,#mermaid-svg-LGVo8RbIoeQ1oMWe .node polygon,#mermaid-svg-LGVo8RbIoeQ1oMWe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .rough-node .label text,#mermaid-svg-LGVo8RbIoeQ1oMWe .node .label text,#mermaid-svg-LGVo8RbIoeQ1oMWe .image-shape .label,#mermaid-svg-LGVo8RbIoeQ1oMWe .icon-shape .label{text-anchor:middle;}#mermaid-svg-LGVo8RbIoeQ1oMWe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .rough-node .label,#mermaid-svg-LGVo8RbIoeQ1oMWe .node .label,#mermaid-svg-LGVo8RbIoeQ1oMWe .image-shape .label,#mermaid-svg-LGVo8RbIoeQ1oMWe .icon-shape .label{text-align:center;}#mermaid-svg-LGVo8RbIoeQ1oMWe .node.clickable{cursor:pointer;}#mermaid-svg-LGVo8RbIoeQ1oMWe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .arrowheadPath{fill:#333333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LGVo8RbIoeQ1oMWe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LGVo8RbIoeQ1oMWe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LGVo8RbIoeQ1oMWe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster text{fill:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe .cluster span{color:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LGVo8RbIoeQ1oMWe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LGVo8RbIoeQ1oMWe rect.text{fill:none;stroke-width:0;}#mermaid-svg-LGVo8RbIoeQ1oMWe .icon-shape,#mermaid-svg-LGVo8RbIoeQ1oMWe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LGVo8RbIoeQ1oMWe .icon-shape p,#mermaid-svg-LGVo8RbIoeQ1oMWe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LGVo8RbIoeQ1oMWe .icon-shape .label rect,#mermaid-svg-LGVo8RbIoeQ1oMWe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LGVo8RbIoeQ1oMWe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LGVo8RbIoeQ1oMWe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LGVo8RbIoeQ1oMWe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入行
分组操作
(BY service)
每组内部排序
(SORT cnt DESC)
每组保留前 N 条
合并输出
LIMIT BY 是在聚合后、最终输出前的一个管道操作,它利用了分组后的局部排序能力,每个组独立维护一个小根堆,只需扫描一次数据即可完成。
8. 其他重要更新
8.1 FIRST / LAST / EARLIEST / LATEST(GA)
这些聚合函数返回某个分组中,按指定排序字段的最早或最晚记录所对应的值。
| 函数 | 参数 | 说明 |
|---|---|---|
FIRST(value, sort_field) |
值字段,排序字段 | 返回排序后第一条记录的 value |
LAST(value, sort_field) |
值字段,排序字段 | 返回排序后最后一条记录的 value |
EARLIEST(value) |
值字段 | 隐式按 @timestamp 排序,等价于 FIRST(value, @timestamp) |
LATEST(value) |
值字段 | 隐式按 @timestamp 排序,等价于 LAST(value, @timestamp) |
示例 :按 source.ip 分组,统计每个 IP 的首次和末次失败时间,以及首次失败时的用户名。
esql
FROM svc-auth-*
| WHERE event.outcome == "failure"
| STATS first_seen = FIRST(@timestamp, @timestamp),
last_seen = LAST(@timestamp, @timestamp),
first_user = EARLIEST(user.name),
attempts = COUNT(*)
BY source.ip
| SORT attempts DESC
8.2 URI_PARTS / USER_AGENT / REGISTERED_DOMAIN(新管道命令)
这三个命令将单个字段展开为多个结构化输出列,极大地简化了解析工作。
| 命令 | 输入字段 | 输出列(示例) |
|---|---|---|
URI_PARTS target = source_field |
URL 字符串 | target.domain, target.path, target.scheme, target.port, target.query 等 |
USER_AGENT target = source_field |
User-Agent 字符串 | target.name, target.version, target.os.name, target.device 等 |
REGISTERED_DOMAIN target = source_field |
域名字符串 | target.registered_domain, target.top_level_domain, target.subdomain |
用法示例:
esql
FROM svc-gateway-*
| WHERE http.response.status_code >= 400
| URI_PARTS parts = url.full
| STATS errors = COUNT(*) BY parts.domain, parts.path
| SORT errors DESC
这些命令在内部使用 Lucene 或 Elasticsearch 内置的解析器,效率远高于自定义脚本。
9. Lookup Join 优化(性能增强)
Lookup Join(在 9.1 版本引入)用于将主查询的每一行与一个较小的"查找索引"进行关联,常用于数据丰富(enrichment)。本次发布带来了两项重要优化:
9.1 Lucene 结构复用
- 问题:每次执行 lookup join,都需要打开查找索引的 doc values 和 TermsEnum 结构,涉及磁盘 I/O。
- 优化 :现在,对于相同查找索引的重复查询,这些底层结构会被缓存并跨查询复用,避免重复读取。
- 适用场景:典型的丰富模式------主查询有大量行,查找索引相对较小且被频繁使用。例如,将 IP 地址映射到地理位置信息。
9.2 单关键字连接加速
- 对于连接键为单个 keyword 字段的最常见情况,ES|QL 使用了一条更快的执行路径,减少了每行连接的开销(例如减少了类型转换和字典查找的代价)。
这两项优化使得 lookup join 在生产环境中更具实用性,尤其适合高吞吐量的数据丰富需求。
总结
| 功能 | 状态 | 核心价值 |
|---|---|---|
| 逻辑视图 | Tech Preview | 查询复用,一处定义,全局生效 |
| 子查询(FROM 中) | Tech Preview | 合并不同 schema 的索引,各分支独立处理 |
| Schema-on-Read(未映射字段) | Tech Preview | 无需 reindex,查询时按需读取 _source |
| 时区支持 | GA | 全局时区设置,简化日期时间处理 |
| LIMIT BY | Tech Preview | 原生分组 Top‑N,无需后处理 |
| FIRST / LAST / EARLIEST / LATEST | GA | 分组内极值关联取值 |
| URI_PARTS / USER_AGENT / REGISTERED_DOMAIN | 新命令 | 解析结构化字段为多列 |
| Lookup Join 优化 | 性能提升 | 缓存复用 + 单键加速,更适合丰富场景 |
| VALUES / MV_EXPAND / FORK | GA | 多值字段操作与并行执行分支 |
| SPARKLINE / MV_UNION / MV_DIFFERENCE / MV_INTERSECTS | Tech Preview | 内置迷你图与集合运算 |
如何用:
- 所有 Tech Preview 功能已包含在 Elasticsearch 的最新版本中(Serverless 中视图暂不可用)。
- 可以在 Kibana 的 Dev Tools 或 Discover 中直接尝试。
重要提示:Tech Preview 功能可能在未来版本中变更,且不享受 GA 功能的 SLA 支持。功能的上线时间由 Elastic 自行决定。
通过上述能力,ES|QL 正在从一个单纯的查询语言,演变为一个完整的数据访问与组合层。逻辑视图、子查询和 Schema-on-Read 三驾马车,让数据建模变得更加动态、灵活,也大大降低了运维成本。