Elasticsearch:如何获取近期未通信的设备列表?
问题:如何对Elasticsearch折叠(collapse)后的结果筛选出30分钟未通信的设备?
我需要获取过去30分钟内未通信的设备列表,计划分三步实现:
- 按设备名称拆分索引
- 获取每个设备的最新通信记录
- 筛选出最新记录时间超过30分钟的设备
目前我已经通过collapse字段按Name.enum折叠索引,并通过inner_hits获取了每个设备的最新记录,对应的请求如下:
GET /<index>/_search { "collapse": { "field": "Name.enum", "inner_hits": { "name": "most_recent", "size": 1, "sort": [{ "Time": { "order": "desc" } }] } } }
我还写了筛选时间超过30分钟的查询语句:
"query": { "bool": { "must_not": { "range": { "Time": { "gt": "now-35m" } } } } }
请问如何将这个筛选条件应用到折叠后的排序结果上?
解决方案
直接将上述query作为主查询会导致逻辑错误:主查询会先过滤掉所有Time > now-35m的文档,若某个设备存在近期通信记录,主查询会保留该设备的旧记录,最终折叠后得到的旧记录会被误判为"未通信",但实际上该设备近期有过通信。
要实现仅保留最新通信时间超过30分钟的设备,更可靠的方式是使用聚合(Aggregation)来完成,步骤如下:
1. 用Terms聚合分组,Top Hits获取最新记录
通过terms聚合按设备名称分组,再用top_hits子聚合获取每组内的最新记录,最后通过bucket_selector聚合筛选出符合时间条件的分组:
GET /<index>/_search { "size": 0, // 不需要返回原始文档,只看聚合结果 "aggs": { "devices": { "terms": { "field": "Name.enum", "size": 10000 // 根据设备数量调整,确保覆盖所有设备 }, "aggs": { "most_recent_record": { "top_hits": { "size": 1, "sort": [{ "Time": { "order": "desc" } }] } }, "filter_inactive_devices": { "bucket_selector": { "buckets_path": { "latestTime": "most_recent_record>hits.hits.0._source.Time" }, "script": "ZonedDateTime.parse(params.latestTime).isBefore(ZonedDateTime.now().minusMinutes(30))", "params": {} } } } } } }
2. 若坚持使用Collapse的替代方案
如果一定要用collapse,由于Elasticsearch无法直接在post_filter中访问inner_hits的内容,只能先获取所有设备的最新记录,再在客户端对结果进行二次筛选:
- 执行你原本的
collapse查询,获取所有设备的最新记录 - 在客户端遍历返回的结果,检查每个结果的
inner_hits.most_recent.hits[0]._source.Time是否超过30分钟,保留符合条件的设备
这种方案适合设备数量较少的场景,若设备数量庞大,聚合方案的效率更高。
内容的提问来源于stack exchange,提问作者parsley72
相关产品推荐
相关产品推荐

