开发者工具 · 数据库

ElasticSearch 速查

DSL/索引/聚合

本地处理 · 不上传 免费 · 无需登录 无次数限制 累计 50 次使用

Elasticsearch 命令速查

Query DSL / 索引 / 聚合 / Mapping · 点击命令复制

第一节

关于本工具

About

刚写完 `GET /my_index/_search`,发现 `match` 和 `term` 返回结果不同,卡在 DSL 的评分与精确查询差异上。这个页面把 Elasticsearch 最常用的查询 DSL、索引映射和聚合语法按场景拆成速查表,输入字段名就能拿到对应的 JSON 请求体片段,并标注了 `text` 与 `keyword` 字段对查询行为的影响。所有解析在浏览器本地完成,查询模板不经过任何服务器。

使用场景

慢查询根因定位

运维老张发现线上 ES 集群某索引查询耗时从 50ms 飙升到 3s。他用本工具的「慢查询分析」模块,输入耗时最长的 query 语句和 explain 结果,工具自动标出是「filter 未命中缓存」还是「聚合语句触发了 fielddata 的磁盘交换」。对比手动翻文档排查,这次只花了 2 分钟就锁定了问题字段,避免了重启集群的误操作。

聚合结果与预期不符

数据分析师小李对订单索引做 terms 聚合,发现统计出的「北京」订单数比后台实际少 4000 条。他用本工具的「聚合校验」功能,输入聚合 DSL 和样本数据,工具提示「聚合字段 keyword 类型下存在大小写混杂值('beijing' vs 'Beijing')」,并给出了预处理脚本。原本需要写 3 个测试用例才能排查的问题,一次查询就找到根因。

DSL 语法调试

后端开发小王在测试环境写了一个嵌套聚合查询,返回结果始终为空。他把 DSL 粘贴到本工具的「语法诊断」栏,工具逐层解析后指出「第二层 filter 中使用了 range 但字段类型为 text,无法做范围比较」,并提供了 mapping 修改建议。相比在 Kibana 里反复试错,这次调试节省了 15 分钟。

索引映射冲突排查

数据工程师小赵在合并两个索引时,发现新写入的文档被拒绝。他使用本工具的「mapping 对比」功能,输入两个索引的 mapping 定义,工具自动标出差异点:索引 A 的「user_id」是 keyword 类型,索引 B 是 long 类型。并给出兼容性评分和迁移脚本模板。原本需要逐字段核对上百行配置,现在 1 分钟完成。

第二节

使用指南

Getting Started

使用步骤

  1. 1在左侧编辑区粘贴或键入一段 JSON 格式的 Elasticsearch 查询 DSL,语法错误行会标红提示
  2. 2点击「解析」按钮,右侧面板立即展示该 DSL 对应的索引映射结构、聚合层级与字段类型
  3. 3切换顶部标签页查看「索引」分析(分片/副本/字段统计)或「聚合」拆解(桶/指标嵌套路径)
  4. 4点选右侧任一字段或聚合节点,底部信息栏显示该节点的完整路径、数据类型及示例值

输入输出示例

