如何在Elasticsearch中获取全部符合条件的记录或查询记录总数?
针对你遇到的Elasticsearch查询默认返回10条、无法获取准确总数的问题,我整理了几个实用的解决方案,分场景给你说明:
一、获取符合条件的记录总数
1. 优化现有查询的track_total_hits参数
你已经用到了track_total_hits: true,但可能没注意:即使不指定size,只要保留这个参数,响应里的hits.total.value就能返回准确的总数(默认情况下ES对超过1万的数据只会返回近似值,设track_total_hits: true会强制返回准确数)。
调整后的查询示例:
{ "from": 0, "track_total_hits": true, "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } }, "sort": [{ "added_at": {"order": "desc"} }] }
响应里的hits.total会包含value(准确总数)和relation: "eq"(表示总数准确)。
2. 使用_count API更高效
如果只需要总数、不需要返回文档内容,用_count API是最优选择——它不会返回任何文档,仅统计符合条件的数量,性能远高于普通查询:
{ "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } } }
请求地址为POST /你的索引名/_count,响应里的count字段就是总条数。
二、获取全量符合条件的数据
要一次性获取所有数据,得根据数据量大小选择不同方案,避免内存过载:
1. 直接调整size参数(小数据量场景)
ES默认的max_result_window是10000,也就是说最多能一次性返回1万条数据。如果你的数据量小于1万,可以直接把size设为10000:
{ "from": 0, "size": 10000, "track_total_hits": true, "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } }, "sort": [{ "added_at": {"order": "desc"} }] }
⚠️ 注意:如果数据量超过1万,不要用这种方式——会触发ES的保护机制,要么返回错误,要么截断数据。
2. 使用Scroll API(大数据量批量导出)
Scroll API适合一次性导出大量数据,它会创建一个快照上下文,分批获取数据,直到所有数据拉取完成:
第一步:初始化Scroll,获取scroll_id
{ "size": 1000, // 每次拉取1000条,可根据服务器性能调整 "track_total_hits": true, "scroll": "1m", // 上下文保留1分钟,可根据数据量调整 "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } }, "sort": [{ "added_at": {"order": "desc"} }] }
请求地址为POST /你的索引名/_search?scroll=1m,响应里会返回_scroll_id和第一批数据。
第二步:循环调用Scroll API获取后续数据
用拿到的scroll_id继续请求,直到返回的hits.hits为空:
{ "scroll": "1m", "scroll_id": "你的scroll_id值" }
请求地址为POST /_search/scroll。
第三步:清理Scroll上下文
数据拉取完成后,记得清理上下文释放资源:
{ "scroll_id": ["你的scroll_id值"] }
请求地址为DELETE /_search/scroll。
3. 使用Search After(实时分页场景)
如果需要实时分页(比如前端展示分页数据),Search After比Scroll更轻量——它不需要维护上下文,而是用上一页最后一条数据的排序值作为下一页的起点,避免深度分页的性能问题:
第一步:第一页查询
{ "size": 1000, "track_total_hits": true, "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } }, "sort": [{ "added_at": {"order": "desc"} }, {"_id": "asc"}] // 加上_id确保排序唯一,避免重复/遗漏 }
响应里的每条数据会有sort字段,记录该条的排序值。
第二步:下一页查询
用上一页最后一条数据的sort值作为search_after的参数:
{ "size": 1000, "track_total_hits": true, "query": { "bool": { "filter": [ { "bool": { "must_not": { "exists": { "field": "deleted_at" } } }} ] } }, "sort": [{ "added_at": {"order": "desc"} }, {"_id": "asc"}], "search_after": ["2024-05-20T12:00:00Z", "document_id_123"] // 替换成上一页最后一条的sort值 }
循环这个过程,直到返回的数据为空,说明所有数据已获取。
内容的提问来源于stack exchange,提问作者F128115

