elasticsearch-esql

elasticsearch-esql

热门

执行 ES|QL(Elasticsearch 查询语言)查询,当用户想要查询 Elasticsearch 数据、分析日志、聚合指标、探索数据,或根据 ES|QL 结果创建图表和仪表板时使用。

542Star
44Fork
更新于 2026/7/22
SKILL.md
只读
名称
elasticsearch-esql
描述

执行 ES|QL(Elasticsearch 查询语言)查询,当用户想要查询 Elasticsearch 数据、分析日志、聚合指标、探索数据,或根据 ES|QL 结果创建图表和仪表板时使用。

Elasticsearch ES|QL

对 Elasticsearch 执行 ES|QL 查询。

什么是 ES|QL?

ES|QL(Elasticsearch 查询语言)是一种用于 Elasticsearch 的管道查询语言。它不同于

  • Elasticsearch 查询 DSL(基于 JSON)
  • SQL
  • EQL(事件查询语言)

ES|QL 使用管道符(|)来串联命令:
FROM index | WHERE condition | STATS aggregation BY field | SORT field | LIMIT n

前提条件: ES|QL 要求查询的索引上启用 _source。禁用了 _source 的索引(例如 "_source": { "enabled": false })会导致 ES|QL 查询失败。

版本兼容性: ES|QL 在 8.11 中引入(技术预览),在 8.14 中正式发布(GA)。像 LOOKUP JOIN(8.18+)、MATCH(8.17+)和 INLINE STATS(9.2+)等功能是在后续版本中添加的。在 8.18 之前的集群上,使用 ENRICH 作为 LOOKUP JOIN 的替代方案(参见生成技巧)。INLINE STATS 和计数器字段的 RATE() 在 9.2 之前没有替代方案。请查看 references/esql-version-history.md 了解各版本的功能可用性。

集群检测: 使用 GET / 响应来确定集群类型和版本:

  • build_flavor: "serverless" — Elastic Cloud Serverless。version.number 跟踪当前正在开发的主线版本(下一个次要版本),因此仅进行语义版本比较的客户端可能会将 Serverless 视为“最新”。不要使用 version.number 来判断功能:如果 build_flavor"serverless",则假定所有 GA 和预览版 ES|QL 功能都可用。
  • build_flavor: "default" — 自管理或 Elastic Cloud Hosted。使用 version.number 判断功能可用性。
  • 快照构建version.number 类似于 9.4.0-SNAPSHOT。去掉 -SNAPSHOT 后缀,使用主版本.次版本进行版本检查。快照构建包含该版本的所有功能以及可能尚未发布的开发中功能——如果查询因未知函数/命令而失败,可能只是该功能尚未落地。Elastic 员工通常使用快照构建进行测试。

环境配置

有关完整的连接配置选项(Elastic Cloud、直接 URL、基本认证、本地开发),请参见 环境设置

运行 node scripts/esql.js test 来验证连接。如果测试失败,请引导用户查看环境设置指南,然后停止。在成功连接测试之前,不要尝试进一步探索。

用法

获取索引信息(用于模式发现)

node scripts/esql.js indices                    # 列出所有索引
node scripts/esql.js indices "logs-*"           # 列出匹配的索引
node scripts/esql.js schema "logs-2024.01.01"   # 获取索引的字段映射

执行原始 ES|QL

node scripts/esql.js raw "FROM logs-* | STATS count = COUNT(*) BY host.name | SORT count DESC | LIMIT 5"

以 TSV 格式输出执行

node scripts/esql.js raw "FROM logs-* | STATS count = COUNT(*) BY component | SORT count DESC" --tsv

TSV 输出选项:

  • --tsv-t:输出为制表符分隔的值(干净,无装饰)
  • --no-header:省略标题行

测试连接

node scripts/esql.js test