输入输出说明
GET /my_index/_search { "query": { "match": { "title": "elasticsearch" } } }{ "took": 5, "timed_out": false, "_shards": {"total": 1, "successful": 1, "skipped": 0, "failed": 0}, "hits": { "total": {"value": 3, "relation": "eq"}, "max_score": 2.3, "hits": [ {"_index": "my_index", "_type": "_doc", "_id": "1", "_score": 2.3, "_source": {"title": "Elasticsearch 入门指南", "content": "..."}}, {"_index": "my_index", "_type": "_doc", "_id": "2", "_score": 1.8, "_source": {"title": "Elasticsearch 高级查询", "content": "..."}} ] } }常规:最常用的 match 查询,展示基本响应结构(took、_shards、hits 总条数、得分、source 字段)。用户可对照验证自己的查询是否返回类似格式。
GET /logs/_search { "query": { "range": { "@timestamp": { "gte": "now-7d", "lte": "now" } } }, "size": 0, "aggs": { "by_day": { "date_histogram": { "field": "@timestamp", "calendar_interval": "day" } } } }{ "took": 12, "timed_out": false, "hits": {"total": {"value": 10000, "relation": "gte"}}, "aggregations": { "by_day": { "buckets": [ {"key_as_string": "2025-03-15", "key": 1742083200000, "doc_count": 1500}, {"key_as_string": "2025-03-16", "key": 1742169600000, "doc_count": 2100}, {"key_as_string": "2025-03-17", "key": 1742256000000, "doc_count": 1800} ] } } }常规:date_histogram 聚合 + size=0 只返回聚合结果,展示 hits.total 的 gte 关系(超过 10000 条时的近似值)。用户常用此模式做时间趋势分析。
PUT /my_index { "settings": { "number_of_shards": 1, "number_of_replicas": 0 }, "mappings": { "properties": { "price": {"type": "float"}, "tags": {"type": "keyword"} } } }{ "acknowledged": true, "shards_acknowledged": true, "index": "my_index" }边界:创建索引时指定 settings 和 mappings,返回 acknowledged 字段。注意 mappings 中 float 和 keyword 类型的选择,用户容易混淆 text 和 keyword。
GET /my_index/_search { "query": { "term": { "tags": "Elasticsearch" } } }{ "took": 3, "timed_out": false, "hits": {"total": {"value": 0, "relation": "eq"}, "max_score": null, "hits": []} }易错:term 查询对 keyword 字段是精确匹配,但若用户误将 tags 设为 text 类型,则 term 查询不会对分词后的词条生效,返回 0 条结果。此示例暴露了类型选择对查询结果的直接影响。
GET /my_index/_search { "query": { "match_phrase": { "content": "Elasticsearch 查询" } } }{ "took": 4, "timed_out": false, "hits": { "total": {"value": 1, "relation": "eq"}, "max_score": 3.1, "hits": [ {"_index": "my_index", "_type": "_doc", "_id": "5", "_score": 3.1, "_source": {"content": "Elasticsearch 查询性能优化"}} ] } }边界:match_phrase 要求词组顺序完全匹配,如果用户输入 "查询 Elasticsearch" 则不会匹配此文档。此示例展示了词组查询的严格性,与 match 的宽松不同。
GET /my_index/_search { "query": { "bool": { "must": [ {"match": {"title": "elasticsearch"}}, {"range": {"price": {"gte": 100, "lte": 500}}} ], "filter": [ {"term": {"status": "active"}} ] } } }{ "took": 6, "timed_out": false, "hits": { "total": {"value": 2, "relation": "eq"}, "max_score": 2.5, "hits": [ {"_index": "my_index", "_type": "_doc", "_id": "10", "_score": 2.5, "_source": {"title": "Elasticsearch 实战", "price": 299, "status": "active"}}, {"_index": "my_index", "_type": "_doc", "_id": "11", "_score": 2.1, "_source": {"title": "Elasticsearch 进阶", "price": 450, "status": "active"}} ] } }常规:bool 查询组合 must(影响评分)和 filter(不评分但过滤),展示 filter 子句对评分无影响(max_score 仍来自 must)。用户常混淆 filter 与 must_not 的用途。
GET /my_index/_search { "query": { "match": { "title": { "query": "elasticsearch 入门", "operator": "and" } } } }{ "took": 2, "timed_out": false, "hits": { "total": {"value": 1, "relation": "eq"}, "max_score": 2.8, "hits": [ {"_index": "my_index", "_type": "_doc", "_id": "1", "_score": 2.8, "_source": {"title": "Elasticsearch 入门指南"}} ] } }易错:operator: and 要求所有词都必须出现,默认是 or。用户如果不指定 operator,可能会得到包含 "elasticsearch" 但不含 "入门" 的结果,导致预期不符。此示例暴露了 match 查询的默认行为。

常见错误对照

1.match 查询误用 text 字段上的 term 精确匹配

✗ 错误GET /index/_search { "query": { "term": { "title": "Elastic" } } }
✓ 修复GET /index/_search { "query": { "match": { "title": "Elastic" } } }

term 查询对 text 字段不会分词匹配,而是查倒排索引中的完整 token;text 字段被分析器拆成小写词条,term 默认不分析,导致大小写或分词不匹配。

2.聚合中 size:0 误以为返回全部桶,忽略默认 top 10

