<?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[Carly Richmond - 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[Carly Richmond - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/kr/search-labs/author/carly-richmond</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/author/carly-richmond</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/author/carly-richmond.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Mon, 21 Sep 2026 09:50:33 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Elasticsearch에서 두 인덱스를 조인하는 방법]]></title>
    <description><![CDATA[Elasticsearch에서 두 인덱스를 결합하기 위해 쿼리, Logstash elasticsearch 필터, enrich 프로세서 및 ES|QL이라는 용어를 사용하는 방법을 설명합니다.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch에서 두 인덱스를 조인하는 것은 기존 SQL 관계형 데이터베이스에서처럼 간단하지 않습니다. 그러나 Elasticsearch에서 제공하는 특정 기술과 기능을 사용하여 유사한 결과를 얻을 수 있습니다.</p><p>이전에는 많은 사람들이 서로 다른 인덱스를 결합하는 메커니즘으로 <a href="https://www.elastic.co/kr/docs/reference/elasticsearch/mapping-reference/nested"><code>nested</code></a><a href="https://www.elastic.co/kr/docs/reference/elasticsearch/mapping-reference/nested"> 필드 유형을</a> 사용했습니다. 그러나 비용이 많이 드는 쿼리와 Kibana의 불완전한 지원, 특히 Lens 시각화로 인해 제한적이었습니다.</p><p>이 문서에서는 다음 접근 방식에 초점을 맞춰 Elasticsearch에서 두 인덱스를 결합하는 프로세스를 자세히 설명합니다: </p><ol><li><p><code>terms</code> 쿼리 사용</p></li><li><p>수집 파이프라인에서 <code>enrich</code> 프로세서 사용</p></li><li><p>Logstash <code>elasticsearch</code> 필터 플러그인</p></li><li><p>ES|QL <code>ENRICH</code></p></li><li><p>ES|QL <code>LOOKUP JOIN</code></p></li></ol><h2>용어 쿼리 사용</h2><p><a href="https://www.elastic.co/kr/docs/reference/query-languages/query-dsl/query-dsl-terms-query">용어 쿼리는</a> Elasticsearch에서 두 인덱스를 결합하는 가장 효과적인 방법 중 하나입니다. 이 쿼리는 특정 필드에 하나 이상의 정확한 용어가 포함된 문서를 검색하는 데 사용됩니다. 여기서는 두 인덱스를 조인하는 데 사용하는 방법에 대해 설명합니다.</p><p>먼저 첫 번째 인덱스에서 필요한 데이터를 검색해야 합니다. 이는 간단한 GET 요청을 사용하여 <code>_source</code> 속성에서 값을 가져와서 수행할 수 있습니다.</p># Simple GET request
GET first_index/_search<p>첫 번째 인덱스의 데이터를 확보하면 이를 사용하여 두 번째 인덱스를 쿼리할 수 있습니다. <code>terms</code> 쿼리를 사용하여 일치시킬 필드와 값을 지정합니다.</p><p>다음은 그 예입니다:</p>GET second_index/_search
{
  "query": {
    "terms": {
      "field_in_second_index": ["value1_from_first_index", "value2_from_first_index"]
    }
  }
}<p>
이 예에서 <code>field_in_second_index</code> 은 두 번째 인덱스에서 첫 번째 인덱스의 값과 일치시키려는 필드입니다. <code>value1_from_first_index</code> 와 <code>value2_from_first_index</code> 는 두 번째 인덱스에서 일치시키려는 첫 번째 인덱스의 값입니다.</p><p><a href="https://www.elastic.co/kr/docs/reference/query-languages/query-dsl/query-dsl-terms-query#query-dsl-terms-lookup">용어 조회는 용어 조회라는</a> 기술을 사용하여 위의 두 단계를 한 번에 수행할 수 있는 기능도 지원합니다. Elasticsearch는 다른 인덱스에서 일치할 값을 투명하게 검색하는 작업을 처리합니다. 예를 들어 선수 목록이 포함된 팀 색인이 있는 경우입니다:</p>PUT teams/_doc/team1
{
  "players":   ["john", "bill", "michael"]
}
PUT teams/_doc/team2
{
  "players":   ["aaron", "joe", "donald"]
}<p>아래와 같이 팀1에서 플레이하는 모든 사람에 대한 사람 인덱스를 쿼리할 수 있습니다:</p>GET people/_search?pretty
{
  "query": {
    "terms": {
        "name" : {
            "index" : "teams",
            "id" : "team1",
            "path" : "players"
        }
    }
  }
}<p>위의 예에서, Elasticsearch는 팀 인덱스에서 id가 team1인 문서에서 선수 이름을 투명하게 검색합니다(즉, 다음과 같이). "john", "bill", "michael")을 검색하여 이름 필드에 이러한 값이 포함된 모든 문서를 사람 색인에서 찾습니다.</p><p>궁금하신 분들을 위해 동등한 SQL 쿼리는 다음과 같습니다:</p><h2>인리치 프로세서 사용</h2><p><a href="https://www.elastic.co/kr/docs/reference/enrich-processor/enrich-processor"><code>enrich</code></a><a href="https://www.elastic.co/kr/docs/reference/enrich-processor/enrich-processor"> 프로세서는</a> Elasticsearch에서 두 개의 인덱스를 조인하는 데 사용할 수 있는 또 다른 강력한 도구입니다. 이 프로세서는 미리 정의된 보강 색인에서 데이터를 추가하여 수신 문서의 데이터를 보강합니다.</p><p>인리치 프로세서를 사용하여 두 인덱스를 결합하는 방법은 다음과 같습니다:</p><p>1. 먼저 인라이크 정책을 만들어야 합니다. 이 정책은 보강에 사용할 인덱스, 일치시킬 필드, 들어오는 문서를 보강하는 데 사용할 필드를 정의합니다.</p><p>다음은 그 예입니다:</p>PUT _enrich/policy/my_enrich_policy
{
  "match": {
    "indices": "first_index",
    "match_field": "field_in_first_index",
    "enrich_fields": ["field_to_enrich"]
  }
}<p>2. 정책이 생성되면 정책을 실행하여 새로 생성한 정책에서 색인 강화 인덱스를 생성해야 합니다:</p>PUT _enrich/policy/my_enrich_policy/_execute<p>이렇게 하면 강화하는 동안 사용될 새로운 숨겨진 강화 인덱스가 생성됩니다. 소스 인덱스의 크기에 따라 이 작업에는 다소 시간이 걸릴 수 있습니다. 다음 단계로 진행하기 전에 인라이크 정책이 완전히 구축되었는지 확인하세요.</p><p>3. 강화 정책이 구축된 후에는 수집 파이프라인에서 강화 프로세서를 사용하여 들어오는 문서의 데이터를 강화할 수 있습니다:</p>PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "enrich": {
        "policy_name": "my_enrich_policy",
        "field": "field_in_second_index",
        "target_field": "enriched_field"
      }
    }
  ]
}<p>이 예에서 <code>field_in_second_index</code> 은 첫 번째 인덱스의 <code>match_field</code> 과 일치해야 하는 두 번째 인덱스의 필드입니다. <code>enriched_field</code> 는 첫 번째 인덱스의 <code>enrich_fields</code> 에서 보강된 데이터를 포함할 두 번째 인덱스의 새 필드입니다.</p><p>이 접근 방식의 한 가지 단점은 <code>first_index</code> 에서 데이터가 변경되면 보강 정책을 다시 실행해야 한다는 것입니다. 보강된 인덱스는 구축된 소스 인덱스에서 자동으로 업데이트되거나 동기화되지 않습니다. 하지만 <code>first_index</code> 이 비교적 안정적이라면 이 접근 방식이 잘 작동합니다.</p><h2>Logstash 엘라스틱서치 필터 플러그인</h2><p>Logstash를 사용하는 경우, 위에서 설명한 <code>enrich</code> 프로세서와 유사한 또 다른 옵션은 <code>elasticsearch</code> 필터 플러그인을 사용하여 지정된 쿼리를 기반으로 이벤트에 관련 필드를 추가하는 것입니다. Logstash 파이프라인의 구성은 <code>my-pipeline.conf</code> 과 같은 <code>.conf</code> 파일에 저장됩니다.</p><p>우리의 파이프라인이 <a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-inputs-elasticsearch"><code>elasticsearch</code></a><a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-inputs-elasticsearch"> 입력 플러그인을</a> 사용하여 Elasticsearch에서 로그를 가져오고, 선택 범위를 좁히기 위한 쿼리를 사용한다고 가정해 보겠습니다:</p>input {
  # Read all documents from Elasticsearch matching the given query
  elasticsearch {
    hosts =&gt; "localhost"
    query =&gt; '{ "query": { "match": { "statuscode": 200 } }, "sort": [ "_doc" ] }'
  }
}<p>주어진 인덱스의 정보로 이러한 메시지를 보강하려면 <code>filter</code> 섹션의 <a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-filters-elasticsearch"><code>elasticsearch</code></a><a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-filters-elasticsearch"> 필터 플러그인을</a> 사용하여 로그를 보강할 수 있습니다:</p>filter {
   elasticsearch {
      hosts =&gt; ["localhost"]
      index =&gt; "index_name"
      query =&gt; "type:start AND operation:%{[opid]}"
      fields =&gt; { "@timestamp" =&gt; "started" }
   }
}<p>위의 코드는 인덱스 <code>index_name</code> 에서 <code>type</code> 이 시작이고 작업 필드가 지정된 <code>opid</code> 과 일치하는 문서를 찾은 다음 <code>@timestamp</code> 필드의 값을 <code>started</code> 라는 새 필드에 복사합니다 .</p><p>그런 다음 보강된 문서는 적절한 출력 소스(이 경우 <a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-outputs-elasticsearch"><code>elasticsearch</code></a><a href="https://www.elastic.co/kr/docs/reference/logstash/plugins/plugins-outputs-elasticsearch"> 출력 플러그인을</a> 사용하여 Elasticsearch로)로 전송됩니다:</p>output {
    elasticsearch {
        hosts =&gt; "localhost"
        data_stream =&gt; "true"
    }
}<p>이미 Logstash를 사용하고 있는 경우, 이 옵션은 보강 로직을 한 곳에 통합하고 새로운 이벤트가 들어올 때 처리하는 데 유용할 수 있습니다. 그러나 그렇지 않은 경우 솔루션이 복잡해지고 실행 및 유지 관리해야 하는 또 다른 구성 요소가 추가됩니다.</p><h2>ES|QL 리치</h2><p>버전 8.14에서 정식 버전으로 출시된 <a href="https://www.elastic.co/kr/docs/explore-analyze/query-filter/languages/esql">ES|QL은</a> Elasticsearch에서 지원하는 파이핑 쿼리 언어로, 데이터를 필터링, 변환, 분석할 수 있게 해줍니다. ENRICH 처리 명령을 사용하면 강화 정책을 사용하여 기존 인덱스의 데이터를 추가할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbeb8992bde773461/6a17f6b663baff00c9741dd4/03aadddc08afffff3f6526c9c052999c97fa09dd-1600x989.png" alt="ESQL 인리치" /><p>원래의 인리치 프로세서 예제에서 동일한 정책 <code>my_enrich_policy</code> 을 사용하면 ES|QL 예제는 다음과 같이 됩니다:</p><p>이 예제에서는 각각 <code>field_in_first_index</code> 및 <code>field_to_enrich</code> 인 일치 및 보강 필드를 재정의할 수도 있습니다:</p><p>강화 정책을 먼저 지정해야 한다는 분명한 한계가 있지만, ES|QL은 필요에 따라 필드를 조정할 수 있는 유연성을 제공합니다.</p><h2>ES|QL 조회 조인</h2><p>Elasticsearch 8.18에서는 <code>LOOKUP JOIN</code> 명령이라는 새로운 인덱스 조인 방법이 도입되었습니다. 이 명령은 조인의 오른쪽에 있는 새 <a href="https://www.elastic.co/kr/docs/reference/elasticsearch/index-settings/index-modules#index-mode-setting">조회 인덱스 모드를</a> 사용하여 SQL 스타일의 LEFT OUTER JOIN으로 작동합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt783ffb3f9802f92d/6a17f6b8e9ea870608a9c788/1d73495979c4d6bb675c4c966ea86d9a72dc1c48-510x605.png" alt="ES|QL 조회 조인" /><p>이전 예제를 다시 살펴보면 새 쿼리는 다음과 같으며, <code>match_field</code> 는 <code>first_index</code> 과 <code>second_index</code> 모두에 존재해야 합니다:</p><p>다른 접근 방식에 비해 조회 조인의 장점은 <code>enrich</code> 정책이 필요하지 않으므로 정책 설정과 관련된 추가 처리가 필요하지 않다는 점입니다. 이 글에서 설명하는 다른 접근 방식과 달리 자주 변경되는 보강 데이터로 작업할 때 유용합니다.</p><h2>결론</h2><p>결론적으로, Elasticsearch는 전통적인 조인 작업을 지원하지는 않지만, 유사한 결과를 얻기 위해 사용할 수 있는 다양한 기능을 제공합니다. 특히 다음을 사용하여 조인 작업을 수행하는 방법에 대해 설명했습니다:</p><ol><li><p><code>terms</code> 쿼리</p></li><li><p>수집 파이프라인의 <code>enrich</code> 프로세서</p></li><li><p>Logstash <code>elasticsearch</code> 필터 플러그인</p></li><li><p>ES|QL <code>ENRICH</code></p></li><li><p>ES|QL <code>LOOKUP JOIN</code></p></li></ol><p>이러한 방법에는 한계가 있으므로 특정 요구 사항과 데이터의 특성에 따라 신중하게 사용해야 한다는 점에 유의하세요.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-join-two-indexes</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-join-two-indexes</guid>
    <category><![CDATA[기본]]></category>
    <dc:creator><![CDATA[Carly Richmond]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74822b3b7cb2a41a/6a17f6b97f6f156288c09cc7/0d4736d10fa3e12e6233cd59993299c7bd48911b-680x450.png" length="0" type="image/png"/>
    <pubDate>Wed, 07 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch와 Go로 고퍼 헌팅을 위한 하이브리드 검색 사용]]></title>
    <description><![CDATA[Elasticsearch와 Elasticsearch Go 클라이언트를 사용해 키워드 검색과 벡터 검색을 결합하여 하이브리드 검색을 수행하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>이 시리즈의 이전 파트에서는 <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> Elasticsearch Go 클라이언트를 사용하는 방법을 보여드렸습니다. 세 번째 파트에서는 하이브리드 검색을 다룹니다. <a href="https://www.elastic.co/elasticsearch/">Elasticsearch와 Elasticsearch</a> <a href="https://www.elastic.co/guide/en/elasticsearch/client/go-api/current/index.html">Go 클라이언트를</a> 사용해 벡터 검색과 키워드 검색을 모두 결합하는 <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch">방법에 대한 예를</a> 공유해드리겠습니다.</p><h2>필수 구성 요소</h2><p>이 시리즈의 1부와 마찬가지로 이 예제에도 다음과 같은 전제 조건이 필요합니다:</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>위키피디아에서 친숙한 Gopher를 포함해 설치류 <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources">기반</a> <a href="https://en.wikipedia.org/wiki/Gopher">페이지 세트로</a> 채워진 자체 Elasticsearch 클러스터를 생성합니다:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="위키피디아 고퍼 페이지" /><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>Elastic Cloud를 사용하는 경우 클라우드 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>0.8</code> 계수가 부스트된 단일 텍스트 검색 쿼리와 <code>0.2</code> 계수가 낮은 knn 쿼리를 결합하려면 두 쿼리 유형에 모두 <code>Boost</code> 필드를 지정하면 됩니다:</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> 입니다. 대규모 데이터 집합을 검색할 때 반환된 문서의 관련성과 쿼리 성능 간의 균형을 맞추기 위해 고려되는 각 쿼리에 대한 결과 집합의 크기는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html#rrf-api">문서에</a> 설명된 대로 기본값이 <code>100</code> 인 <code>window_size</code> 의 값으로 제한됩니다.</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 클라이언트를 사용하여 Elasticsearch에서 벡터 검색과 키워드 검색을 결합하는</a> 방법에 대해 설명했습니다.</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">벡터 검색이란 무엇인가요? | Elastic</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://github.com/carlyrichmond/gopher-hunting-elasticsearch">대한 개요와</a> <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> 사용한 각 접근 방식의 예를 공유하겠습니다. 이 예제에서는 Elasticsearch와 Go에서 벡터 검색을 사용해 고퍼를 찾고 고퍼가 무엇을 먹는지 알아내는 방법을 보여드립니다.</p><h2>필수 구성 요소</h2><p>이 예제를 따라 하려면 다음 전제 조건이 충족되는지 확인하세요:</p><ol><li><p>Go 버전 1.21 이상 설치</p></li><li><p>를 사용하여 나만의 Go 리포지토리를 생성합니다.</p></li><li><p>위키피디아에서 친숙한 <a href="https://en.wikipedia.org/wiki/Gopher">Gopher를</a> 포함한 설치류 기반 페이지 세트로 채워진 자체 Elasticsearch 클러스터를 생성합니다:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="위키피디아 고퍼 페이지" /><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>Elastic Cloud를 사용하는 경우 클라우드 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 내에서 임베딩을 생성할 수 있습니다. 모델을 설정하는 데는 6단계가 있습니다:</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 Machine Learning 클라이언트를</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="Elastic Test 학습된 모델 예시" /><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: 허깅 얼굴 추론 API</h3><p>또 다른 옵션은 Elasticsearch 외부에서 동일한 임베딩을 생성하여 문서의 일부로 수집하는 것입니다. 이 옵션은 Elasticsearch 머신 러닝 노드를 사용하지 않으므로 무료 티어에서 수행할 수 있습니다.</p><p>허깅 페이스는 무료로 사용할 수 있는 속도 제한이 없는 <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>QueryVectorBuilder</code> 옵션을 사용하는 대신 <code>QueryVector</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 클라이언트를 사용하여 Elasticsearch에서 벡터 검색을 수행하는</a> 방법에 대해 설명했습니다. 이 시리즈의 모든 코드는 <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> <a href="https://www.elastic.co/search-labs/blog/perform-text-queries-with-the-elasticsearch-go-client">1부에서</a> 다룬 벡터 검색과 키워드 검색 기능을 Go에서 결합하는 방법에 대한 개요를 살펴봅니다.</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">벡터 검색이란 무엇인가요? | Elastic</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>위키피디아에서 친숙한 Gopher를 포함한 <a href="https://github.com/carlyrichmond/gopher-hunting-elasticsearch#sources">설치류 기반</a> <a href="https://en.wikipedia.org/wiki/Gopher">페이지 세트로</a> 채워진 자체 Elasticsearch 클러스터를 생성합니다:</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6a436c42996e5172/6a1704cf0c48573d1c01a957/34fa81a9b4c292634c719b8303a9b6b7506d7920-1440x662.png" alt="위키피디아 고퍼 페이지" /><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>Elastic Cloud를 사용하는 경우 클라우드 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>원치 않는 토큰을 제거하거나 토큰을 언어 루트로 다시 변환하는 <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"> 토큰 필터</a> 또는 <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> 같은 0개 이상의 <code>filter</code> 옵션을 사용하여 토큰라이저의 출력 스트림에서 토큰을 변환합니다.</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>_Source</code> 속성과 함께 문서 자체를 JSON 형식으로 포함하는 <code>res.Hits.Hits</code> 형식으로 반환됩니다. 이 소스를 Go 친화적인 구조체로 변환하려면 아래 예시와 같이 Go <a href="https://pkg.go.dev/encoding/json">encoding/json 패키지를</a> 사용하여 JSON 응답을 <a href="https://pkg.go.dev/encoding/json#Unmarshal">마샬링 해제해야 합니다:</a></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의 Wikipedia 페이지가 반환됩니다:</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 클라이언트를 사용하여 Elasticsearch에서 기존 텍스트 쿼리를 수행하는</a> 방법에 대해 설명했습니다. 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(분석기)에서 분석 이해하기 | #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>