如何做一个与博物馆相关网站,北京今天又出现一例,品牌型网站有哪些,视频素材网站推荐Elasticsearch8.17.0在mac上的安装
Kibana8.17.0在mac上的安装
Elasticsearch检索方案之一#xff1a;使用fromsize实现分页
快速掌握Elasticsearch检索之二#xff1a;滚动查询(scrool)获取全量数据(golang)
1、search_after检索
在前面的文章介绍了fromsize的普通分页…Elasticsearch8.17.0在mac上的安装
Kibana8.17.0在mac上的安装
Elasticsearch检索方案之一使用fromsize实现分页
快速掌握Elasticsearch检索之二滚动查询(scrool)获取全量数据(golang)
1、search_after检索
在前面的文章介绍了fromsize的普通分页查询以及scroll滚动查询获取全量数据其弥补了fromsize只能检索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:querySource []string json:_source,omitemptySize int64 json:size,omitemptyFrom int64 json:from,omitemptyOrderItems []query json:sort,omitemptyTrackTotal bool json:track_total_hits,omitemptySearchAfter []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 (contextfmtosstringstimegithub.com/elastic/go-elasticsearch/v7/esapigithub.com/elastic/go-elasticsearch/v8jsoniter github.com/json-iterator/gogithub.com/liupengh3c/esbuilder
)
// 最外层数据结构
type Documents struct {Shards Shards json:_shardsHits HitOutLayer json:hitsTimedOut bool json:timed_outTook int json:took
}
type Shards struct {Failed int json:failedSkipped int json:skippedSuccessful int json:successfulTotal int json:total
}
type HitOutLayer struct {Hits []Hits json:hitsMaxScore float64 json:max_scoreTotal Total json:total
}
type Hits struct {ID string json:_idIndex string json:_indexScore float64 json:_scoreSource map[string]any json:_sourceType string json:_typeSort []any json:sort
}
type Total struct {Relation string json:relationValue 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)}
}