From Size 方式分页查询 demo
func TestESQueryDemo(client *elastic.Client) {
// ES SDK 教程:https://www.yisu.com/zixun/694102.html
query := elastic.NewBoolQuery().
Must(elastic.NewMatchQuery("lastname", "smith")).
Filter(elastic.NewTermQuery("age", "20")).
Filter(elastic.NewRangeQuery("modify_time").Format("yyyy-MM-dd").Gte("2021-07-22").Lt("2021-07-23"))
ctx := context.Background()
index := "info"
result, err := client.Search(index).Query(query).Size(20).From(0).Do(ctx)
if err != nil {
logs.Error("err=%v", err)
}
for page, hit := range result.Hits.Hits {
//var t Employee
//err := json.Unmarshal(*hit.Source, &t) //另一种取出的方法
//if err != nil {
// fmt.Println("failed")
//}
//fmt.Printf("employee name %s:%s\n", t.FirstName, t.LastName)
logs.Info("page=%v, hit=%v", page, hit)
}
logs.Info("result=%v, err=%v", *result.Hits, err)
}
其他 api 使用参考:go语言操作es的方法
Scroll 分页查询 demo
Form Size 方式最多查一万条记录(是总共 1w,不是单次 1w,就算每次只查 1 条,查 10000 次之后也会报错),即 from + size <= 10000,当索引第 10001 条数据时,会报错,比如当我设置 from=10000,size=1,查询时提示 Type: illegal_argument_exception, Reason: Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting.
当所请求的数据总量大于1w时,可用scroll来代替from+size。
scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询。游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引视图一样。
func TestESQueryDemo(client *elastic.Client) {
defer logs.Flush()
query := elastic.NewBoolQuery().
Must(elastic.NewMatchQuery("lastname", "smith")).
Filter(elastic.NewTermQuery("age", "20")).
Filter(elastic.NewRangeQuery("modify_time").Format("yyyy-MM-dd").Gte("2021-07-22").Lt("2021-07-23"))
ctx := context.Background()
index := "info"
result, err := client.Scroll(index).
Query(query).
Scroll("8s"). // 这个时间只需要能查到第一次时间片就够了,不是查询所有文档的时间
Size(200).
Do(ctx)
scrollID := result.ScrollId
logs.Info("total = %d, result=%v, err=%v", result.TotalHits(), *result.Hits, err)
for {
result, err = client.Scroll("2s").ScrollId(scrollID).Do(ctx) // 每次查询都会使用这个时间续期
logs.Info("total = %d, result=%v, err=%v", result.TotalHits(), *result.Hits, err)
if len(result.Hits.Hits) <= 0 {
break
}
logs.Info("scrollID = %v", result.ScrollId)
time.Sleep(1 * time.Second)
}
// 清除游标
_, err = client.ClearScroll().ScrollId(result.ScrollId).Do(ctx)
}
关于 Scroll 查询返回 返回 EOF 错误
使用 Scroll 来分页查询,如果首次查询时发现无有效记录,即首次查询结果为空时,那么会返回 EOF 错误,而不是空的结果列表。此时,需要我们做一个判断,究竟是 EOF 错误则认为是返回结果为空,否则认为是发生了错误
if err == io.EOF {
fmt.Println("the error equal io.EOF")
}
if err.Error() == "EOF" {
fmt.Println("the error.Error() equal io.EOF")
}
fmt.Println(err) // 查询错误,而非数据为空
关于 Scroll 时间参数的说明
启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。
游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。
这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该早点儿释放掉。
设置这个超时能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。
参考:es scroll 时间_游标查询 Scroll | Elasticsearch: 权威指南 | Elastic、Elasticsearch的滚动查询---Scroll,解决ES每次最多查一万笔数据的问题
两种分页查询方式的对比
ES对于from+size的个数是有限制的,二者之和不能超过1w。当所请求的数据总量大于1w时,可用scroll来代替from+size。
ES的搜索是分2个阶段进行的,即Query阶段和Fetch阶段。 Query阶段比较轻量级,通过查询倒排索引,获取满足查询结果的文档ID列表。 而Fetch阶段比较重,需要将每个shard的结果取回,在协调结点进行全局排序。 通过From+size这种方式分批获取数据的时候,随着from加大,需要全局排序并丢弃的结果数量随之上升,性能越来越差。
而Scroll查询,先做轻量级的Query阶段以后,免去了繁重的全局排序过程。 它只是将查询结果集,也就是doc id列表保留在一个上下文里, 之后每次分批取回的时候,只需根据设置的size,在每个shard内部按照一定顺序(默认doc_id续), 取回这个size数量的文档即可。
由此也可以看出scroll不适合支持那种实时的和用户交互的前端分页工作,其主要用途用于从ES集群分批拉取大量结果集的情况,一般都是offline的应用场景。 比如需要将非常大的结果集拉取出来,存放到其他系统处理,或者需要做大索引的reindex等等。 不要把 scroll 用于实时请求,它主要用于大数据量的场景。例如:将一个索引的内容索引到另一个不同配置的新索引中。