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 = searchAfter
    return 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.ConfigCompatibleWithStandardLibrary
    docs := 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)
    }
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/944176.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

精读DeepSeek v3技术文档的心得感悟

最近宋大宝同学读完了DeepSeekv3的文档,心中颇多感慨,忍不住想在这里记录一下对这款“业界有望启示未来低精度训练走向”的开源大模型的观察与思考。DeepSeek v3的亮点绝不仅仅是“Float8”或“超长上下文”这么简单,而是贯穿了从数值精度、注…

WAV文件双轨PCM格式详细说明及C语言解析示例

WAV文件双轨PCM格式详细说明及C语言解析示例 一、WAV文件双轨PCM格式详细说明1. WAV文件基本结构2. PCM编码方式3. 双轨PCM格式详细说明二、C语言解析WAV文件的代码示例代码说明一、WAV文件双轨PCM格式详细说明 WAV文件是一种用于存储未压缩音频数据的文件格式,广泛应用于音频…

Day1 微服务 单体架构、微服务架构、微服务拆分、服务远程调用、服务注册和发现Nacos、OpenFeign

目录 1.导入单体架构项目 1.1 安装mysql 1.2 后端 1.3 前端 2.微服务 2.1 单体架构 2.2 微服务 2.3 SpringCloud 3.微服务拆分 3.1 服务拆分原则 3.1.1 什么时候拆 3.1.2 怎么拆 3.2 拆分购物车、商品服务 3.2.1 商品服务 3.2.2 购物车服务 3.3 服务调用 3.3.1 RestTemplate 3.…

DeepSpeed 使用 LoRA 训练后文件结构详解

DeepSpeed 使用 LoRA 训练后文件结构详解 在大语言模型(LLM)的训练过程中,DeepSpeed 提供了强大的分布式训练能力,而 LoRA(Low-Rank Adaptation)通过参数高效微调技术显著减少了资源占用。完成训练后&…

Llama 3 预训练(二)

目录 3. 预训练 3.1 预训练数据 3.1.1 网络数据筛选 PII 和安全过滤 文本提取与清理 去重(De-duplication) 启发式过滤(Heuristic Filtering) 基于模型的质量过滤 代码和数学推理数据处理 多语言数据处理 3.1.2 确定数…

Autoware Universe 安装记录

前提: ubuntu20.04,英伟达显卡。 ROS2-Galactic安装 wget http://fishros.com/install -O fishros && . fishros 选择galactic(ROS2)版本,桌面版 ROS2-dev-tools安装 sudo apt install python3-testresources sudo apt update …

【小程序】自定义组件的data、methods、properties

目录 自定义组件 - 数据、方法和属性 1. data 数据 2. methods 方法 3. properties 属性 4. data 和 properties 的区别 5. 使用 setData 修改 properties 的值 自定义组件 - 数据、方法和属性 1. data 数据 在小程序组件中,用于组件模板渲染的私有数据&…

socket编程(C++/Windows)

相关文章推荐&#xff1a; Socket 编程基础 面试官&#xff0c;不要再问我三次握手和四次挥手 TCP的三次握手与四次挥手 参考视频&#xff1a; https://www.bilibili.com/video/BV1aW4y1w7Ui/?spm_id_from333.337.search-card.all.click TCP通信流程 服务端 #include<…

linux自动化一键批量检查主机端口

1、准备 我们可以使用下面命令关闭一个端口 sudo iptables -A INPUT -p tcp --dport 端口号 -j DROP我关闭的是22端口&#xff0c;各位可以关其它的或者打开其它端口测试&#xff0c;谨慎关闭22端口&#xff01;不然就会像我下面一样握手超时&#x1f62d;&#x1f62d;&…

实验五 时序逻辑电路部件实验

一、实验目的 熟悉常用的时序逻辑电路功能部件&#xff0c;掌握计数器、了解寄存器的功能。 二、实验所用器件和仪表 1、双 D触发器 74LS74 2片 2、74LS162 1片 3、74194 1片 4、LH-D4实验仪 1台 1.双…

开源轻量级文件分享服务Go File本地Docker部署与远程访问

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

flask基础

from flask import Flask, requestapp Flask(__name__)# app.route(/) # def hello_world(): # put applications code here # return Hello World!app.route(/) # 路由 当用户访问特定 URL 时&#xff0c;Flask 会调用对应的视图函数来处理请求 def index():return …

WPF使用OpenCvSharp4

WPF使用OpenCvSharp4 创建项目安装OpenCvSharp4 创建项目 安装OpenCvSharp4 在解决方案资源管理器中&#xff0c;右键单击项目名称&#xff0c;选择“管理 NuGet 包”。搜索并安装以下包&#xff1a; OpenCvSharp4OpenCvSharp4.ExtensionsOpenCvSharp4.runtime.winSystem.Man…

社媒运营专线 - SD-WAN 跨境网络专线 —— 外贸企业社媒平台的专属 “快车道”

在当今全球化的商业浪潮中&#xff0c;社交媒体平台已成为外贸企业拓展国际市场、提升品牌知名度和促进业务增长的关键阵地。然而&#xff0c;网络访问速度慢、IP 不纯净等问题却如影随形&#xff0c;严重制约了企业社媒运营的效率和效果。幸运的是&#xff0c;社媒运营专线 - …

RustDesk内置ID服务器,Key教程

RustDesk内置ID服务器&#xff0c;Key教程 首先需要准备一个域名&#xff0c;并将其指定到你的 rustdesk 服务器 ip 地址上&#xff0c;这里编译采用的是Github Actions &#xff0c;说白了是就workflows&#xff0c;可以创建一些自动化的工作流程&#xff0c;例如代码的检查&a…

代码随想录算法【Day4】

Day4 1.链表的题目&#xff0c;要在草稿纸上模拟清晰后就简单了 2.双指针更加灵活的应用。 3.环形链表多练习。 24. 两两交换链表中的节点 class Solution { public:ListNode* swapPairs(ListNode* head) {ListNode* _dummyHead new ListNode(0); //虚拟头结点_dummyHead…

计算机毕业设计hadoop+spark+hive民宿推荐系统 酒店推荐系统 民宿价格预测 酒店价格 预测 机器学习 深度学习 Python爬虫 HDFS集群

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

win11中win加方向键失效的原因

1、可能是你把win键锁了&#xff1a; 解决办法&#xff1a;先按Fn键&#xff0c;再按win键 2、可能是可能是 贴靠窗口设置 中将贴靠窗口关闭了&#xff0c;只需要将其打开就好了

Unity自定义Inspector属性名特性以及特性自定义布局问题

前言&#xff1a; 在Unity中编辑属性的适合&#xff0c;一般都是显示属性的英文&#xff0c;如果想要改成中文的话又不能改变属性名&#xff0c;那么自定义特性是很好的选择。 一、自定以特性 这一块没有什么要多说的&#xff0c;就是自定义特性 using UnityEngine; #if UNI…

sentinel集成nacos启动报[check-update] get changed dataId error, code: 403错误排查及解决

整合nacos报403错误 因为平台写的一个限流代码逻辑有问题&#xff0c;所以准备使用sentinel来限流。平台依赖里面已经引入了&#xff0c;之前也测试过&#xff0c;把sentinel关于nacos的配置加上后&#xff0c;启动一直输出403错误 [fixed-10.0.20.188_8848-test] [check-upda…