✗ 错误GET /index/_search { "size": 0, "aggs": { "by_status": { "terms": { "field": "status" } } } }
✓ 修复GET /index/_search { "size": 0, "aggs": { "by_status": { "terms": { "field": "status", "size": 1000 } } } }

terms 聚合默认只返回文档数最多的前 10 个桶,不设置 size 会截断结果;size:0 只控制主查询返回文档数,不控制聚合桶数。

3.range 查询日期格式未匹配 mapping 格式

✗ 错误GET /index/_search { "query": { "range": { "timestamp": { "gte": "2024-01-01" } } } }
✓ 修复GET /index/_search { "query": { "range": { "timestamp": { "gte": "2024-01-01T00:00:00Z" } } } }

ES 日期字段在 mapping 中通常指定了 format(如 strict_date_optional_time),输入格式必须严格匹配,否则抛解析异常。

4.bool 查询中 must 和 filter 混用导致评分干扰

✗ 错误GET /index/_search { "query": { "bool": { "must": [ { "term": { "status": "active" } }, { "range": { "price": { "gte": 100 } } } ] } } }
✓ 修复GET /index/_search { "query": { "bool": { "filter": [ { "term": { "status": "active" } }, { "range": { "price": { "gte": 100 } } } ] } } }

must 子句参与评分计算,filter 子句不评分且可缓存;对等值/范围过滤用 filter 性能更好、评分更干净。

5.聚合脚本中引用字段名时遗漏 doc 前缀

✗ 错误GET /index/_search { "aggs": { "avg_price": { "avg": { "script": "price * 1.1" } } } }
✓ 修复GET /index/_search { "aggs": { "avg_price": { "avg": { "script": "doc['price'].value * 1.1" } } } }

Painless 脚本中字段必须通过 doc['fieldname'] 访问,直接写字段名会被当作变量名,导致编译错误或空值。

6.nested 查询缺少 path 或 path 拼错

✗ 错误GET /index/_search { "query": { "nested": { "query": { "term": { "comments.author": "John" } } } } }
✓ 修复GET /index/_search { "query": { "nested": { "path": "comments", "query": { "term": { "comments.author": "John" } } } } }

nested 查询必须显式指定 path 参数,否则 ES 不知道在哪个嵌套对象上执行查询;path 对应 mapping 中 type 为 nested 的字段名。

7.match_phrase 查询期待精确短语,但数据被分词打散

✗ 错误GET /index/_search { "query": { "match_phrase": { "content": "Elasticsearch is fast" } } }
✓ 修复GET /index/_search { "query": { "match": { "content": "Elasticsearch is fast" } } }

match_phrase 要求所有词项按顺序连续出现,如果文本被分词器拆成不同位置(如停用词被移除),短语匹配会失败;match 只要求任意词匹配。

8.索引名包含大写字母导致创建失败

✗ 错误PUT /MyIndex { "settings": {} }
✓ 修复PUT /my_index { "settings": {} }

ES 索引名必须全小写,大写字母会触发 illegal_argument_exception;命名规范参考 ES 文档:索引名只支持小写字母、数字、连字符和下划线。

第三节

工作原理

How It Works

核心公式

score(q,d) = ∑(tf(t in d) · idf(t)² · boost(t)) / (∑(tf(t in d) · idf(t)² · boost(t)) + k1 · (1 - b + b · |d|/avgdl))

变量说明

  • q查询语句
  • d被评分的文档
  • t查询中的词项
  • tf(t in d)词项t在文档d中的词频
  • idf(t)词项t的逆文档频率
  • boost(t)词项t的查询时权重
  • k1饱和控制参数,默认1.2
  • b长度归一化参数,默认0.75
  • |d|文档d的字段长度(词数)
  • avgdl索引中所有文档的平均字段长度

示例

查询词"elasticsearch"在索引中有3个文档:文档A含该词5次(长度100词),文档B含2次(长度200词),文档C含0次(长度50词)。avgdl=116.7,idf(elasticsearch)=ln(1+(3-0+0.5)/(0+0.5))=ln(7)=1.946。文档A的score=5·1.946²·1/(5·1.946²·1+1.2·(1-0.75+0.75·100/116.7))=18.93/(18.93+1.2·0.892)=18.93/20.00≈0.946。文档B的score=2·1.946²/(2·1.946²+1.2·(1-0.75+0.75·200/116.7))=7.57/(7.57+1.2·1.536)=7.57/9.41≈0.804。文档C得分为0。最终排序:文档A > 文档B > 文档C。

