<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Go - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Go - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/cn/search-labs/blog/category/go-programming</link>
    </image>
    <link>https://www.elastic.co/cn/search-labs/blog/category/go-programming</link>
    <atom:link href="https://www.elastic.co/cn/search-labs/rss/category/go-programming.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[cn]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 03:05:00 GMT</lastBuildDate>
  <item>
    <title><![CDATA[使用 Elasticsearch 和 Go 进行混合搜索来打地鼠]]></title>
    <description><![CDATA[了解如何使用 Elasticsearch 和 Elasticsearch Go 客户端结合关键字和矢量搜索实现混合搜索。]]></description>
    <content:encoded><![CDATA[<p>本系列的前几部分演示了如何使用 Elasticsearch Go 客户端进行<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">传统的关键字搜索</a>和<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">矢量搜索</a>。第三部分涉及混合搜索。我们将分享如何使用<a href="https://www.elastic.co/elasticsearch/"> Elasticsearch</a> 和<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch"> </a><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go 客户端</a> 结合矢量搜索和关键字搜索的 示例 。</p><h2>准备工作</h2><p>与本系列的第一部分一样，本示例也需要以下前提条件：</p><ol><li><p>安装 Go 1.21 或更高版本</p></li><li><p>使用<a href="https://go.dev/doc/code">Go 文档</a>中推荐的结构和软件包管理创建自己的 Go 仓库</p></li><li><p>创建自己的 Elasticsearch 集群，其中包含一组<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources"> 基于啮齿类动物的页面</a> ，包括维基百科中与我们友好的<a href="https://en.wikipedia.org/wiki/Gopher"> Gopher</a> 的 页面 ：</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="维基百科 Gopher 网页" /><h2>连接到 Elasticsearch</h2><p>提醒一下，在我们的示例中，我们将使用 Go 客户端提供的<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">Typed API</a>。要为任何查询建立安全连接，都需要使用以下两种方法之一配置客户端：</p><ol><li><p>云 ID 和 API 密钥（如果使用弹性云</p></li><li><p>群集 URL、用户名、密码和证书</p></li></ol><p>连接到位于 Elastic Cloud 上的集群的过程如下：</p>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:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>然后，<code>client</code> 连接可用于搜索，这将在随后的章节中演示。</p><h2>手动增强混合搜索</h2><p>在组合任何一组搜索算法时，传统的方法是手动配置常量，以提高每种查询类型的效率。具体来说，为每个查询指定一个因子，然后将组合结果集与预期结果集进行比较，以确定查询的召回率。然后，我们对几组因子进行重复计算，选出最接近理想状态的因子。</p><p>例如，在两种查询类型中都指定<code>Boost</code> 字段，就可以将系数为<code>0.8</code> 的单一文本搜索查询与系数为<code>0.2</code> 的较低 knn 查询结合起来，如下例所示：</p>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:         &amp;knnBoost,
			K:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {
					Query: term,
					Boost: &amp;queryBoost,
				},
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p><code>Boost</code> 选项中为每个查询指定的因子会添加到文档得分中。通过将匹配查询的得分提高一个比 knn 查询更大的系数，关键词查询的结果将获得更高的权重。</p><p>手动提升的挑战在于，它需要进行调整，以找出能产生理想结果集的因素，这对不是搜索专家的人来说尤其如此。这只是一个尝试随机值的例子，看看什么值更接近你想要的结果集。</p><h2>混合搜索中的互惠排名融合&amp; Go 客户端</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">互惠排名融合</a>（RRF）在 Elasticsearch 8.9 混合搜索技术预览版中发布。其目的是降低与调整相关的学习曲线，减少为优化结果集而试验各种因素的时间。</p><p>使用 RRF 时，文档得分通过以下算法混合得分重新计算：</p>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
<p>使用 RRF 的好处是，我们可以利用 Elasticsearch 中合理的默认值。排序常数<code>k</code> 默认为<code>60</code> 。在对大型数据集进行搜索时，为了在返回文档的相关性和查询性能之间进行权衡，每个考虑过的查询结果集的大小都限制在<code>window_size</code> 的值范围内，默认值为<code>100</code> ，如<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html#rrf-api">文档</a>中所述。</p><p><code>k</code> 和<code>windows_size</code> 也可以在 Go 客户端<code>Rank</code> 方法中的<code>Rrf</code> 配置中进行配置，如下例所示：</p>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:             &amp;k,
			NumCandidates: &amp;numCandidates,
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {Query: term},
			},
		}).
		Rank(&amp;types.RankContainer{
			Rrf: &amp;types.RrfRank{
				WindowSize:   &amp;windowSize,
				RankConstant: &amp;rankConstant,
			},
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<h2>结论</h2><p>在此，我们讨论了如何使用 Elasticsearch<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Go 客户端 在</a> Elasticsearch 中将向量搜索和关键字搜索结合起来。</p><p>查看<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">GitHub 仓库</a>中本系列的所有代码。如果您还没有阅读本系列的所有代码，请查看<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">第 1 部分</a>和<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">第 2 部分</a>。</p><p><em>祝您打地鼠愉快</em></p><h2>资源</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Elasticsearch 指南</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go 客户端</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">什么是矢量搜索？| 弹性</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">互惠等级融合</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[向量数据库]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 02 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[使用 Elasticsearch Go 客户端在 Elasticsearch 中执行矢量搜索]]></title>
    <description><![CDATA[通过实际示例了解如何使用 Elasticsearch Go 客户端在 Elasticsearch 中执行矢量搜索。]]></description>
    <content:encoded><![CDATA[<p>使用包括 Go 语言在内的任何编程语言构建软件，都需要终身学习。在大学和工作生涯中，卡莉涉猎过多种编程语言和技术，包括最新、最先进的矢量搜索技术。但这还不够！所以最近 Carly 也开始玩围棋了。</p><p>就像动物、编程语言和您友好的作者一样，搜索也经历了不同实践的演变，您可能很难在自己的搜索用例中做出选择。在本博客中，我们将分享矢量搜索的概述，以及使用<a href="https://www.elastic.co/elasticsearch/"> Elasticsearch</a> 和<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Elasticsearch Go 客户端</a> 的每种方法的<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch"> 示例</a> 。这些示例将向您展示如何使用 Elasticsearch 和 Go 中的矢量搜索查找地鼠并确定它们吃什么。</p><h2>准备工作</h2><p>要学习本示例，请确保满足以下前提条件：</p><ol><li><p>安装 Go 1.21 或更高版本</p></li><li><p>使用</p></li><li><p>创建您自己的 Elasticsearch 集群，其中包含一组基于啮齿动物的页面，包括维基百科中对我们友好的<a href="https://en.wikipedia.org/wiki/Gopher">Gopher</a> 的介绍：</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="维基百科 Gopher 网页" /><h2>连接到 Elasticsearch</h2><p>在我们的示例中，我们将使用 Go 客户端提供的<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">类型 API</a>。要为任何查询建立安全连接，都需要使用以下两种方法之一配置客户端：</p><ol><li><p>云 ID 和 API 密钥（如果使用弹性云）。</p></li><li><p>集群 URL、用户名、密码和证书。</p></li></ol><p>连接到位于 Elastic Cloud 上的集群的过程如下：</p>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:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>如后续章节所示，<code>client</code> 连接可用于矢量搜索。</p><h2>向量搜索</h2><p>矢量搜索试图通过将搜索问题转换为使用矢量的数学比较来解决这一问题。文件嵌入过程还有一个额外的阶段，即使用模型将文件转换成密集的向量表示，或者简单地转换成数字流。这种方法的优势在于，通过将非文本文档（如图像和音频）翻译成矢量并与查询一起进行搜索。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4b3b9b2ad1e8f46/6a1704d06234e0800fdb1927/b113358093e367358d684f7aaf0a6684ebb2d0dd-1440x653.png" alt="矢量搜索图" /><p>简单地说，矢量搜索是一组矢量距离计算。在下图中，我们将查询<code>Go Gopher</code>的向量表示与向量空间中的文档进行比较，并返回最接近的结果（用常数<code>k</code> 表示）：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c736ecfc49bb038/6a1704d2cf4f25bcb6b2d075/54a17a4b41029644e7c66f33d428e8d01a6f4ce3-1184x743.png" alt="Gopher 矢量空间示例" /><p>根据为文档生成嵌入式的方法，有两种不同的方法可以找出地鼠吃什么。</p><h3>方法 1：自带模型</h3><p>有了白金许可，就可以通过上传模型和使用推理 API 在 Elasticsearch 中生成嵌入。建立模型有六个步骤：</p><ol><li><p>从模型库中选择一个 PyTorch 模型上传。在本例中，我们使用 Hugging Face 的<a href="https://huggingface.co/sentence-transformers/msmarco-MiniLM-L-12-v3">句子转换器/msmarco-MiniLM-L-12-v3</a>来生成嵌入。</p></li><li><p>使用 Elasticsearch 集群的凭据和任务类型<code>text_embeddings</code> ，使用<a href="https://www.elastic.co/guide/en/elasticsearch/client/eland/current/overview.html">Python 版 Eland 机器学习客户端</a>将模型加载到 Elastic 中。如果没有安装 Eland，可以<a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-import-model.html#ml-nlp-import-docker">使用 Docker 运行导入步骤</a>，如下图所示：</p></li></ol>docker run -it --rm --network host \
    docker.elastic.co/eland/eland \
    eland_import_hub_model \
      --cloud-id $ELASTIC_CLOUD_ID \
      --es-api-key $ELASTIC_API_KEY \
      --hub-model-id sentence-transformers/msmarco-MiniLM-L-12-v3 \
      --task-type text_embedding
<ol><li><p>上传后，请使用样本文档快速测试模型<code>sentence-transformers__msmarco-minilm-l-12-v3</code> ，以确保生成的嵌入结果符合预期：</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a399e1f50ecfdaf/6a1704d4ab7f08935edb9d78/fbbdde621361a487eb07304bed029c228d0f7aa6-1440x789.png" alt="弹性测试训练模型示例" /><ol><li><p>创建包含推理处理器的摄取管道。这将允许使用上传的模型生成矢量表示：</p></li></ol>PUT _ingest/pipeline/search-rodents-vector-embedding-pipeline
{
  "processors": [
    {
      "inference": {
        "model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
        "target_field": "text_embedding",
        "field_map": {
          "body_content": "text_field"
        }
      }
    }
  ]
}
<ol><li><p>创建一个新索引，其中包含类型为<code>dense_vector</code> 的字段<code>text_embedding.predicted_value</code> ，用于存储为每个文档生成的向量嵌入：</p></li></ol>PUT vector-search-rodents
{
  "mappings": {
    "properties": {
      "text_embedding.predicted_value": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      },
      "text": {
        "type": "text"
      }
    }
  }
}
<ol><li><p>使用新创建的摄取管道重新索引文档，生成文本嵌入作为每个文档的附加字段<code>text_embedding.predicted_value</code> ：</p></li></ol>POST _reindex
{
  "source": {
    "index": "search-rodents"
  },
  "dest": {
    "index": "vector-search-rodents",
    "pipeline": "search-rodents-vector-embedding-pipeline"
  }
}
<p>现在，我们可以使用新的索引<code>vector-search-rodents</code> ，在同一个搜索 API 中使用<code>Knn</code> 选项，如下例所示：</p>func VectorSearch(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Generate query vector using the same model used in the inference processor
			QueryVectorBuilder: &amp;types.QueryVectorBuilder{
				TextEmbedding: &amp;types.TextEmbedding{
					ModelId:   "sentence-transformers__msmarco-minilm-l-12-v3",
					ModelText: term,
				},
			}}).Do(context.Background())

	if err != nil {
		return nil, fmt.Errorf("error in rodents vector search: %w", err)
	}

	return getRodents(res.Hits.Hits)
}
<p>通过解分叉转换 JSON 结果对象的方法与关键字搜索示例完全相同。常量<code>K</code> 和<code>NumCandidates</code> 允许我们配置返回的邻接文件数和每个分区要考虑的候选文件数。需要注意的是，增加候选结果的数量会提高结果的准确性，但由于要进行更多的比较，查询的运行时间会更长。</p><p>当使用查询<code>What do Gophers eat?</code> 执行代码时，返回的结果与下图类似，突出显示 Gopher 文章包含所需的信息，与之前的关键字搜索不同：</p>[
  {ID:64f74ecd4acb3df024d91112 Title:Gopher - Wikipedia Url:https://en.wikipedia.org/wiki/Gopher} 
  {ID:64f74ed34acb3d71aed91fcd Title:Squirrel - Wikipedia Url:https://en.wikipedia.org/wiki/Squirrel} 
  //Other results omitted
]
<h3>方法 2：拥抱脸推理应用程序接口</h3><p>另一种方法是在 Elasticsearch 之外生成相同的嵌入，并将其作为文档的一部分进行摄取。由于该选项不使用 Elasticsearch 机器学习节点，因此可以在免费层上完成。</p><p>Hugging Face 公开了一个免费使用、有费率限制的<a href="https://huggingface.co/docs/api-inference/index">推理 API</a>，只要有账户和 API 令牌，就可以手动生成相同的嵌入式数据，用于实验和原型开发，帮助你开始工作。不建议用于生产。也可以使用类似的方法在本地调用自己的模型来生成嵌入，或使用付费 API。</p><p>在下面的函数<code>GetTextEmbeddingForQuery</code> 中，我们根据查询字符串使用推理 API 生成从<code>POST</code> 请求端点返回的向量：</p>// HuggingFace text embedding helper
func GetTextEmbeddingForQuery(term string) []float32 {
    // HTTP endpoint
    model := "sentence-transformers/msmarco-minilm-l-12-v3"
    posturl := fmt.Sprintf("https://api-inference.huggingface.co/pipeline/feature-extraction/%s", model)

    // JSON body
    body := []byte(fmt.Sprintf(`{
        "inputs": "%s",
        "options": {"wait_for_model":True}
    }`, term))

    // Create a HTTP post request
    r, err := http.NewRequest("POST", posturl, bytes.NewBuffer(body))

    if err != nil {
        log.Fatal(err)
        return nil
    }

    token := os.Getenv("HUGGING_FACE_TOKEN")
    r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

    client := &amp;http.Client{}
    res, err := client.Do(r)
    if err != nil {
        panic(err)
    }

    defer res.Body.Close()

    var post []float32
    derr := json.NewDecoder(res.Body).Decode(&amp;post)

    if derr != nil {
        log.Fatal(derr)
        return nil
    }

    return post
}
<p>生成的<code>[]float32</code> 类型的向量将作为<code>QueryVector</code> 传递，而不是使用<code>QueryVectorBuilder</code> 选项来利用之前上传到 Elastic 的模型。</p>func VectorSearchWithGeneratedQueryVector(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	vector, err := GetTextEmbeddingForQuery(term)
	if err != nil {
		return nil, err
	}

	if vector == nil {
		return nil, fmt.Errorf("unable to generate vector: %w", err)
	}

  var k = 10
	var numCandidates = 10

	res, err := client.Search().
		Index("vector-search-rodents").
		Knn(types.KnnSearch{
      # Field in document containing vector
			Field:         "text_embedding.predicted_value",
      # Number of neighbors to return
			K:             &amp;k,
      # Number of candidates to evaluate in comparison
			NumCandidates: &amp;numCandidates,
      # Query vector returned from Hugging Face inference API
			QueryVector:   vector,
		}).
		Do(context.Background())

	if err != nil {
		return nil, err
	}

	return getRodents(res.Hits.Hits)
}
<p>请注意，无论使用哪两个选项，<code>K</code> 和<code>NumCandidates</code> 选项都是一样的，而且生成的结果也是一样的，因为我们使用的是同一个模型来生成嵌入结果</p><h2>结论</h2><p>在此，我们讨论了如何使用 Elasticsearch<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Go 客户端 在</a> Elasticsearch 中执行矢量搜索。查看<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">GitHub 仓库</a>中本系列的所有代码。请继续阅读<a href="https://www.elastic.co/search-labs/blog/hybrid-search-with-the-elasticsearch-go-client">第 3 部分</a>，了解如何将矢量搜索与 Go 语言<a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">第一部分</a>中的关键字搜索功能相结合。</p><p>在此之前，祝您打地鼠愉快！</p><h2>资源</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Elasticsearch 指南</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go 客户端</a></p></li><li><p><a href="https://www.elastic.co/what-is/vector-search">什么是矢量搜索？| 弹性</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[向量数据库]]></category>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 01 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[使用 Elasticsearch Go 客户端执行文本查询]]></title>
    <description><![CDATA[通过实际示例了解如何使用 Elasticsearch Go 客户端在 Elasticsearch 中执行传统文本查询。]]></description>
    <content:encoded><![CDATA[<p>使用包括 Go 语言在内的任何编程语言构建软件，都需要终身学习。在整个大学和工作生涯中，Carly 需要适应多语言环境，并涉猎多种编程语言，包括 Python、C、JavaScript、TypeScript 和 Java。但这还不够！所以最近她也开始玩围棋了！</p><p>就像动物、编程语言和您的一位友好作家一样，搜索也经历了不同实践的演变，您可能很难在自己的搜索用例中做出选择。在本博客中，我们将概述传统的关键字搜索，并以使用<a href="https://www.elastic.co/elasticsearch/"> Elasticsearch</a> 和<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Elasticsearch</a> Go<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch"> 客户端 为例</a> 进行说明。</p><h2>准备工作</h2><p>要学习本示例，请确保满足以下前提条件：</p><ol><li><p>安装 Go 1.21 或更高版本</p></li><li><p>使用<a href="https://go.dev/doc/code">Go 文档</a>中推荐的结构和软件包管理创建自己的 Go 仓库</p></li><li><p>创建您自己的 Elasticsearch 集群，其中包含一组<a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources"> 基于啮齿动物的页面</a> ，包括维基百科中与我们友好的<a href="https://en.wikipedia.org/wiki/Gopher"> Gopher</a> 的 页面 ：</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="维基百科 Gopher 网页" /><h2>连接到 Elasticsearch</h2><p>在我们的示例中，我们将使用 Go 客户端提供的<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/typedapi.html">类型 API</a>。要为任何查询建立安全连接，都需要使用以下任一种方法配置客户端：</p><ol><li><p>云 ID 和 API 密钥（如果使用弹性云）。</p></li><li><p>集群 URL、用户名、密码和证书。</p></li></ol><p>连接到位于 Elastic Cloud 上的集群的过程如下：</p>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:  &amp;elastictransport.ColorLogger{os.Stdout, true, true},
	})

	if err != nil {
		return nil, fmt.Errorf("unable to connect: %w", err)
	}

	return es, nil
}
<p>如稍后所示，<code>client</code> 连接可用于搜索。</p><h2>关键词搜索</h2><p>自 1990 年第一个有文字记载的互联网搜索引擎<a href="https://en.wikipedia.org/wiki/Archie_(search_engine)">Archie</a> 诞生以来，关键词搜索就是我们所熟悉的基础搜索类型。</p><p>关键词搜索的核心部分是将文件翻译成倒排索引。与教科书后面的索引完全相同，倒排索引包含标记列表与其在每个文档中的位置之间的映射。下图显示了生成索引的关键阶段：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt366681a12027f14b/6a17e7aa1d1b831c2093e452/9cd7b2a60d9aa54facb37738b821bba6ca925830-1440x581.png" alt="反向索引生成" /><p>如上所述，Elasticsearch 中令牌的生成包括三个关键阶段：</p><ol><li><p>通过 0 个或多个<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-charfilters.html"><code>char_filters</code></a> 删除不必要的字符。在我们的示例中，我们通过<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-htmlstrip-charfilter.html"><code>html_strip</code></a> 过滤器去除<code>body_content</code> 字段中的 HTML 元素。</p></li><li><p>使用<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html"><code>standard</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-tokenizers.html"> 令牌化器</a> 从内容中分割 令牌 ，令牌 化器 会根据间距和关键标点符号进行分割。</p></li><li><p>使用零个或多个<code>filter</code> 选项，如<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lowercase-tokenfilter.html"><code>lowercase</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lowercase-tokenfilter.html"> token filter</a> 或 stemmer<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html"><code>snowball</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html"> </a>等词干转换器，从令牌转换器的输出流中移除不需要的令牌或转换令牌，以便将令牌转换回其语言词根。</p></li></ol><h2>用 Go 在 Elasticsearch 中搜索</h2><p>使用 Go 客户端进行查询时，我们要指定要搜索的索引，并传递查询和其他选项，就像下面的示例一样：</p>func KeywordSearch(client *elasticsearch.TypedClient, term string) ([]Rodent, error) {
	res, err := client.Search().
		Index("search-rodents").
		Query(&amp;types.Query{
			Match: map[string]types.MatchQuery{
				"title": {Query: term},
			},
		}).
		From(0).
		Size(10).
		Do(context.Background())

	if err != nil {
		return nil, fmt.Errorf("could not search for rodents: %w", err)
	}

	return getRodents(res.Hits.Hits)
}
<p>在上面的示例中，我们执行了一个标准的<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html"><code>match</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html"> 查询</a>，以查找索引中包含传入函数的指定字符串的任何文档。请注意，我们通过<code>Do(context.Background())</code> 向搜索执行传递了一个<a href="https://pkg.go.dev/context#Background">新的空上下文</a>。此外，Elasticsearch 返回的任何错误都会输出到<code>err</code> 属性，以便记录日志和处理错误。</p><p>结果以<code>res.Hits.Hits</code> 的形式返回，其中<code>_Source</code> 属性包含 JSON 格式的文档本身。要将该源代码转换为 Go 友好的结构，我们需要使用 Go<a href="https://pkg.go.dev/encoding/json"> encoding/json</a><a href="https://pkg.go.dev/encoding/json#Unmarshal"> </a>包对 JSON 响应进行 解码 ，如下例所示：</p>func getRodents(hits []types.Hit) ([]Rodent, error) {
	var rodents []Rodent

	for _, hit := range hits {
		var currentRodent Rodent
		err := json.Unmarshal(hit.Source_, &amp;currentRodent)

		if err != nil {
			return nil, fmt.Errorf("an error occurred while unmarshaling rodent %s: %w", hit.Id_, err)
		}

		currentRodent.ID = hit.Id_
		rodents = append(rodents, currentRodent)
	}

	return rodents, nil
}
<p>搜索并取消查询<code>gopher</code> 将如期返回维基百科中有关 Gopher 的页面：</p>[
  {ID:64f74ecd4acb3df024d91112 Title:Gopher - Wikipedia Url:https://en.wikipedia.org/wiki/Gopher}
]
<p>然而，如果我们询问<code>What do Gophers eat?</code> ，就不会得到我们想要的结果：</p>[]
<p>通过简单的关键字搜索，就能以我们熟悉的方式将结果返回到 Go 应用程序中。它还能很好地进行精确的术语匹配，这与查找特定公司或术语等情况相关。</p><p>然而，正如我们在上文所看到的，由于词汇不匹配问题，它在识别上下文和语义方面存在困难。此外，对图像和音频等非文本文件格式的支持也具有挑战性。</p><h2>结论</h2><p>在这里，我们讨论了如何使用 Elasticsearch<a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html"> Go 客户端 在</a> Elasticsearch 中执行传统文本查询。鉴于 Go 被广泛用于基础设施脚本和构建网络服务器，因此了解如何用 Go 进行搜索非常有用。</p><p>查看<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">GitHub 仓库</a>中本系列的所有代码。请继续阅读<a href="https://www.elastic.co/search-labs/blog/perform-vector-search-with-the-elasticsearch-go-client">第 2 部分</a>，了解矢量搜索的概述以及如何在 Go 中执行矢量搜索。在此之前，祝您打地鼠愉快！</p><h2>资源</h2><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html">Elasticsearch 指南</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Elasticsearch Go 客户端</a></p></li><li><p><a href="https://codingexplained.com/coding/elasticsearch/understanding-analysis-in-elasticsearch-analyzers">了解 Elasticsearch 中的分析（分析器），作者 Bo Andersen | #CodingExplained</a></p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client</guid>
    <category><![CDATA[Go]]></category>
    <dc:creator><![CDATA[Carly Richmond,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcae959696d55a9b/6a1704d5ab7f085a56db9d7c/491ef9efbb30b253e1d9e3b7f816a9a23c8f5264-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 31 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>