ES聚合查询必须设size: 0,否则默认返回10条文档;terms聚合字段须为keyword类型,text字段需用.field.keyword子字段;Laravel中需手写JSON结构,且缺失状态计数需PHP层补零。

ES 聚合查询必须设 size: 0
不设 size: 0,Elasticsearch 会默认返回 10 条匹配文档(hits),同时附带聚合结果。这不仅浪费网络带宽和解析开销,还可能干扰前端逻辑——比如你只想要分组数量,却收到一堆无关文档。实际开发中,只要目的纯属统计,就该显式关闭文档返回。
常见错误是漏写或写成 "size": "0"(字符串),ES 会静默忽略;必须是整数 0。另外,size 是 query level 参数,要放在顶层 JSON 中,不是嵌套在 aggs 里。
terms 聚合字段必须是 keyword 类型
对 text 字段直接做 terms 聚合会报错:"Fielddata is disabled on text fields by default"。这是因为 text 字段经过分词,原始值不可用于精确分组。解决方案只有两个:
- 改用对应字段的
.keyword子字段,例如"field": "status.keyword"(推荐,零内存开销) - 强行开启 fielddata(不推荐),需 PUT mapping 并设
"fielddata": true,但会显著增加 JVM 堆内存压力
注意:mapping 一旦创建,text 字段无法直接添加 keyword 子字段,得重建索引或用 copy_to 预埋。
Laravel 中构造 ES 聚合请求体要手动拼装 JSON
Laravel 没有原生 ES 聚合 DSL 构建器,不能像 Eloquent 那样链式调用。你得手写结构清晰的数组,再 json_encode 发给客户端(如 guzzlehttp/guzzle)。
关键点:
-
aggs键名可任意,但后续解析时得用同一字符串取值 - 分组总数要靠
doc_count字段,不是count或value - 若需“全部 + 各状态”双层统计,得用
terms+sum_bucket聚合,或后端二次计算总和
示例片段:
$body = [
'size' => 0,
'aggs' => [
'status_groups' => [
'terms' => ['field' => 'status.keyword', 'size' => 10]
]
]
];
总计行(All)不能靠 ES 单次聚合直接生成
ES 的 terms 聚合只返回有数据的桶,缺失状态(如当前无 failed 记录)不会出现 doc_count: 0 的项。想让结果包含所有预设状态(含 0 计数),必须:
- 先查出所有合法 status 值(从 DB 或配置数组读取)
- 用
terms聚合拿到实际计数 - PHP 层 merge 补零,再加一行
['key' => 'All', 'doc_count' => array_sum($counts)]
别指望 min_doc_count: 0 —— 它只对已出现在倒排索引里的 term 生效,对完全没出现过的值无效。这是 ES 底层设计决定的,绕不开。


















