深入 Kibana 05 - Discover:交互式检索的执行模型
上一篇建立了 KQL→DSL 的翻译管道——Search Source 把 KQL、Lucene query string 和过滤器 pills 组装成一个合法的 ES 请求体。这一篇进入 Discover。Discover 容易被误解成"就是个搜索框加日志表格"。更准确的说法是:Discover 是一个多请求编排的 UI 状态机——打开一次 Discover 并不是发一次 ES 请求,而是至少发三类请求:field_caps 请求(字段侧边栏)、date_histogram 聚合请求(顶部直方图)、以及 document search 请求(文档表格);每次条件变化,三类请求都会按依赖顺序触发。本文只抓一个问题:字段列表怎么来、直方图怎么算、翻页为什么用 search_after,以及这三件事背后共用的 Search Source 父子结构。
Discover 的请求拓扑
1 | |
字段侧边栏:field_caps API
Discover 左侧的字段列表不是直接读 Data View 的字段配置,而是通过 field_caps API 实时查询索引的字段能力集,再与 Data View 的元数据合并渲染。
field_caps 返回的每个字段包含:
1 | |
aggregatable: true 的字段在侧边栏会显示"可视化"快捷按钮;searchable: true 的字段可以在 KQL 里按 field:value 过滤。两者都是 false 的字段在侧边栏仍然列出(通常来自 _ignored 之类的元字段)但标为不可用。
字段侧边栏里的文档频率和 top values(点击字段展开后显示)来自另一类内嵌聚合请求:对该字段发一个 terms 聚合,取 top N 个值及其计数。这个请求是懒触发的,点击字段展开时才发。
直方图:date_histogram 聚合的参数逻辑
顶部柱状图是一次 date_histogram 聚合,核心参数:
1 | |
时间粒度(fixed_interval)由 Discover 根据时间范围动态选择:时间范围越大,粒度越粗(天/小时),时间范围越小,粒度越细(分/秒)。这个计算是客户端逻辑,使目标桶数大约保持在屏幕像素数的合理范围内。
size: 0 是关键:直方图请求只要聚合结果,不需要任何文档,这个请求和文档请求 [3] 是并发发出的,互不阻塞。
自 Kibana 8.x 起,直方图的时间粒度可在 Discover 的图表设置里手动固定;若不手动设置,仍走自动档。
文档表格:search_after 翻页
文档表格的翻页采用 search_after 机制,而不是传统的 from + size。
传统 from + size:
1 | |
问题:ES 需要在内存中排序并丢弃前 10000 条,深度翻页时内存开销随 from 线性增长,默认有 index.max_result_window: 10000 限制。
search_after 机制:
1 | |
search_after 不需要在内存中保留跳过的文档,每次翻页是 O(1) 内存开销(相对于 from)。Discover 文档表格的"加载更多"按钮就是在这个游标上追加。
sort 数组里额外带上 _id 的原因:时间戳相同的文档必须有确定的排序,否则分页边界不稳定,会出现重复或跳过。
列选择与 Saved Search
Discover 的列选择(文档表格里显示哪些字段)直接影响请求 [3] 的 fields 参数——只请求用户选中的字段,不传 _source: true(自 7.x 起 Discover 默认用 fields 参数而非 _source,这样 runtime fields 和 field aliases 才能正确返回)。
点击"Save"保存当前 Discover 视图,实际上写入了一个 type: search 的 Saved Object,它的 attributes 里包含:
1 | |
这个 Saved Object 的 references 数组里有一条指向所用 Data View 的引用。其他组件(Dashboard Embeddable、Visualize 基于 Saved Search 建图)通过 references 找到这个依赖,确保 Data View 存在时才渲染。
上下文文档(Surrounding Documents)
Discover 文档表格每行有一个展开按钮,还有一个"查看上下文文档"链接。点击后,Kibana 额外发两条请求:
1 | |
两条请求的 query 里包含当前 Discover 的全部 filters,只放开时间范围约束,用相邻时间范围替代。结果就是"在当前过滤条件下,这条日志前后各 5 条"。
实验:用 Inspect 面板捕获三类请求
以下步骤在 Kibana 7.x/8.x Discover 界面均可执行:
-
打开 Discover,选择一个日志类 Data View(含
@timestamp字段),设置时间范围为最近 1 小时。 -
点击 Discover 右上角 Inspect 按钮,展开 Requests 标签,观察页面加载时发出了几条请求——应该能看到至少两条:直方图请求(名称通常含 “Chart”/“Histogram”)和文档请求(名称通常含 “Documents”)。
-
展开直方图请求的 Request 面板,验证:
size: 0(不返回文档)aggs.histogram.date_histogram存在,field为 Data View 的时间字段query.bool.filter包含时间范围
-
展开文档请求的 Request 面板,验证:
size等于页面大小(默认 500)sort含时间字段降序 +_id作为决定性排序键fields数组只包含侧边栏选中的列_source: false
-
缩小时间范围到最近 5 分钟,重新观察直方图请求,确认
fixed_interval变细(秒级)。 -
在侧边栏点击一个 keyword 字段展开 top values,打开 Inspect 观察是否新增了一条
terms聚合请求。
模式提炼
1 | |
工程迁移表
| Discover 概念 | 工程类比 |
|---|---|
| field_caps API → 字段侧边栏 | GraphQL introspection → schema explorer |
| date_histogram size:0 → 直方图 | SQL SELECT date_trunc, COUNT(*) GROUP BY 独立查询 |
| 父 Search Source 继承 | React Context / 依赖注入:全局 filter 下沉到子查询 |
| search_after 翻页 | keyset pagination(cursor-based)vs offset pagination |
columns → fields 参数 |
GraphQL field selection,只请求需要的列 |
| Saved Search → Saved Object | 查询书签 / 视图配置持久化 |
| Surrounding Documents | 日志系统"上下文行"功能(Loki LogQL context) |
| Grafana Explore | 与 Discover 同构的日志探索 UI,查询模型不同(Loki/Tempo/Prom) |
常见误解
误解一:“Discover 每次刷新只发一条 ES 请求”。打开 Discover 至少触发三类请求,其中 field_caps 和直方图在很多场景下并发发出,文档请求紧随其后。用 Inspect 面板或浏览器 DevTools Network 标签可以看到全部请求。
误解二:“Discover 的分页是 from+size,翻到一万条后会报错”。Discover 默认用 search_after,不受 index.max_result_window 限制。from+size 限制只影响显式写 from 参数的请求(包括某些旧版 Kibana 的聚合和自定义脚本)。
误解三:“字段侧边栏显示的 top values 是实时的全量统计”。侧边栏的 top values 是对当前时间范围内的 terms 聚合,受时间范围限制,不是对整个索引的全量统计;点击字段展开时才触发,不是预加载。
误解四:“保存 Discover 视图只保存了搜索条件”。Saved Search 对象还保存了列配置、排序、以及完整的 Search Source(含 KQL query、所有 filter pills)。Dashboard 里嵌入的 “Saved Search” panel 实际上是把这整个 Saved Object 加载并运行,包括它的列和过滤器。
练习
-
在 Discover 的 Inspect 面板里找到直方图请求和文档请求,确认两者共享相同的
query.bool.filter内容(除时间范围桶参数外),验证父子 Search Source 继承关系在 network 层的体现。 -
把 Discover 列配置改为只显示
status、method两列,再打开 Inspect 查看文档请求的fields参数——验证确实只请求了这两个字段,而不是_source: true。保存为 Saved Search,用GET api/saved_objects/_find?type=search找到刚保存的对象,读它的columns和searchSourceJSON。 -
思考题:search_after 翻页要求排序键在分页边界上是稳定且唯一的(因此加
_id)。在自己的业务系统里,如果要对一张没有自增主键的宽表实现无限滚动翻页,应该用哪个字段或字段组合作为游标?如果时间戳精度不够(同毫秒多条),有什么兜底策略?
系列导航
| 序号 | 主题 | 状态 |
|---|---|---|
| 00 | 导读:Kibana 的状态存在 Elasticsearch 里 | 已发布 |
| 01 | Kibana 架构:浏览器、Node.js server 与 New Platform | |
| 02 | Saved Object:Kibana 一切状态的统一模型 | |
| 03 | Data View(Index Pattern):查询之前的字段抽象 | |
| 04 | Search Source 与查询翻译:KQL、Lucene 与 Query DSL | 上一篇 |
| 05 | Discover:交互式检索的执行模型 | 本篇 |
| 06 | 聚合式可视化的老路:Visualize 与 bucket/metric | 下一篇 |
| 07-09 | Lens / TSVB / Dashboard | 后续阶段 |
| 10-12 | 告警、上报与自动化 | 后续阶段 |
| 13-15 | 平台、安全与扩展 | 后续阶段 |
| 16-17 | 演进、生态与对比 | 后续阶段 |
参考资料
- Kibana 官方文档 Discover:https://www.elastic.co/guide/en/kibana/current/discover.html
- ES field_caps API:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-caps.html
- ES search_after 文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after
- ES date_histogram aggregation:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-datehistogram-aggregation.html
- Kibana Saved Objects API:https://www.elastic.co/guide/en/kibana/current/saved-objects-api.html
- Kibana 源码 Discover plugin:https://github.com/elastic/kibana/tree/main/src/plugins/discover