指南

  1. 检测部署类型:始终先运行 node scripts/esql.js test。这会检测部署是 Serverless 项目(所有功能可用)还是版本化集群(功能取决于版本)。来自 GET /build_flavor 字段是权威信号——如果它等于 "serverless",则忽略报告的版本号,自由使用所有 ES|QL 功能。

  2. 发现模式(必需——切勿猜测索引或字段名称):

    node scripts/esql.js indices "pattern*"
    node scripts/esql.js schema "index-name"
    

    在生成查询之前始终运行模式发现。索引名称和字段名称因部署而异,无法可靠猜测。即使是听起来很常见的数据(例如“logs”)也可能存在于名为 logs-testlogs-app-*application_logs 的索引中。字段名称可能使用 ECS 点分表示法(source.ipservice.name)或扁平的自定义名称——唯一的方法是检查。

    优先选择简单性: 除非用户明确要求跨多个源的数据,否则查询单个索引。不要使用 COALESCE 组合不同模式的索引,除非特别要求——选择与问题最相关的单个索引。当多个索引包含相似数据时,优先选择模式最完整的索引。

    schema 命令报告索引模式。如果显示 Index mode: time_series,则输出包括数据流名称和可复制的 TS 语法——使用 TS <data-stream>(而不是 FROM)、TBUCKET(interval)(而不是 DATE_TRUNC),并用 SUM(RATE(...)) 包裹计数器字段。在编写任何时间序列查询之前,请阅读 生成技巧 中的完整 TS 部分。您也可以通过 Elasticsearch 索引设置 API 直接检查索引模式:

    curl -s "$ELASTICSEARCH_URL/<index-name>/_settings/index.mode" -H "Authorization: ApiKey $ELASTICSEARCH_API_KEY"
    

    对于 9.4+ 上的 TSDS 索引,优先使用语言内发现命令 METRICS_INFOTS_INFO(均为 GA),而不是检查映射——它们直接枚举指标目录和每个时间序列的维度标签。两者都必须跟在 TS 之后,并且必须在 STATS/SORT/LIMIT 之前。请参见 时间序列查询

    node scripts/esql.js raw "TS metrics-tsds | METRICS_INFO | SORT metric_name" --tsv
    node scripts/esql.js raw "TS metrics-tsds | TS_INFO | KEEP metric_name, dimensions | SORT metric_name" --tsv
    
  3. 为任务选择正确的 ES|QL 功能:在编写查询之前,将用户的意图与最合适的 ES|QL 功能匹配。优先使用单个高级查询,而不是多个基本查询。

    • “查找模式”、“分类”、“对相似消息分组” → CATEGORIZE(field)
    • “尖峰”、“下降”、“异常”、“X 何时变化” → CHANGE_POINT value ON key
    • “随时间变化的趋势”、“时间序列” → STATS ... BY BUCKET(@timestamp, interval) 或 TSDB 的 TS
    • “PromQL”、“Prometheus 查询/仪表板/告警”、sum by (instance) (...)、标签匹配器如 {cluster="prod"}PROMQL 源命令(9.4+ 预览);请参见 PROMQL 命令。对于原生 ES|QL 表达,优先使用 TS
    • “搜索”、“查找匹配的文档” → MATCH(默认)、QSTR(高级布尔)、KQL(Kibana 迁移)。对于内容/文档相关性搜索,请遵循 ES|QL 搜索策略
    • “计数”、“平均值”、“分解” → 带聚合函数的 STATS
  4. 在生成查询之前阅读参考资料

    • 生成技巧 - 关键模式(TS/TBUCKET/RATE、每个聚合的 WHERE、LOOKUP JOIN、CIDR_MATCH)、常见模板和歧义处理
    • 时间序列查询 - 在任何 TS 查询之前阅读:内/外聚合模型、TBUCKET 语法、RATE 约束
    • PROMQL 命令在任何 PROMQL 查询之前阅读:选项、输出模式、限制以及 PROMQLTS 的决策矩阵(9.4+ 预览)
    • ES|QL 完整参考 - 所有命令和函数的完整语法
    • ES|QL 搜索策略 — 用于内容/文档相关性搜索(检索 → 融合 → 重排序)
    • ES|QL 搜索参考 — 全文搜索函数语法(MATCH、QSTR、KQL、评分)
  5. 生成查询,遵循 ES|QL 语法。优先选择最简单的查询来回答问题——除非用户要求,否则不要添加额外的索引、字段或转换。只包含直接回答问题的字段在 KEEP 中。不要添加超出用户指定的额外过滤条件(例如,当用户只说“errors”时,不要添加 OR level == "ERROR")。

    • FROM index-pattern(或时间序列索引的 TS index-pattern)开头
    • 添加 WHERE 进行过滤(在 9.3+ 上使用 TRANGE 进行时间范围)
    • 使用 EVAL 计算字段
    • 使用 STATS ... BY 进行聚合
    • 对于时间序列指标:计数器使用 TS 配合 SUM(RATE(...)),仪表使用 AVG(...),时间分桶使用 TBUCKET(interval)——请参见 生成技巧 中的 TS 部分了解三个关键语法规则
    • 对于检测尖峰、下降或异常,在时间分桶聚合后使用 CHANGE_POINT
    • 根据需要添加 SORTLIMIT
  6. 使用 TSV 标志执行

    node scripts/esql.js raw "FROM index | STATS count = COUNT(*) BY field" --tsv
    

ES|QL 快速参考

版本可用性: 为简洁起见,本节省略了版本注释。请查看 ES|QL 版本历史 了解各 Elasticsearch 版本的功能可用性。

基本结构

