Elasticsearch检索之三:官方推荐方案search_after检索实现(golang)
Elasticsearch8.17.0在mac上的安装
Kibana8.17.0在mac上的安装
Elasticsearch检索方案之一:使用from+size实现分页
快速掌握Elasticsearch检索之二:滚动查询(scrool)获取全量数据(golang)
1、search_after检索
在前面的文章介绍了from+size的普通分页查询,以及scroll滚动查询获取全量数据,其弥补了from+size只能检索1W条以内的数据的缺憾,但是滚动查询本身也存在缺陷,当es滚动上下文大于500,则无法再次进行检索,此时search_after应运而生,它是带着使命来的。
2、使用Kibana了解search_after使用方法
说明:本地es中的数据共11000条,doc_id字段从1-11000,方便说明问题。
首先构造一个排序查询:
GET /new_tag_202411/_search
{"size": 10,"sort": [{"doc_id": {"order": "asc"}}]
}
这个检索请求发出后,返回的数据【doc_id】从1开始,每次10条,也就是返回doc_id从1-10的数据,这里着重列出返回的第10条数据:

为什么列出第10条数据,因此search_after需要第10条(检索出的最后一条)数据的sort字段的值作为输入:
GET /new_tag_202411/_search
{"size": 10,"sort": [{"doc_id": {"order": "asc"}}],"search_after": [10] // 将第一个检索请求返回的sort字段的值放入此字段
}
这时,检索返回将从doc_id为11的数据开始返回:
之后再将本次返回的最后一条数据sort字段放入下一次的检索条件中,继续下一次的检索,从此周而复始,直到检索完全部数据,这个逻辑和scroll滚动查询替换scroll_id的道理是一样的。
注意,使用search_after进行查询时,from必须设置为0或者-1,否则会报错:

3、esbuilder自研dsl库支持search_after字段
我自己开发的esbuilder库之前没有支持search_after字段,因为之前不知道这个功能😄😄,库地址:
github.com/liupengh3c/esbuilder
search_after字段为一个数组,数组类型都为常规的整形、字符串,相对来说比较简单,因此在dsl结构体中直接增加该字段:
type dsl struct {QueryDsl query `json:"query"`Source []string `json:"_source,omitempty"`Size int64 `json:"size,omitempty"`From int64 `json:"from,omitempty"`OrderItems []query `json:"sort,omitempty"`TrackTotal bool `json:"track_total_hits,omitempty"`SearchAfter []any `json:"search_after,omitempty"`
}
之后实现reciver method,支持对该字段进行赋值:
func (dsl *dsl) SetSearchAfter(searchAfter []any) *dsl {dsl.SearchAfter = searchAfterreturn dsl
}
本小节的内容与search_after的使用关系不大,看不明白也没关系,可以跳过,如果想了解的话可以留言,看到后我一定会第一时间回复。
4、利用search_after全量检索的代码实现(golang)
第一步构造一个带有排序的检索请求,排序的字段最好是每个文档的值唯一:

第二步,设计死循环进行查询,同时获取每次检索结果最后一条sort字段的值赋值给search_after字段,直接检索出所有数据:
for {fmt.Println(dslQuery.BuildJson())search := esapi.SearchRequest{Index: []string{"new_tag_202411"},Body: strings.NewReader(dslQuery.BuildJson()),}resp, err = search.Do(context.Background(), client)if err != nil {fmt.Println("search err:", err.Error())return}err = json.NewDecoder(resp.Body).Decode(&docs)if err != nil {fmt.Println("decode err:", err.Error())return}if len(docs.Hits.Hits) == 0 {fmt.Println("no more data")break}fmt.Println("检索数据数量:", len(docs.Hits.Hits), "doc_id:", docs.Hits.Hits[len(docs.Hits.Hits)-1].Source["doc_id"])dslQuery.SetSearchAfter(docs.Hits.Hits[len(docs.Hits.Hits)-1].Sort)
}
其中,最重要的一行代码:
dslQuery.SetSearchAfter(docs.Hits.Hits[len(docs.Hits.Hits)-1].Sort)
这行代码是在每轮次中更新search_after参数,否则无法实现滚动查询的效果,无法检索全量数据。
为了方便观察,我们将size设置为1000,每次检索1000条,增加打印dsl语句以及检索到数据量、doc_id值:

