通过 Elasticsearch 和 Go 使用混合搜索进行地鼠狩猎
作者:CARLY RICHMOND,LAURENT SAINT-FÉLIX
就像动物和编程语言一样,搜索也经历了不同实践的演变,很难在其中做出选择。 在本系列的最后一篇博客中,Carly Richmond 和 Laurent Saint-Félix 将关键字搜索和向量搜索结合起来,使用 Go 客户端在 Elasticsearch 中寻找地鼠(gopher)。

今天构建软件是对终生学习的承诺。 正如你从本系列前面的博客中看到的那样,Carly 最近开始使用 Go。
搜索经历了不同实践的演变。 在你自己的搜索用例之间做出决定可能很困难。 本系列所有代码均以第一部分中介绍的关键字和向量搜索示例为基础。 请继续阅读第 2 部分及第二部分的代码,了解本系列中的所有代码。 在本系列的第 2 部分中,我们将分享如何使用 Elasticsearch 和 Elasticsearch Go 客户端组合向量搜索和关键字搜索的示例。
先决条件
就像本系列的第一部分一样,此示例需要以下先决条件:
- 安装 Go 版本 1.13 或更高版本
- 使用 Go 文档中介绍的推荐结构和包管理创建您自己的 Go 存储库
- 创建你自己的 Elasticsearch 集群,其中填充了一组基于啮齿动物的页面,包括来自维基百科的我们友好的 Gopher:

连接到 Elasticsearch
提醒一下,在我们的示例中,我们将使用 Go 客户端提供的 Typed API。 为任何查询建立安全连接需要使用以下任一配置客户端:
- 云 ID 和 API 密钥(如果使用 Elastic Cloud)
- 集群 URL、用户名、密码和证书
连接到位于 Elastic Cloud 上的集群如下所示:
func GetElasticsearchClient() (*elasticsearch.TypedClient, error) {var cloudID = os.Getenv("ELASTIC_CLOUD_ID")var apiKey = os.Getenv("ELASTIC_API_KEY")var es, err = elasticsearch.NewTypedClient(elasticsearch.Config{CloudID: cloudID,APIKey: apiKey,Logger: &elastictransport.ColorLogger{os.Stdout, true, true},})if err != nil {return nil, fmt.Errorf("unable to connect: %w", err)}return es, nil
}
然后,client 连接可用于搜索,如后续部分所示。
如果你是使用自己部署的 Elasticsearch 集群,你可以参考文章 “Elasticsearch:运用 Go 语言实现 Elasticsearch 搜索 - 8.x”。
手动配置 boost 参数
当组合任何一组搜索算法时,传统方法是手动配置常量来增强每种查询类型。 具体来说,为每个查询指定一个因素,并将组合结果集与预期集进行比较,以确定查询的召回率。 然后我们重复几组因素并选择最接近我们所需状态的一组。