FROM index-pattern
| WHERE condition
| EVAL new_field = expression
| STATS aggregation BY grouping
| SORT field DESC
| LIMIT n

常见模式

过滤和限制:

FROM logs-*
| WHERE @timestamp > NOW() - 24 hours AND level == "error"
| SORT @timestamp DESC
| LIMIT 100

按时间聚合:

FROM metrics-*
| WHERE @timestamp > NOW() - 7 days
| STATS avg_cpu = AVG(cpu.percent) BY bucket = DATE_TRUNC(1 hour, @timestamp)
| SORT bucket DESC

按计数取前 N:

FROM web-logs
| STATS count = COUNT(*) BY response.status_code
| SORT count DESC
| LIMIT 10

文本搜索(8.17+): 使用 MATCH 作为全文搜索的默认方式,而不是 LIKE/RLIKE——它明显更快且支持相关性评分。对 text 字段使用 MATCH 通常就足够了——不要添加冗余的关键字相等性过滤器(例如 category == "X")与 MATCH 一起使用,除非用户明确要求过滤。仅当需要高级布尔逻辑、通配符或单表达式多字段搜索时,才使用 QSTRMATCH 的第一个参数必须是一个真实的字段名称——而不是列出多个字段的字符串(例如 "title,content"),也不是多个字段参数;使用 MATCH(a, "q") OR MATCH(b, "q") 组合字段。KQL 从 8.18/9.0+ 开始可用。对于内容/文档搜索用例,请遵循 ES|QL 搜索策略。请参见 ES|QL 搜索参考 获取完整函数指南。

FROM documents METADATA _score
| WHERE MATCH(content, "search terms")
| SORT _score DESC
| LIMIT 20

字符串提取: 使用 DISSECT 进行基于分隔符的结构化模式(首选——生成命名字段),使用 GROK 进行基于正则表达式的提取。对于简单情况,使用 SUBSTRING(s, start, len) 进行固定位置提取,SPLIT(s, delim) 分割为多值,LOCATE(substr, s) 查找字符位置。SPLIT 返回多值——使用 MV_FIRSTMV_LASTMV_SLICE 选择元素。INSTRSTRPOS 不存在——使用 LOCATEREGEXP_EXTRACT 不存在——使用 GROK

// 使用 DISSECT 从电子邮件中提取域名(首选——生成命名字段)
FROM customers
| DISSECT email "%{local}@%{domain}"
| STATS count = COUNT(*) BY domain

// 替代方案:使用 SPLIT 从电子邮件中提取域名
FROM customers
| EVAL domain = MV_LAST(SPLIT(email, "@"))
| STATS count = COUNT(*) BY domain

// 解析 HTTP 日志行
FROM logs-*
| DISSECT message "%{method} %{path} %{status_text}"
| KEEP @timestamp, method, path, status_text

日志分类(Platinum 许可证): 使用 CATEGORIZE 自动将日志消息聚类为模式组。在探索或查找非结构化文本中的模式时,优先使用此方法而不是运行多个 STATS ... BY field 查询。

FROM logs-*
| WHERE @timestamp > NOW() - 24 hours
| STATS count = COUNT(*) BY category = CATEGORIZE(message)
| SORT count DESC
| LIMIT 20

变化点检测(Platinum 许可证): 使用 CHANGE_POINT 检测指标序列中的尖峰、下降和趋势变化。优先使用此方法而不是手动检查时间分桶计数。

FROM logs-*
| STATS c = COUNT(*) BY t = BUCKET(@timestamp, 30 seconds)
| SORT t
| CHANGE_POINT c ON t
| WHERE type IS NOT NULL

时间序列指标: 使用 TS 时,使用 TRANGE 进行时间过滤(9.3+)或完全省略——不要TBUCKET 旁边添加冗余的 WHERE @timestamp > NOW() - ...TBUCKET 持续时间定义了聚合窗口。

// 计数器指标:SUM(RATE(...)) 配合 TBUCKET(duration)
TS metrics-tsds
| WHERE TRANGE(1 hour)
| STATS SUM(RATE(requests)) BY TBUCKET(1 hour), host

// 仪表指标:AVG(...) — 不需要 RATE
TS metrics-tsds
| STATS avg_cpu = AVG(cpu) BY service.name, bucket = TBUCKET(5 minutes)
| SORT bucket

使用 PromQL 语法的时间序列(9.4+ 预览): 当用户明确要求 PromQL、引用 Prometheus 语法(sum by (instance) (...)、标签匹配器如 {cluster="prod"})或正在迁移 Prometheus 仪表板或告警时,使用 PROMQL 源命令。PROMQL 命令接受标准 PromQL,带有可选的 indexstepbucketsstartendscrape_interval 选项,并生成一个表,供 ES|QL 管道的其余部分处理。范围选择器是可选的——省略时,窗口为 max(step, scrape_interval)。否则优先使用 TS(9.4 中 GA)。PROMQL 不支持组修饰符、集合运算符(or/and/unless)或像 histogram_quantilepredict_linearlabel_join 这样的函数——对于这些情况,回退到 TS。请参见 PROMQL 命令 获取完整参考。