从上面的打印可以看到,search_after的值一直在更新,这样才能达到检索全量的目标,doc_id值的变化也可以从侧面看出整个全量检索的过程,目标达成啦~~~~~。
5、全部实例代码
github地址:
https://github.com/liupengh3c/career/blob/main/elastic/search_after/main.go
代码:
package main
import ("context""fmt""os""strings""time""github.com/elastic/go-elasticsearch/v7/esapi""github.com/elastic/go-elasticsearch/v8"jsoniter "github.com/json-iterator/go""github.com/liupengh3c/esbuilder"
)
// 最外层数据结构
type Documents struct {Shards Shards `json:"_shards"`Hits HitOutLayer `json:"hits"`TimedOut bool `json:"timed_out"`Took int `json:"took"`
}
type Shards struct {Failed int `json:"failed"`Skipped int `json:"skipped"`Successful int `json:"successful"`Total int `json:"total"`
}
type HitOutLayer struct {Hits []Hits `json:"hits"`MaxScore float64 `json:"max_score"`Total Total `json:"total"`
}
type Hits struct {ID string `json:"_id"`Index string `json:"_index"`Score float64 `json:"_score"`Source map[string]any `json:"_source"`Type string `json:"_type"`Sort []any `json:"sort"`
}
type Total struct {Relation string `json:"relation"`Value int `json:"value"`
}
func main() {SearchFromSize()
}
func SearchFromSize() {st := time.Now()defer func() {fmt.Println("cost:", time.Since(st).Milliseconds(), "ms")}()var json = jsoniter.ConfigCompatibleWithStandardLibrarydocs := Documents{}cert, _ := os.ReadFile("/Users/liupeng/Documents/study/elasticsearch-8.17.0/config/certs/http_ca.crt")client, err := elasticsearch.NewClient(elasticsearch.Config{Username: "elastic",Password: "xpE4DQGWE9bCkoj7WXYE",Addresses: []string{"https://127.0.0.1:9200"},CACert: cert,})if err != nil {fmt.Println("create client err:", err.Error())return}dslQuery := esbuilder.NewDsl()boolQuery := esbuilder.NewBoolQuery()boolQuery.Filter(esbuilder.NewRangeQuery("doc_id").Gte(1))dslQuery.SetQuery(boolQuery)dslQuery.SetFrom(0)dslQuery.SetSize(1000)dslQuery.SetOrder(esbuilder.NewSortQuery("doc_id", "asc"))dsl := dslQuery.BuildJson()search := esapi.SearchRequest{Index: []string{"new_tag_202411"},Body: strings.NewReader(dsl),}resp, err := search.Do(context.Background(), client)if err != nil {fmt.Println("search err:", err.Error())return}err = json.NewDecoder(resp.Body).Decode(&docs)if err != nil {fmt.Println("decode err:", err.Error())return}fmt.Println(docs.Hits.Hits[len(docs.Hits.Hits)-1].Sort)dslQuery.SetSearchAfter(docs.Hits.Hits[len(docs.Hits.Hits)-1].Sort)for {fmt.Println(dslQuery.BuildJson())search := esapi.SearchRequest{Index: []string{"new_tag_202411"},Body: strings.NewReader(dslQuery.BuildJson()),}resp, err = search.Do(context.Background(), client)if err != nil {fmt.Println("search err:", err.Error())return}err = json.NewDecoder(resp.Body).Decode(&docs)if err != nil {fmt.Println("decode err:", err.Error())return}if len(docs.Hits.Hits) == 0 {fmt.Println("no more data")break}fmt.Println("检索数据数量:", len(docs.Hits.Hits), "doc_id:", docs.Hits.Hits[len(docs.Hits.Hits)-1].Source["doc_id"])dslQuery.SetSearchAfter(docs.Hits.Hits[len(docs.Hits.Hits)-1].Sort)}
}
相关文章:
Elasticsearch检索之三:官方推荐方案search_after检索实现(golang)
Elasticsearch8.17.0在mac上的安装 Kibana8.17.0在mac上的安装 Elasticsearch检索方案之一:使用fromsize实现分页 快速掌握Elasticsearch检索之二:滚动查询(scrool)获取全量数据(golang) 1、search_after检索 在前面的文章介绍了fromsize的普通分页…...
hot100_238. 除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
软件测试基础详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 “尽早的介入测试,遇到问题的解决成本就越低” 随着软件测试技术的发展,测试工作由原来单一的寻找缺陷逐渐发展成为预防缺陷,…...
MySQL 备份方案设计之准备事项
MySQL 备份方案设计之准备事项 文章目录 MySQL 备份方案设计之准备事项1.选择合适的备份工具2.其他需要考虑的因素推荐资料 1.选择合适的备份工具 工欲善其事,必先利其器。 目前市面上的 MySQL 备份工具也有很多,整理如下(仅供参考ÿ…...
《计算机网络A》单选题-复习题库解析-最终
目录 151、信道容量计算公式“CW*log2(1S/N)”中,“S/N”表示( ) 152、下面哪一种编码方式不包含同步时钟信息( ) 153、子网划分的根本目的是( ) 154、在传统以太…...
向 SwiftUI 视图注入 managedObjectContext 环境变量导致 Xcode 预览(Preview)崩溃的解决
问题现象 从 SwiftUI 诞生到现在,我们这些秃头码农们早已都习惯了在 Xcode 预览中调试 App 界面了。不过,对于某些场景下向 SwiftUI 视图传递 managedObjectContext 环境变量(environment)总是会导致 Xcode 预览崩溃,这是怎么回事呢? 如上图所示,甚至我们将一个常驻内存…...
Ruby 数据类型
Ruby 数据类型 Ruby,作为一种动态、开放源代码的编程语言,以其简洁明了的语法和强大的功能而闻名。在Ruby中,数据类型是编程的核心组成部分,它们决定了变量可以存储的信息种类以及可以对这些信息执行的操作。Ruby是一种类型安全的…...
复合机器人正以其高效、精准、灵活的特点,逐渐在汽车装配线上崭露头角
随着全球汽车制造业的快速发展,汽车装配线已成为衡量企业生产效率和技术水平的重要标准。传统的装配方式往往依赖于大量的人工操作,这不仅效率低下,还面临着质量不稳定、安全隐患等问题。然而,随着智能科技的飞速进步,…...
Docker + JMeter + InfluxDB + Grafana搭建压测可视化实时监控
一:简单介绍 为了解决上述问题,必须要请出了 InfluxDB + Grafana : InfluxDB :持续型数据库,有时间戳组件,以时间的形式去存储数据; Grafana :一款采用 Go 语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具…...
leetcode 2658. 网格图中鱼的最大数目
题目如下 数据范围 使用并查集来做这道题。 其实按照题目的意思就是让我们求每一个联通的水域可以捞到的最大权值。 我们可以从前往后遍历这个二维数组只需要判断前一个水域和上一个水域是否和当前的(i, j)联通如果有则合并水域,同时用一个weight数组保存每一个联…...
Java 集合 Collection、List、Set
一. Collection 单列集合 1. Collection代表单列集合,每个元素(数据)只包含一个值 2. Collection集合特点 ① List系列集合:添加的元素是有序、可重复、有索引。 ArrayList、LinekdList:有序、可重复,有索引 ② Set系列集合&…...
报错:nginx [emerg] open() etcnginxnginx.conf failed (2 No such file or directory)
报错:nginx: [emerg] open() “/etc/nginx/nginx.conf” failed (2: No such file or directory) 背景:在创建nginx容器时,想把宿主机上的某一目录挂载到容器的/etc/nginx路径,报错"/etc/nginx/nginx.conf" failed (2:…...
基于AI的运维资源调度:效率与智能的双重提升
在现代运维场景中,随着系统复杂性和服务规模的不断增长,传统的资源调度方式已无法满足高效、动态和精准的需求。AI技术的引入为资源调度带来了新的解决方案,通过智能算法和数据驱动,实现了资源分配的自动化与优化。本文将详细探讨…...
自动化办公 | 根据成绩进行自动评级
今天我们将介绍一个常见的自动化办公需求:根据成绩自动评级。通过这篇文章,我们将介绍如何利用Python进行自动化办公,将表格中的成绩根据预定的规则进行评级,并生成一个新的带评级信息的表格。 需求背景 我们有一个表格…...
纯血鸿蒙ArkUI线性布局详解
线性布局说明 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row和Column构建。线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。线性布局的排列方向由所选容器组…...
小程序组件 —— 22 组件案例 - 轮播区域绘制
这一节我们实现轮播图最外层的盒子,也就是把轮播图的最外层搭好,先不给轮播图添加图片,因为图片属于新的组件,组件里面有一些知识点,需要单独分开讲; 回顾一下,在进行传统网页开发时࿰…...
如何判断一个学术论文是否具有真正的科研价值?ChatGPT如何提供帮助?
目录 1.创新性与学术贡献的超级加分✔ 2.科研过程中的各个环节—从0到1✔ 3.创新性与理论深度的完美结合✔ 4.论证与写作的清晰性✔ 5.数据整理和文献回顾——效率与精准并存✔ 6.创新性要求辅助✔ 总结 宝子们,学术论文写作的旅程是不是感觉像是走进了迷雾森…...
【置顶】测试学习笔记整理
一、测试开发体系介绍 1.软件测试概念 (1)【理论】软件测试基础概念:软件测试概念、作用、原则、对象,软件缺陷、测试用例 (2)【理论】软件开发流程扫盲:敏捷开发(XP、SCRUM&#…...
新浪微博Java开发面试题及参考答案
怎么判断两个链表是否相交?怎么优化? 判断两个链表是否相交可以采用多种方法。 一种方法是使用双指针。首先分别遍历两个链表,得到两个链表的长度。然后让长链表的指针先走两个链表长度差的步数。之后,同时移动两个链表的指针,每次比较两个指针是否指向相同的节点。如果指…...
【SQL Server】教材数据库(1)
1 利用sql建立教材数据库,并定义以下基本表: 学生(学号,年龄,性别,系名) 教材(编号,书名,出版社编号,价格) 订购(学号…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
