博客

使用 Elasticsearch 和 Go 进行混合搜索来打地鼠

了解如何使用 Elasticsearch 和 Elasticsearch Go 客户端结合关键字和矢量搜索实现混合搜索。

本系列的前几部分演示了如何使用 Elasticsearch Go 客户端进行传统的关键字搜索矢量搜索。第三部分涉及混合搜索。我们将分享如何使用 Elasticsearch Elasticsearch Go 客户端 结合矢量搜索和关键字搜索的 示例 。

准备工作

与本系列的第一部分一样,本示例也需要以下前提条件:

  1. 安装 Go 1.21 或更高版本

  2. 使用Go 文档中推荐的结构和软件包管理创建自己的 Go 仓库

  3. 创建自己的 Elasticsearch 集群,其中包含一组 基于啮齿类动物的页面 ,包括维基百科中与我们友好的 Gopher 的 页面 :

维基百科 Gopher 网页

连接到 Elasticsearch

提醒一下,在我们的示例中,我们将使用 Go 客户端提供的Typed API。要为任何查询建立安全连接,都需要使用以下两种方法之一配置客户端:

  1. 云 ID 和 API 密钥(如果使用弹性云

  2. 群集 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 连接可用于搜索,这将在随后的章节中演示。

手动增强混合搜索

在组合任何一组搜索算法时,传统的方法是手动配置常量,以提高每种查询类型的效率。具体来说,为每个查询指定一个因子,然后将组合结果集与预期结果集进行比较,以确定查询的召回率。然后,我们对几组因子进行重复计算,选出最接近理想状态的因子。

例如,在两种查询类型中都指定Boost 字段,就可以将系数为0.8 的单一文本搜索查询与系数为0.2 的较低 knn 查询结合起来,如下例所示:

func HybridSearchWithBoost(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10
	var knnBoost float32 = 0.2
	var queryBoost float32 = 0.8

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			Boost:         &knnBoost,
			K:             &k,
			NumCandidates: &numCandidates,
			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 查询更大的系数,关键词查询的结果将获得更高的权重。

手动提升的挑战在于,它需要进行调整,以找出能产生理想结果集的因素,这对不是搜索专家的人来说尤其如此。这只是一个尝试随机值的例子,看看什么值更接近你想要的结果集。

混合搜索中的互惠排名融合& Go 客户端

互惠排名融合(RRF)在 Elasticsearch 8.9 混合搜索技术预览版中发布。其目的是降低与调整相关的学习曲线,减少为优化结果集而试验各种因素的时间。

使用 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 ,如文档中所述。

kwindows_size 也可以在 Go 客户端Rank 方法中的Rrf 配置中进行配置,如下例所示:

func HybridSearchWithRRF(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	var k = 10
	var numCandidates = 10

	// Minimum required window size for the default result size of 10
	var windowSize int64 = 10
	var rankConstant int64 = 42

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
			Field:         "text_embedding.predicted_value",
			K:             &k,
			NumCandidates: &numCandidates,
			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 部分

祝您打地鼠愉快

资源

  1. Elasticsearch 指南

  2. Elasticsearch Go 客户端

  3. 什么是矢量搜索?| 弹性

  4. 互惠等级融合

相关内容

LINQ to Elasticsearch ES|QL:编写 C# 代码,查询 Elasticsearch

Florian Bernd

利用 Elasticsearch 中的 NLP 和向量搜索增强聊天机器人功能

Priscilla Parodi

Elasticsearch 与 OpenSearch:向量搜索性能比较

Ugo Sangiorgi

人工智能剽窃利用 Elasticsearch 检测剽窃行为

Priscilla Parodi

高级 RAG 技术第 2 部分:查询和测试

Han Xiang Choong

准备好打造最先进的搜索体验了吗?

足够先进的搜索不是一个人的努力就能实现的。Elasticsearch 由数据科学家、ML 操作员、工程师以及更多和您一样对搜索充满热情的人提供支持。让我们联系起来,共同打造神奇的搜索体验,让您获得想要的结果。

亲自试用