es日期直方图聚合必须用date_histogram而非terms,需指定calendar_interval(小写)、field(date类型字段不加.keyword),结果在aggregations下buckets中获取。

ES 日期直方图聚合必须用 date_histogram,不是 terms
直接对日期字段用 terms 聚合,结果会是毫秒级时间戳的离散值,根本没法按天/月分组。Laravel 里调用官方客户端时,得明确用 date_histogram 类型,并指定 calendar_interval 参数。
常见错误现象:返回一堆重复的、带毫秒的长字符串,比如 "2024-05-01T08:30:45.123Z",每条记录都不同,聚合失效。
-
calendar_interval值必须小写,如"day"、"month",不能写成"Day"或"MONTH" - 字段名要带
.keyword后缀?错——日期类型字段(date)不用加后缀,加了反而报illegal_argument_exception - ES 8.x 默认禁用
fielddata,别在日期字段上误配"fielddata": true,这属于文本字段配置,对 date 字段无效
Laravel 中构造 date_histogram 聚合的正确写法
别拼 JSON 字符串,用官方客户端的数组 DSL。核心结构固定:外层 aggs → 内层聚合名 → date_histogram → 必填 field 和 calendar_interval。
示例:按天汇总订单创建量
$params = [
'index' => 'orders',
'body' => [
'aggs' => [
'by_day' => [
'date_histogram' => [
'field' => 'created_at', // 确保 mapping 是 date 类型
'calendar_interval' => 'day',
'format' => 'yyyy-MM-dd'
]
]
]
]
];
$response = $client->search($params);
-
format仅控制返回桶的 key 显示格式,不影响计算逻辑;不设也能查,但 key 是时间戳数字 - 如果想按月,把
'calendar_interval' => 'day'换成'calendar_interval' => 'month'即可 - ES 8.x 不再支持
interval(已被calendar_interval/fixed_interval替代),用错会报unknown field [interval]
时区偏移导致聚合结果错位怎么办
默认按 UTC 统计,你看到的 “2024-05-01” 桶,实际可能对应你本地时间的 4 月 30 日晚 16:00 到 5 月 1 日 16:00。尤其跨日业务(如凌晨下单)容易漏数据。
- 加
time_zone参数校正,例如'time_zone' => '+08:00'(中国标准时间) - 不要依赖 PHP 的
date_default_timezone_set(),ES 不读这个 - 若用 Laravel Scout + elasticsearch driver,它不透传
time_zone,得绕过 Scout 直接调$client->search() - 注意:ES 集群配置的
cluster.routing.allocation.disk.threshold_enabled等参数不影响聚合,纯属干扰项
聚合结果怎么取数?别遍历 hits
直方图结果不在 hits 里,而在 aggregations 下对应聚合名的 buckets 数组中。新手常循环 $response['hits']['hits'],结果空跑一整页。
正确路径:
$buckets = $response['aggregations']['by_day']['buckets'] ?? [];
foreach ($buckets as $bucket) {
echo $bucket['key_as_string'] . ': ' . $bucket['doc_count'] . "\n";
}
-
key_as_string是 format 后的字符串(如"2024-05-01"),key是毫秒时间戳数字 - 桶数量受
size控制,默认只返回 10 个,大数据量需显式加'size' => 1000(但别设太大,ES 有默认限制) - ES 8.x 移除了
top_hits子聚合的from/size全局限制,但 date_histogram 本身没分页机制,想覆盖更长时间范围,得靠min_doc_count和客户端补零逻辑