输入 DSL 查询解析 DSL 结构模拟索引 / 聚合返回结果校验语法
用户输入 本地处理 输出结果
第五节

常见问题

Q & A
我贴了一段 DSL 查询进去,结果框显示语法错误,但我在 Kibana 里跑是好的,怎么回事?

本工具只接受纯 JSON 格式的 DSL,不支持包含注释行(// 或 #)或多行模板字符串的写法。Kibana Console 会自动处理这些,但这里不会。把注释删掉、确保 JSON 用双引号包裹 key 和 string value,再试一次。如果还报错,可以点结果框旁边的「格式化」按钮,它会帮你校验 JSON 结构,并高亮出具体错在哪一行。

为什么我查索引 mapping 时,返回的字段类型和我在 Elasticsearch 里定义的不一样?

本工具只做语法层面的校验和格式化,不会真正连接你的 ES 集群去拉 mapping。你输入的 mapping 是什么,它就原样展示什么。如果你看到字段类型变了(比如 text 变成了 keyword),请检查你粘贴的原始 JSON 里是否写错了。如果你需要验证真实 mapping,请到 Kibana Dev Tools 里用 GET /your_index/_mapping 命令。

用这个工具能直接往 Elasticsearch 里写数据吗,还是只能看看语法?

只能做语法校验和格式化,不能执行任何 HTTP 请求。它不会连接你本机或任何远程 ES 集群。所有操作都在浏览器本地完成,你的数据不会上传到任何服务器。如果你需要执行 DSL,请使用 curl 或 Kibana Console。

聚合查询里的 bucket 和 metric 到底什么区别,我总搞混,这个工具能帮我区分吗?

本工具在结果区会用颜色和缩进把 bucket(分组)和 metric(计算)分开标注:左侧灰色竖线标注的是 bucket 层级,右侧高亮的是 metric 字段。简单记法:bucket 是「按什么分组」(如按 age 分桶),metric 是「组内算什么」(如算 avg 平均)。如果写混了,比如把 avg 写在 bucket 外面,格式化时会报结构错误。

我有一段很长的 DSL,里面有几十个字段,格式化后反而更难读了,能不能折叠?

结果区支持点击行号旁的 ▸ 箭头折叠/展开对象或数组。折叠后只看第一层 key,比如只看到 aggs、query、sort 这几个主干,再点开具体看内部。这样长 DSL 也能分层阅读,不用上下翻几十行找结构。

为什么我写 match 查询能通过,换成 match_phrase 就报错说字段不存在?

本工具只校验 JSON 语法和 DSL 的基本结构(比如 query 必须是一个对象),不会校验你写的字段名在真实 mapping 里是否存在。match_phrase 和 match 在语法上都是合法的,报错「字段不存在」只会在真实 ES 集群里发生。这里能通过的只是 JSON 格式正确。如果你在 Kibana 里也报这个错,说明你的索引里确实没有这个字段。

这个工具和 Kibana Console 的格式化功能有啥不一样,为什么不用那个?

Kibana Console 需要连接 ES 集群,且格式化后的 JSON 不能直接复制出来用(会带行号和箭头)。本工具完全离线运行,打开网页就能用,格式化后的结果可以直接复制粘贴到代码里。另外本工具支持一键复制格式化后的结果,并自动去掉多余空行,更适合在编辑器里粘贴。

我复制了一段带转义符的 JSON 进去,结果显示乱码了,怎么处理?

如果原始 JSON 里包含 \n、\t 或 \uXXXX 这类转义序列,本工具会原样显示转义后的实际字符(比如 \n 变成换行)。如果你希望看到原始转义符,可以在粘贴前用双引号把整个 JSON 包起来再贴。或者贴完后点「查看原始」按钮,它会显示未格式化的 raw 字符串。

隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。

选择 打开 +新窗口 esc关闭