例如,可以通过在两种查询类型中指定 Boost 字段来将增强系数为 0.8 的单个文本搜索查询与系数较低的 0.2 的 knn 查询组合起来,如下例所示:
func HybridSearchWithBoost(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {var knnBoost float32 = 0.2var queryBoost float32 = 0.8res, err := client.Search().Index("vector-search-rodents").Knn(types.KnnQuery{Field: "text_embedding.predicted_value",Boost: &knnBoost,K: 10,NumCandidates: 10,QueryVectorBuilder: &types.QueryVectorBuilder{TextEmbedding: &types.TextEmbedding{ModelId: "sentence-transformers__msmarco-minilm-l-12-v3",ModelText: term,},}}).Query(&types.Query{Match: map[string]types.MatchQuery{"title": {Query: term,Boost: &queryBoost,},},}).Do(context.Background())if err != nil {return nil, err}return getRodents(res.Hits.Hits)
}
每个查询的 Boost 选项中指定的因子将添加到文档分数中。 通过比 knn 查询更大的因子增加匹配查询的分数,关键字查询的结果的权重更大。
手动提升的挑战是,特别是如果你不是搜索专家,则需要进行调整以找出导致所需结果集的因素。 这只是尝试随机值以查看什么能让你更接近所需结果集的情况。
倒数排序融合 - Reciprocal Rank Fusion
倒数排序融合 (RRF) 在 Elasticsearch 8.9 中的混合搜索技术预览版中发布。 它的目的是减少与调整相关的学习曲线,并减少尝试因素以优化结果集的时间。
- D - 文档集
- R - 一组排名作为 1..|D| 的排列
- K - 通常默认设置为 60

使用 RRF,通过以下算法混合分数来重新计算文档分数:
score := 0.0
// q is a query in the set of queries (vector and keyword search)
for _, q := range queries {// result(q) is the results if document in result(q) {// k is a ranking constant (default 60)// rank(result(q), d) is the document's rank within result(q) // range from 1 to the window_size (default 100)score += 1.0 / (k + rank(result(q), d))}
}return score
使用 RRF 的优点是我们可以利用 Elasticsearch 中合理的默认值。 排名常数 k 默认为 60。为了在大型数据集上搜索时返回文档的相关性和查询性能之间进行权衡,每个考虑的查询的结果集的大小限制为 window_size 的值,默认为 100 如文档中所述。
k 和 windows_size 也可以在 Go 客户端的 Rank 方法中的 Rrf 配置中进行配置,如下例所示:
func HybridSearchWithRRF(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {// Minimum required window size for the default result size of 10var windowSize int64 = 10var rankConstant int64 = 42res, err := client.Search().Index("vector-search-rodents").Knn(types.KnnQuery{Field: "text_embedding.predicted_value",K: 10,NumCandidates: 10,QueryVectorBuilder: &types.QueryVectorBuilder{TextEmbedding: &types.TextEmbedding{ModelId: "sentence-transformers__msmarco-minilm-l-12-v3",ModelText: term,},}}).Query(&types.Query{Match: map[string]types.MatchQuery{"title": {Query: term},},}).Rank(&types.RankContainer{Rrf: &types.RrfRank{WindowSize: &windowSize,RankConstant: &rankConstant,},}).Do(context.Background())if err != nil {return nil, err}return getRodents(res.Hits.Hits)
}
结论
在这里,我们讨论了如何使用 Elasticsearch Go 客户端在 Elasticsearch 中组合向量搜索和关键字搜索。
查看 GitHub 存储库以获取本系列中的所有代码。 如果你还没有查看本系列中的所有代码,请查看第 1 部分和第 2 部分。
快乐地鼠狩猎!
原文:Using hybrid search for gopher hunting with Elasticsearch and Go — Elastic Search Labs
相关文章:
通过 Elasticsearch 和 Go 使用混合搜索进行地鼠狩猎
作者:CARLY RICHMOND,LAURENT SAINT-FLIX 就像动物和编程语言一样,搜索也经历了不同实践的演变,很难在其中做出选择。 在本系列的最后一篇博客中,Carly Richmond 和 Laurent Saint-Flix 将关键字搜索和向量搜索结合起…...
【LIUNX】配置缓存DNS服务
配置缓存DNS服务 A.安装bind bind-utils1.尝试修改named.conf配置文件2.测试nslookup B.修改named.conf配置文件1.配置文件2.再次测试 缓存DNS服务器:只提供域名解析结果的缓存功能,目的在于提高数据查询速度和效率,但是没有自己控制的区域地…...
Arduino驱动A01NYUB防水超声波传感器(超声波传感器)
目录 1、传感器特性 2、控制器和传感器连线图 3、通信协议 4、驱动程序 A01NYUB超声波测距传感器是一款通过发射和接收机械波来感应物体距离的电子传感器。该款产品具有监测距离远、范围广、防水等优点,且具有一定的穿透能力(烟雾、粉尘等)。该产品带有可拆卸式喇叭口,安…...
curl(八)时间和环境变量以及配置
一 时间 ① --connect-timeout 连接超时时间 ② -m | --max-time 数据最大传输时间 -m: 限制curl 完成时间(overall time limit)-m,--max-time <seconds> 整个交互完成的超时时间场景: 通过设置-m参数,可以避免请求时间过长而导致的超时错误…...
K8S知识点(十)
(1)Pod详解-启动命令 创建Pod,里面的两个容器都正常运行 (2)Pod详解-环境变量 (3)Pod详解-端口设置 (4)Pod详解-资源配额 修改:memory 不满足条件是不能正常…...
Netty实现通信框架
一、LengthFieldBasedFrameDecoder的参数解释 1、LengthFieldBasedFrameDecoder的构造方法参数 看下最多参数的构造方法 /*** Creates a new instance.** param byteOrder* the {link ByteOrder} of the length field* param maxFrameLength* the maximum len…...
【OpenCV实现图像:用OpenCV图像处理技巧之白平衡算法】
文章目录 概要加载样例图像统计数据分析White Patch Algorithm小结 概要 白平衡技术在摄影和图像处理中扮演着至关重要的角色。在不同的光照条件下,相机可能无法准确地捕捉到物体的真实颜色,导致图像呈现出暗淡、色调不自然或者褪色的效果。为了解决这个…...
文件包含 [ZJCTF 2019]NiZhuanSiWei1
打开题目 代码审计 if(isset($text)&&(file_get_contents($text,r)"welcome to the zjctf")){ 首先isset函数检查text参数是否存在且不为空 用file_get_contents函数读取text制定的文件内容并与welcome to the zjctf进行强比较 echo "<br><h…...
Java网络编程基础内容
IP地址 域名解析: 本机访问域名时,会从本地的DNS上解析数据(每个电脑都有),如果有,获取其对应的IP,通过IP访问服务器。如果本地没有,会去网络提供商的DNS找域名对应的IP࿰…...
DevChat:开发者专属的基于IDE插件化编程协助工具
DevChat:开发者专属的基于IDE插件化编程协助工具 一、DevChat 的介绍1.1 DevChat 简介1.2 DevChat 优势 二、DevChat 在 VSCode 上的使用2.1 安装 DevChat2.2 注册 DevChat2.3 使用 DevChat 三、DevChat 的实战四、总结 一、DevChat 的介绍 在AI浪潮的席卷下&#x…...
Python数据容器之[列表]
Python数据容器 Python中的数据容器: 一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素 每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。 数据容器根据特点的不同,如: 是否支…...
大咖直播间”系列直播课第一期——如何抓住HarmonyOS带来的机遇?
想了解#HarmonyOS#背后隐藏着怎样的商业机遇? 想成功搭上万物互联快车,与HarmonyOS一起发展壮大? 想知道开发者应该怎样把握时代机遇,实现高质高效就业? 答案尽在#华为开发者学堂#《大咖直播间》第一期课程,…...
跨域:利用JSONP、WebSocket实现跨域访问
跨域基础知识点:跨域知识点 iframe实现跨域的四种方式:iframe实现跨域的四种方式 注:本篇中使用到的虚拟主机也是上面iframe中配置的 目录 JSONP跨域 JSONP介绍 跨域实验: WebSocket跨域 websocket介绍 跨域实验 JSONP跨域 …...
java项目之戒烟网站(ssm+vue)
项目简介 戒烟网站实现了以下功能: 用户可以对首页,用户分享,论坛交流,公告文章,个人中心,后台管理等功能进行操作。 管理员可以对网站所有功能进行管理,包括管理用户的基本信息。 Ǵ…...
Redis集群,你真的学会了吗?
目录 1、为什么引入集群 1.1、先来了解集群是什么 1.2、哨兵模式的缺陷 引入集群解决了什么问题 1.3、使用集群,如何存储数据 2、三种主流的分片方式【经典面试题】 2.1、哈希求余算法 2.1.1、哈希求余算法的介绍 2.1.2、哈希求余算法如何扩容 2.2、一致性…...
手机地磁传感器与常见问题
在手机中,存在不少传感器,例如光距感,陀螺仪,重力加速度,地磁等。关于各传感器,虽功能作用大家都有所了解,但是在研发设计debug过程中,却总是会遇到很多头疼的问题。关于传感器&…...
EF Core 数据库映射成实体类
首先在 NuGet 包管理器中安装三个包 Microsoft.EntityFrameworkCore.SqlServer 是一个用于与 SQL Server 数据库进行交互的实体框架核心包。这个包提供了方便的方法和工具,用于在 .NET Core 应用程序中操作 SQL Server 数据库。 Microsoft.EntityFrameworkCore.Too…...
【算法优选】 动态规划之斐波那契数列模型
文章目录 🎋前言🍀[第 N 个泰波那契数](https://leetcode.cn/problems/n-th-tribonacci-number/)🚩题目描述🚩算法流程🚩代码实现 🎄[使用最小花费爬楼梯](https://leetcode.cn/problems/min-cost-climbing…...
FreeRTOS知识梳理
一、RTOS:Real time operating system,中文意思为 实时操作系统,它是一类操作系统,比如uc/OS、FreeRTOS、RTX、RT-Thread 这些都是实时操作系统。 二、移植FreeRTOS到STM32F103C8T6上 interface选择CMSIS_V1,RCC选择Crystal Ceramic Resonator 。 …...
冒泡排序算法(C++版)
1、什么是冒泡排序? 冒泡排序(Bubble Sort)是一种简单的排序算法,其基本思想是多次遍历待排序的元素序列,每次比较相邻两个元素,如果它们的顺序不正确就交换它们,直到整个序列有序。在每一轮遍…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