// 自适应 Kibana 查询——日期选择器驱动时间范围和步长
PROMQL index=metrics-* sum by (instance) (rate(http_requests_total))

// 命名结果,使用 ES|QL 后处理
PROMQL index=k8s step=1h bytes=(max by (cluster) (network.bytes_in))
| STATS max_bytes = MAX(bytes) BY cluster
| SORT cluster

使用 LOOKUP JOIN 的数据丰富: 基本的 ON 子句在两个索引中按名称匹配字段(LOOKUP JOIN idx ON field_name)。当连接键在源中具有不同名称时,首先使用 RENAME 对齐名称。9.2+ 技术预览还支持表达式谓词(ON expr == expr);请参见 ES|QL 完整参考 了解详情。在 LOOKUP JOIN 之后,查找列可通过其原始字段名称使用——不要使用表限定(例如,写 threat_level,而不是 threat_intel.threat_level)。排序提示: 当问题要求前 N 个结果时,在 LOOKUP JOIN 之前进行 SORTLIMIT 以减少丰富成本。对于一般列表或完整丰富,将 LOOKUP JOIN 放在 FROM/WHERE 之后。

// 字段名称不匹配——在连接前使用 RENAME
FROM support_tickets
| RENAME product AS product_name
| LOOKUP JOIN knowledge_base ON product_name

// 聚合、限制、然后丰富(仅前 N 个)
FROM orders
| STATS total_spent = SUM(total) BY customer_id
| SORT total_spent DESC
| LIMIT 3
| LOOKUP JOIN customers_lookup ON customer_id
| KEEP name, customer_id, total_spent

// 多字段连接(9.2+)
FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment
| KEEP service_name, environment, owner_team

多值字段过滤: 使用 MV_CONTAINS 检查多值字段是否包含特定值。使用 MV_COUNT 计数。

// 按多值成员资格过滤
FROM employees
| WHERE MV_CONTAINS(languages, "Python")

// 查找匹配多个值的条目
FROM employees
| WHERE MV_CONTAINS(languages, "Java") AND MV_CONTAINS(languages, "Python")

// 计数多值条目
FROM employees
| EVAL num_languages = MV_COUNT(languages)
| SORT num_languages DESC

变化点检测(替代示例): 当用户询问尖峰、下降或异常时使用。需要时间分桶聚合、SORT,然后 CHANGE_POINT

FROM logs-*
| STATS error_count = COUNT(*) BY bucket = DATE_TRUNC(1 hour, @timestamp)
| SORT bucket
| CHANGE_POINT error_count ON bucket AS type, pvalue

完整参考

有关完整的 ES|QL 语法,包括所有命令、函数和运算符,请阅读:

错误处理

当查询执行失败时,脚本返回:

  • 生成的 ES|QL 查询
  • Elasticsearch 的错误消息
  • 常见问题的建议

常见问题:

  • 字段不存在 → 在编写查询之前始终使用 get_schemalist_indices。切勿猜测字段或索引名称——它们因部署而异。
  • 类型不匹配 → 使用类型转换函数(TO_STRING、TO_INTEGER 等)
  • 语法错误 → 查看 ES|QL 参考以获取正确的语法。始终对字符串使用双引号,不要使用单引号。
  • 无结果 → 检查时间范围和过滤条件
  • 错误的函数名称 → ES|QL 使用下划线名称:STD_DEV() 而不是 STDDEV()MEDIAN_ABSOLUTE_DEVIATION() 而不是 MAD()。对字符串使用 CONCAT(),而不是 +。使用 CASE(cond, val, ...) 而不是 CASE WHEN...THEN...END
  • 错误的日期部分 → DATE_EXTRACT 使用 ES|QL 部分名称:"hour_of_day" 而不是 "hour""day_of_month" 而不是 "day""month_of_year" 而不是 "month"。使用 DATE_DIFF("day", start, end) 进行日期算术,而不是减法。

示例

# 模式发现
node scripts/esql.js test
node scripts/esql.js indices "logs-*"
node scripts/esql.js schema "logs-2024.01.01"

# 执行查询
node scripts/esql.js raw "FROM logs-* | STATS count = COUNT(*) BY host.name | LIMIT 10"
node scripts/esql.js raw "FROM metrics-* | STATS avg = AVG(cpu.percent) BY hour = DATE_TRUNC(1 hour, @timestamp)" --tsv