<?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[Andre Luiz - 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[Andre Luiz - 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/andre-luiz</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/author/andre-luiz</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/author/andre-luiz.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Tue, 22 Sep 2026 19:38:54 GMT</lastBuildDate>
  <item>
    <title><![CDATA[임베딩을 Elasticsearch 필드 유형에 매핑하기: semantic_text, dense_vector, sparse_vector]]></title>
    <description><![CDATA[semantic_text, dense_vector 또는 sparse_vector를 사용하는 방법과 시기, 그리고 임베딩 생성과의 관계에 대해 논의합니다.]]></description>
    <content:encoded><![CDATA[<p>정보 검색의 관련성과 정확성을 높이기 위한 임베딩의 사용은 지난 몇 년 동안 크게 증가했습니다. Elasticsearch와 같은 도구는 밀집 벡터, 희소 벡터, 시맨틱 텍스트와 같은 특수한 필드 유형을 통해 이러한 유형의 데이터를 지원하도록 발전해 왔습니다. 그러나 좋은 결과를 얻으려면 임베딩을 사용 가능한 Elasticsearch 필드 유형에 올바르게 매핑하는 방법을 이해하는 것이 필수적입니다: <code>semantic_text</code>, <code>dense_vector</code>, 및 <code>sparse_vector</code> 을 참조하세요.</p><p>이 문서에서는 이러한 필드 유형, 각 필드 유형이 언제 사용되는지, 색인 및 쿼리 중 임베딩 생성 및 사용 전략과 어떻게 연관되는지에 대해 설명합니다.</p><h2>고밀도 벡터 유형</h2><p>Elasticsearch의 <code>dense_vector</code> 필드 유형은 거의 모든 차원이 관련된 텍스트, 이미지, 오디오와 같은 데이터의 숫자 표현인 고밀도 벡터를 저장하는 데 사용됩니다. 이러한 벡터는 OpenAI, Cohere 또는 Hugging Face와 같은 플랫폼에서 제공하는 임베딩 모델을 사용하여 생성되며, 다른 문서와 정확한 용어를 공유하지 않더라도 데이터의 전체적인 의미적 의미를 포착하도록 설계되었습니다.</p><p>Elasticsearch에서 고밀도 벡터는 사용되는 모델에 따라 최대 4096개의 차원을 가질 수 있습니다. 예를 들어, 모든 MiniLM-L6-v2 모델은 384차원의 벡터를 생성하는 반면, OpenAI의 텍스트 임베딩-ada-002는 1536차원의 벡터를 생성합니다.</p><p><code>dense_vector</code> 필드는 사전 생성된 벡터를 사용하거나 사용자 정의 유사성 함수를 적용하거나 외부 모델과 통합하는 등 보다 강력한 제어가 필요한 경우 이러한 종류의 임베딩을 저장하는 기본 유형으로 일반적으로 채택됩니다.</p><h3>dense_vector 유형은 언제, 왜 사용하나요?</h3><p>고밀도 벡터는 문장, 단락 또는 전체 문서 간의 의미적 유사성을 포착하는 데 탁월합니다. 같은 용어가 아니더라도 텍스트의 전체적인 의미를 비교하는 것이 목표일 때 매우 효과적입니다.</p><p>고밀도 벡터 필드는 OpenAI, Cohere 또는 Hugging Face와 같은 플랫폼에서 제공하는 모델을 사용하는 외부 임베딩 생성 파이프라인이 이미 있고 이러한 벡터를 수동으로만 저장하고 쿼리하려는 경우에 이상적입니다. 이 유형의 필드는 임베딩 모델과의 호환성이 높고 생성 및 쿼리에서 완전한 유연성을 제공하므로 검색 중에 벡터를 생성, 색인 및 사용하는 방법을 제어할 수 있습니다.</p><p>또한 순위 로직을 조정해야 하는 경우를 위해 k-NN 또는 script_score와 같은 쿼리를 사용하여 다양한 형태의 시맨틱 검색을 지원합니다. 이러한 가능성으로 인해 고밀도 벡터는 검색 증강 세대(RAG), 추천 시스템, 유사도에 기반한 개인화된 검색과 같은 애플리케이션에 이상적입니다.</p><p>마지막으로 이 필드에서는 <code>cosineSimilarity</code>, <code>dotProduct</code> 또는 <code>l2norm</code> 와 같은 기능을 사용하여 관련성 로직을 사용자 지정하여 사용 사례의 필요에 따라 순위를 조정할 수 있습니다. </p><p>고밀도 벡터는 위에서 언급한 고급 사용 사례와 같은 유연성, 사용자 지정 및 호환성이 필요한 사용자에게 여전히 최고의 옵션입니다.</p><h3>고밀도 벡터 유형에 쿼리를 사용하는 방법은 무엇인가요?</h3><p><strong><code>dense_vector</code></strong> 로 정의된 필드에 대한 검색은 K-최근 이웃 쿼리를 사용합니다. 이 쿼리는 밀도 벡터가 쿼리 벡터에 가장 가까운 문서를 찾는 작업을 담당합니다. 다음은 고밀도 벡터 필드에 k-NN 쿼리를 적용하는 방법의 예시입니다:</p>{
  "knn": {
    "field": "my_dense_vector",
    "k": 10,
    "num_candidates": 50,
    "query_vector": [/* vector generated by model */]
  }
}<p>k-NN 쿼리 외에도 문서 점수를 사용자 정의할 필요가 있는 경우, 스크립트_스코어 쿼리를 사용하여 <strong>코사인 유사도, dotProduct 또는 l2norm과</strong> 같은 벡터 비교 함수와 결합하여 보다 제어된 방식으로 관련성을 계산할 수도 있습니다. 예시를 참조하세요:</p>{
"script_score": {
    "query": { "match_all": {} },
    "script": {
      "source": "cosineSimilarity(params.query_vector,
'my_dense_vector') + 1.0",
      "params": {
        "query_vector": [/* vector */]
      }
    }
  }
}<p>더 자세히 알아보고 싶으시다면 <a href="https://www.elastic.co/search-labs/blog/vector-search-set-up-elasticsearch">Elasticsearch에서 벡터 검색을 설정하는 방법</a>문서를 살펴보는 것을 추천합니다.</p><p></p><h2>희소 벡터 유형</h2><p><strong><code>sparse_vector</code></strong> 필드 유형은 대부분의 값이 0이고 일부 용어에만 가중치가 있는 숫자 표현인 스파스 벡터를 저장하는 데 사용됩니다. 이 유형의 벡터는 SPLADE 또는 ELSER(Elastic Learned Sparse EncodeR)와 같은 용어 기반 모델에서 흔히 볼 수 있습니다.</p><h3>희소 벡터 유형을 언제, 왜 사용해야 하나요?</h3><p>스파스 벡터는 의미론적 지능을 유지하면서 어휘를 보다 정밀하게 검색해야 할 때 이상적입니다. 토큰/값 쌍으로 텍스트를 표시하고 관련 가중치가 있는 가장 관련성이 높은 용어만 강조 표시하여 명확성, 제어 및 효율성을 제공합니다.</p><p>이 유형의 필드는 텍스트에서 상대적 중요도에 따라 각 토큰에 다른 가중치를 할당하는 ELSER 또는 SPLADE 모델과 같이 용어를 기반으로 벡터를 생성할 때 특히 유용합니다.</p><p>쿼리에서 특정 단어의 영향력을 제어하려는 경우 희소 벡터 유형을 사용하면 용어의 가중치를 수동으로 조정하여 결과의 순위를 최적화할 수 있습니다.</p><p>주요 이점으로는 문서가 관련성이 있는 것으로 간주된 이유를 명확하게 이해할 수 있으므로 검색의 투명성과 모든 차원을 저장하는 고밀도 벡터와 달리 0이 아닌 값을 가진 토큰만 저장되므로 저장 효율성이 높다는 점이 있습니다.</p><p>또한, 스파스 벡터는 하이브리드 검색 전략에서 이상적인 보완재이며, 밀도 벡터와 결합하여 어휘 정확도와 의미 이해를 결합할 수도 있습니다.</p><h3>희소 벡터 유형에 쿼리를 사용하는 방법은 무엇인가요?</h3><p><strong><code>sparse_vector</code></strong> 쿼리를 사용하면 토큰/값 형식의 쿼리 벡터를 기반으로 문서를 검색할 수 있습니다. 아래 쿼리 예시를 참조하세요:</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "query_vector": {
        "token1": 0.6,
        "token2": 0.2,
        "token3": 0.9
      }
    }
  }
}<p>학습된 모델을 사용하려는 경우 쿼리 텍스트를 스파스 벡터로 자동 변환하는 추론 엔드포인트를 사용할 수 있습니다:</p>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "inference_id": "the inference ID to produce the token/weights",
      "query": "search text"
    }
  }
}<p>이 주제를 더 자세히 알아보려면 <a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">학습된 ML 모델을 사용한 희소 벡터 임베딩 이해를</a> 읽어보시기 바랍니다.</p><h2>시맨틱 텍스트 유형</h2><p><strong><code>semantic_text</code></strong> 필드 유형은 Elasticsearch에서 시맨틱 검색을 사용하는 가장 간단하고 직관적인 방법입니다. 추론 엔드포인트를 통해 인덱싱 및 쿼리 시점에 임베딩 생성을 자동으로 처리합니다. 즉, 벡터를 수동으로 생성하거나 저장하는 것에 대해 걱정할 필요가 없습니다.</p><h3>시맨틱 텍스트는 언제, 왜 사용해야 하나요?</h3><p><code>semantic_text</code> 필드는 벡터를 수동으로 처리할 필요 없이 최소한의 기술적 노력으로 시작하고 싶은 분들에게 이상적입니다. 이 필드에서는 임베딩 생성 및 벡터 검색 매핑과 같은 단계를 자동화하여 더 빠르고 편리하게 설정할 수 있습니다.</p><p><strong>매핑, 임베딩 생성 및 수집 파이프라인을 수동으로 구성해야</strong> <strong>하는 복잡성을</strong> 제거하므로 단순성과 추상화를 중시하는 경우 사용을 고려해야 합니다.<code>semantic_text</code> 추론 모델을 선택하기만 하면 나머지는 Elasticsearch가 알아서 처리합니다.</p><p>주요 장점으로는 인덱싱과 쿼리 중에 수행되는 <strong>자동 임베딩 생성과</strong> 선택한 추론 모델을 지원하도록 사전 구성되어 <strong>바로 사용할 수 있는 매핑이</strong> 있습니다.</p><p>또한 이 필드에서는 <strong>긴 텍스트의 자동 분할(텍스트 청킹)을 기본적으로 지원하여</strong> 큰 텍스트를 각각 임베딩된 작은 구절로 나눌 수 있으므로 검색 정확도가 향상됩니다. 이는 특히 시맨틱 검색의 기본 엔지니어링을 다루지 않고도 빠르게 가치를 제공하고자 하는 팀의 생산성을 크게 향상시킵니다.</p><p>하지만 <code>semantic_text</code> 은 속도와 간편함을 제공하지만 이 접근 방식에는 몇 가지 한계가 있습니다. 시장 표준 모델을 사용할 수 있으며, Elasticsearch에서 추론 엔드포인트로 사용할 수 있는 한 사용할 수 있습니다. 그러나 <code>dense_vector</code> 필드에서 가능한 것처럼 <strong>외부에서 생성된 임베딩은 지원하지 않습니다</strong>.</p><p>벡터 생성 방식을 더 잘 제어하고 싶거나, 자체 임베딩을 사용하거나, 고급 전략을 위해 여러 필드를 결합해야 하는 경우 <code>dense_vector</code> 및 <code>sparse_vector</code> 필드는 보다 맞춤화된 또는 도메인별 시나리오에 필요한 유연성을 제공합니다.</p><h3>시맨틱 텍스트 유형에 쿼리를 사용하는 방법</h3><p><strong><code>semantic_text</code></strong> 이전에는 임베딩 유형(밀도형 또는 희소형)에 따라 다른 쿼리를 사용해야 했습니다. 희소 필드에는 <code>sparse_vector</code> 쿼리가 사용되었고, <code>dense_vector</code> 필드에는 KNN 쿼리가 필요했습니다.</p><p>시맨틱 텍스트 유형에서는 쿼리 벡터를 자동으로 생성하고 색인된 문서의 임베딩과 비교하는 <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">시맨틱 쿼리를</a> 사용하여 검색을 수행합니다. <strong><code>semantic_text</code></strong> 유형을 사용하면 쿼리를 포함할 추론 엔드포인트를 정의할 수 있지만, 아무것도 지정하지 않으면 인덱싱 중에 사용된 것과 동일한 엔드포인트가 쿼리에 적용됩니다.</p>{
  "query": {
    "semantic": {
      "field": "semantic_text_field",
      "query": "search text"
    }
  }
}<p>자세한 내용은 <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch의 새로운 semantic_text 매핑 문서를 읽어보시기 바랍니다: 시맨틱 검색</a> 간소화.</p><h2>결론</h2><p>Elasticsearch에서 임베딩을 매핑하는 방법을 선택할 때는 벡터를 생성하는 방법과 벡터에 대해 필요한 제어 수준을 이해하는 것이 중요합니다. 시맨틱 텍스트 필드를 사용하면 자동 및 확장 가능한 시맨틱 검색이 가능하므로 많은 초기 사용 사례에 이상적입니다. 더 많은 제어, 미세 조정된 성능 또는 사용자 지정 모델과의 통합이 필요한 경우 고밀도 벡터 및 희소 벡터 필드는 필요한 유연성을 제공합니다.</p><p>이상적인 필드 유형은 사용 사례, 사용 가능한 인프라, 머신 러닝 스택의 성숙도에 따라 달라집니다. 가장 중요한 것은 Elastic이 현대적이고 적응력이 뛰어난 검색 시스템을 구축할 수 있는 도구를 제공한다는 점입니다.</p><h2>참고 자료</h2><ul><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">시맨틱 텍스트 필드 유형</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sparse-vector.html">희소 벡터 필드 유형</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">고밀도 벡터 필드 유형</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">시맨틱 쿼리</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-sparse-vector-query.html">희소 벡터 쿼리</a></p></li><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">kNN 검색</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch의 새로운 의미론적 텍스트 매핑: 시맨틱 검색 간소화</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/sparse-vector-embedding">학습된 ML 모델을 사용한 스파스 벡터 임베딩 이해하기</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mapping-embeddings-to-elasticsearch-field-types</guid>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <category><![CDATA[매핑]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72cd3c2601b22886/6a17083b0c4857259901a9dc/f98fdff837db55b466780c0bae672aa6f6c3a966-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ML을 사용하여 필터 및 패싯 생성]]></title>
    <description><![CDATA[ML 모델을 사용하여 검색 환경에서 필터 및 패싯 생성을 자동화할 때의 장단점을 기존의 하드코딩 방식과 비교하여 살펴봅니다.]]></description>
    <content:encoded><![CDATA[<p>필터와 패싯은 검색 결과를 구체화하는 데 사용되는 메커니즘으로, 사용자가 관련 콘텐츠나 제품을 더 빠르게 찾을 수 있도록 도와줍니다. 기존 접근 방식에서는 규칙을 수동으로 정의합니다. 예를 들어 영화 카탈로그에서는 장르와 같은 속성이 필터와 패싯에 사용하도록 미리 정의되어 있습니다. 반면, AI 모델을 사용하면 영화의 특성에서 새로운 속성을 자동으로 추출할 수 있어 보다 역동적이고 개인화된 프로세스를 구현할 수 있습니다. 이 블로그에서는 각 방법의 장단점을 살펴보고, 각 방법의 적용 사례와 문제점을 강조합니다.</p><h2>필터와 패싯 비교</h2><p>시작하기 전에 필터와 패싯이 무엇인지 정의해 보겠습니다. <strong>필터는</strong> 결과 집합을 제한하는 데 사용되는 미리 정의된 속성입니다. 예를 들어 마켓플레이스에서는 검색이 수행되기 전에도 필터를 사용할 수 있습니다. 사용자는 <strong>"비디오 게임"</strong> 과 같은 카테고리를 선택한 다음 <strong>"PS5"</strong> 과 같이 전체 데이터베이스가 아닌 보다 구체적인 하위 집합으로 검색을 구체화할 수 있습니다. 이렇게 하면 보다 관련성 높은 결과를 얻을 가능성이 크게 높아집니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a77b38aae238938/6a170b821949f72a52e7aa51/5ed8868fa5017d034e1273e35c884a5430afdf3c-1600x937.png" alt="필터" /><p><strong>패싯은</strong> 필터와 유사하게 작동하지만 검색을 수행한 후에만 사용할 수 있습니다. 즉, 검색이 결과를 반환하고 이를 기반으로 새로운 세분화 옵션 목록이 생성됩니다. 예를 들어 PS5 콘솔을 검색할 때 저장 <strong>용량</strong>, <strong>배송비</strong>, <strong>색상</strong> 등의 측면이 표시되어 사용자가 이상적인 제품을 선택하는 데 도움이 될 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt166e356b80d423ef/6a170b840e2e494ca341a10f/c5633fcc5b6fbb916110faf32144d8d43572e33a-1600x937.png" alt="패싯 " /><p>이제 필터와 패싯을 정의했으니, 기존 방식과 머신 러닝(ML) 기반 방식이 구현과 사용에 미치는 영향에 대해 논의해 보겠습니다. 각 방법에는 검색 효율성에 영향을 미치는 장점과 과제가 있습니다.</p><h2>필터 및 패싯에 대한 전통적인 접근 방식</h2><p>이 접근 방식에서는 필터와 패싯이 미리 정의된 규칙에 따라 수동으로 정의됩니다. 즉, 카탈로그 구조와 사용자 요구를 고려하여 검색을 구체화하는 데 사용할 수 있는 속성이 미리 정해지고 계획되어 있습니다.</p><p>예를 들어 마켓플레이스에서 "전자제품" 또는 "패션" 같은 카테고리에는 브랜드, 형식, 가격대 등의 특정 필터가 있을 수 있습니다. 이러한 규칙은 정적으로 생성되므로 검색 환경의 일관성을 보장하지만 새로운 제품이나 카테고리가 등장할 때마다 수동으로 조정해야 합니다.</p><p>이 접근 방식은 표시되는 필터와 패싯에 대한 예측 가능성과 제어 기능을 제공하지만, 동적으로 세분화해야 하는 새로운 트렌드가 발생할 경우 제한적일 수 있습니다.</p><p><strong>장점:</strong></p><ul><li><p><strong>예측 가능성 및 제어:</strong> 필터와 패싯을 수동으로 정의할 수 있으므로 관리가 더 쉬워집니다.</p></li><li><p><strong>낮은 복잡성:</strong> 모델을 훈련할 필요가 없습니다.</p></li><li><p><strong>유지 관리의 용이성:</strong> 규칙이 미리 정의되어 있으므로 신속하게 조정 및 수정할 수 있습니다.</p></li></ul><p><strong>단점</strong>:</p><ul><li><p><strong>새 필터에는 재색인 작업이 필요합니다:</strong> 새 속성을 필터로 사용해야 할 때마다 문서에 이 정보가 포함되어 있는지 확인하기 위해 전체 데이터 집합을 다시 색인해야 합니다.</p></li><li><p><strong>동적 적응이 부족합니다:</strong> 필터는 정적이며 사용자 행동의 변화에 따라 자동으로 조정되지 않습니다.</p></li></ul><h3>필터/패싯 구현 - 고전적인 접근 방식</h3><p><strong>개발 도구인 Kibana에서는</strong> <strong>고전적인 접근 방식을</strong> 사용하여 필터/패싯 데모를 만들어 보겠습니다.</p><p>먼저 인덱스를 구성하기 위한 매핑을 정의합니다:</p>PUT videogames
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "brand": { "type": "keyword" },
      "storage": { "type": "keyword" },
      "price": { "type": "float" },
      "description": { "type": "text" }
    }
  }
}<p><strong>브랜드</strong> 및 <strong>저장</strong> 필드는 <strong>키워드로</strong> 설정되어 집계<strong>(패싯)</strong>에서 바로 사용할 수 있습니다. <strong>가격</strong> 필드는 <strong>플로트</strong> 유형으로 <strong>가격 범위를</strong> 생성할 수 있습니다.</p><p>다음 단계에서는 제품 데이터가 색인화됩니다:</p>POST videogames/_bulk
{ "index": { "_id": 1 } }
{ "name": "Play Station 5", "brand": "Sony", "storage": "1TB", "price": 499.99, "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games." }
{ "index": { "_id": 2 } }
{ "name": "Xbox Series X", "brand": "Microsoft", "storage": "1TB", "price": 499.99, "description": "Fastest, most powerful Xbox console ever. Play thousands of titles: Every game looks and plays better on Xbox Series X. At the heart of Series X is the Xbox Velocity. Architecture, which combines a custom SSD and built-in software to significantly reduce load times in and out of game. Switch between multiple games in an instant with Quick Resume. Explore new worlds and experience the action like never before with an unparalleled 12 teraflops of graphics processing power. Enjoy 4K gaming at up to 120 frames per second, premium advanced 3D sound, and more. 4K at 120 FPS: requires compatible content and display X version - with disc drive" }
{ "index": { "_id": 3 } }
{ "name": "Nintendo Switch", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "SHARPER, VIBRANT VISUALS. The new 7-inch screen on the Nintendo Switch OLED takes your gaming to the next level: vibrant colors with sharp contrasts for every moment. INTEGRATED GAMEPLAY. Enjoy the console's many multiplayer modes and connect with other players. Online or locally, the fun on the Nintendo Switch is guaranteed. ENJOY IMMERSION FOR LONGER. In addition to delivering an unparalleled experience, thanks to its improved audio, the Nintendo Switch has a rechargeable battery while you play. From 4.5 hours to 9 hours of battery life. INCLUDES SUPER MARIO BROS. WONDER. Transform your world with the phenomenal flowers in this new Mario game, full of amazing adventures, power-ups and new abilities. NINTENDO SWITCH ONLINE SUBSCRIPTION. Access online games, play with friends and enjoy the exclusive benefits of the Nintendo Switch Online subscription." }
{ "index": { "_id": 4 } }
{ "name": "Steam Deck", "brand": "Valve", "storage": "512GB", "price": 399.99, "description": "You can save games, apps, photos and videos without worrying about space. High-Level Performance: The 4-core processor and graphics ensure a dynamic experience and fast responses. High-Definition Images: Smooth transitions and sharp images provide complete immersion in the game. Wireless Connectivity: Wi-Fi technology allows you to play wherever you want, without wires or cables limiting your fun" }
{ "index": { "_id": 5 } }
{ "name": "Nintendo Switch Lite", "brand": "Nintendo", "storage": "512GB", "price": 299.99, "description": "MADE TO BE PORTABLE. Nintendo Switch Lite is designed specifically for portable gaming. The console lets you jump into your favorite games wherever you are. COMPACT AND LIGHTWEIGHT. With its sleek, lightweight design, this console is ready to hit the road wherever you are. COMPATIBLE GAMES. The Nintendo Switch Lite system plays the library of Nintendo Switch games that work in handheld mode. A WORLD OF COLOR TO CHOOSE FROM. Available in a variety of vibrant and unique colors, Nintendo Switch Lite lets you bring even more personality wherever you go." }<p>이제 브랜드, 스토리지 및 가격대별로 결과를 그룹화하여 클래식 패싯을 검색해 보겠습니다. 쿼리에서 size:0이 정의되었습니다. 이 시나리오에서는 쿼리에 해당하는 문서를 포함하지 않고 집계 결과만 검색하는 것이 목표입니다.</p>POST videogames/_search
{
  "size": 0,
  "aggs": {
    "brands": {
      "terms": { "field": "brand" }
    },
    "storage_sizes": {
      "terms": { "field": "storage" }
    },
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 300 },   
          { "from": 300, "to": 500 },  
          { "from": 500 }  
        ]
      }
    }
  }
}<p>응답에는 <strong>브랜드</strong>, <strong>스토리지</strong>, <strong>가격에</strong> 대한 카운트가 포함되어 필터와 패싯을 만드는 데 도움이 됩니다.</p>"aggregations": {
   "brands": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "Microsoft",
         "doc_count": 1
       },
       {
         "key": "Nintendo",
         "doc_count": 1
       },
       {
         "key": "Sony",
         "doc_count": 1
       },
       {
         "key": "Valve",
         "doc_count": 1
       }
     ]
   },
   "storage_sizes": {
     "doc_count_error_upper_bound": 0,
     "sum_other_doc_count": 0,
     "buckets": [
       {
         "key": "1TB",
         "doc_count": 2
       },
       {
         "key": "512GB",
         "doc_count": 2
       }
     ]
   },
   "price_ranges": {
     "buckets": [
       {
         "key": "*-300.0",
         "to": 300,
         "doc_count": 1
       },
       {
         "key": "300.0-500.0",
         "from": 300,
         "to": 500,
         "doc_count": 3
       },
       {
         "key": "500.0-*",
         "from": 500,
         "doc_count": 0
       }
     ]
   }
 }<h2>필터 및 패싯에 대한 머신 러닝/AI 기반 접근 방식</h2><p>이 접근 방식에서는 인공 지능(AI) 기술을 포함한 머신 러닝(ML) 모델이 데이터 속성을 분석하여 관련 필터와 패싯을 생성합니다. ML/AI는 미리 정의된 규칙에 의존하는 대신 인덱싱된 데이터 특성을 활용합니다. 이를 통해 새로운 패싯과 필터를 동적으로 검색할 수 있습니다.</p><p><strong>장점</strong>:</p><ul><li><p><strong>자동 업데이트:</strong> 수동으로 조정할 필요 없이 새로운 필터와 패싯이 자동으로 생성됩니다.</p></li><li><p><strong>새로운 속성 발견:</strong> <strong>이전에는 고려하지 않았던 </strong>데이터 특성을 필터로 식별하여 검색 환경을 더욱 풍부하게 만들 수 있습니다.</p></li><li><p><strong>수동 작업 감소:</strong> AI가 사용 가능한 데이터에서 학습하므로 팀에서 필터링 규칙을 지속적으로 정의하고 업데이트할 필요가 없습니다.</p></li></ul><p><strong>단점:</strong></p><ul><li><p><strong>유지 관리의 복잡성:</strong> 모델을 사용하려면 생성된 필터의 일관성을 보장하기 위해 사전 검증이 필요할 수 있습니다.</p></li><li><p><strong>ML 및 AI 전문 지식이 필요합니다:</strong> 이 솔루션은 모델 성능을 미세 조정하고 모니터링할 수 있는 자격을 갖춘 전문가가 필요합니다.</p></li><li><p><strong>관련 없는 필터의 위험:</strong> 모델이 제대로 보정되지 않은 경우 사용자에게 유용하지 않은 패싯을 생성할 수 있습니다.</p></li><li><p><strong>비용:</strong> ML 및 AI를 사용하려면 타사 서비스가 필요할 수 있으므로 운영 비용이 증가할 수 있습니다.</p></li></ul><p>잘 보정된 모델과 잘 만들어진 프롬프트가 있더라도 생성된 패싯은 검토 단계를 거쳐야 한다는 점에 유의할 필요가 있습니다. 이 검증은 수동 또는 모더레이션 규칙에 따라 이루어질 수 있으며, 콘텐츠가 적절하고 안전한지 확인합니다. 반드시 단점이 있는 것은 아니지만, 사용자에게 제공하기 전에 패싯의 품질과 적합성을 확인하는 것은 중요한 고려 사항입니다.</p><h3>필터/패싯 구현 - AI 접근 방식</h3><p>이 데모에서는 AI 모델을 사용하여 자동으로 제품 특성을 분석하고 관련 속성을 제안합니다. 잘 구조화된 프롬프트를 통해 카탈로그에서 정보를 추출하고 이를 필터와 패싯으로 변환합니다. 아래에서 프로세스의 각 단계를 설명합니다.</p><p>처음에는 <strong>추론 API를</strong> 사용하여 ML 서비스와의 통합을 위해 엔드포인트를 등록할 것입니다. 아래는 <strong>OpenAI 서비스와의</strong> 통합 예시입니다.</p>PUT _inference/completion/generate_filter_ia
{
   "service": "openai",
   "service_settings": {
       "api_key": "your-key",
       "model_id": "gpt-4o-mini"
   }
}<p>이제 프롬프트를 실행하고 모델에서 생성된 새 필터를 가져오는 파이프라인을 정의합니다.</p>PUT /_ingest/pipeline/generate_filter_ai
{
   "processors": [
     {
       "script": {
         "source": """ctx.prompt = "You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: " + ctx.name + "description: " + ctx.description + "Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try to create max 3 facets by characteristics found). Put the values into an array. Using key and value, e.g. dynamic_facets: [{ \"name\": \"Gaming Experience\", \"value\": \"Haptic Feedback\" },{ \"name\": \"Gaming Experience\", \"value\": \"Adaptive Triggers\" } - Return only a JSON."
         """
       }
     },
     {
       "inference": {
         "model_id": "generate_filter_ia",
         "input_output": {
           "input_field": "prompt",
           "output_field": "result"
         }
       }
     },
     {
       "gsub": {
         "field": "result",
         "pattern": "```json",
         "replacement": ""
       }
     },
     {
       "json" : {
         "field" : "result",
         "strict_json_parsing": false,
         "add_to_root" : true
       }
     },
     {
       "remove": {
         "field": "result"
       }
     },
     {
       "remove": {
         "field": "prompt"
       }
     }
   ]
}<p>"PlayStation 5" 제품에 대한 이 파이프라인의 시뮬레이션을 다음 설명과 함께 실행합니다:</p><p><em>놀라운 게임: 놀라운 그래픽에 감탄하고 새로운 PS5의 기능을 경험해 보세요.</em></p><p><em>놀라운 몰입감: 햅틱 피드백, 적응형 트리거, 3D 오디오 기술을 지원하여 더욱 깊이 있는 게임 환경을 경험하세요.</em></p><p><em>슬림한 디자인: PS5 디지털 에디션으로 게이머는 세련되고 컴팩트한 디자인에 강력한 게임 기술을 즐길 수 있습니다.</em></p><p><em>1TB의 저장 공간: 1TB의 내장 SSD 스토리지로 좋아하는 게임을 준비해 두고 플레이하세요.</em></p><p><em>이전 버전과의 호환성 및 게임 부스트: PS5 콘솔은 4,000개 이상의 PS4 게임을 플레이할 수 있습니다. 게임 부스트를 사용하면 최고의 PS4 콘솔 게임에서 더욱 빠르고 부드러운 프레임 속도를 즐길 수 있습니다.</em></p><p>이 시뮬레이션에서 생성된 프롬프트 출력을 관찰해 보겠습니다.</p>{
 "docs": [
   {
     "doc": {
       "_index": "index",
       "_version": "-3",
       "_id": "1",
       "_source": {
         "name": "Play Station 5",
         "result": """```json
{
 "dynamic_facets": [
   { "name": "Storage Capacity", "value": "1TB SSD" },
   { "name": "Graphics Technology", "value": "Stunning Graphics" },
   { "name": "Audio Technology", "value": "3D Audio" }
 ]
}
```""",
         "description": "Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.",
         "model_id": "generate_filter_ia",
         "prompt": """You are an expert in data organization for search and product categorization. Your task is to analyze the following product and identify the best dynamic facets that can be used in an e-commerce search experience. Product: Play Station 5description: Stunning Gaming: Marvel at stunning graphics and experience the features of the new PS5. Breathtaking Immersion: Discover a deeper gaming experience with support for haptic feedback, adaptive triggers, and 3D Audio technology. Slim Design: With the PS5 Digital Edition, gamers get powerful gaming technology in a sleek, compact design. 1TB of Storage: Have your favorite games ready and waiting for you to play with 1TB of built-in SSD storage. Backward Compatibility and Game Boost: The PS5 console can play over 4,000 PS4 games. With Game Boost, you can even enjoy faster, smoother frame rates in some of the best PS4 console games.Instructions: - Analyze the product name and description. - Extract only the dynamic facets (technological features or product characteristics that can be inferred from the description, try create max 3 facets by characteristics found). Put the values like arrays. Using key and value, e.g. dynamic_facets: [{ "name": "Gaming Experience", "value": "Haptic Feedback" },{ "name": "Gaming Experience", "value": "Adaptive Triggers" } - Return only a JSON."""
       },
       "_ingest": {
         "timestamp": "2025-03-19T22:14:32.0161803Z"
       }
     }
   }
 ]
}<p>이제 새 인덱스에 새 필드인 <strong>dynamic_facets가</strong> 추가되어 AI가 생성한 패싯을 저장합니다.</p>PUT videogames_1
{
 "mappings": {
   "properties": {
     "name": { "type": "text" },
     "brand": { "type": "keyword" },
     "storage": { "type": "keyword" },
     "price": { "type": "float" },
     "description": { "type": "text" },
     "dynamic_facets": { "type": "nested",
     "properties": { "name": { "type": "keyword" },
                     "value": { "type": "keyword" } } }
   }
 }
}<p><strong>재색인 API를</strong> 사용하여 <strong>비디오게임</strong> 인덱스를 <strong>비디오게임_1로</strong> 재색인하고, 이 과정에서 <strong>생성_필터_ai</strong> 파이프라인을 적용합니다. 이 파이프라인은 인덱싱 중에 동적 패싯을 자동으로 생성합니다.</p>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "videogames"
 },
 "dest": {
   "index": "videogames_1",
   "pipeline": "generate_filter_ai"
 }
}<p>이제 검색을 실행하여 새 필터를 가져옵니다:</p>GET videogames_1/_search
{
 "size": 0,
 "query": {
   "match": {
     "name": "nintendo"
   }
 },
 "aggs": {
   "dynamic_facets": {
     "nested": {
       "path": "dynamic_facets"
     },
     "aggs": {
       "facets": {
         "terms": {
           "field": "dynamic_facets.name"
         },
         "aggs": {
           "facets": {
             "terms": {
               "field": "dynamic_facets.value"
             }
           }
         }
       }
     }
   }
 }
}<p>결과:</p>"aggregations": {
   "dynamic_facets": {
     "doc_count": 3,
     "facets": {
       "doc_count_error_upper_bound": 0,
       "sum_other_doc_count": 0,
       "buckets": [
         {
           "key": "Frame Rate",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "120 FPS",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Gaming Resolution",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "4K",
                 "doc_count": 1
               }
             ]
           }
         },
         {
           "key": "Graphics Processing Power",
           "doc_count": 1,
           "facets": {
             "doc_count_error_upper_bound": 0,
             "sum_other_doc_count": 0,
             "buckets": [
               {
                 "key": "12 Teraflops",
                 "doc_count": 1
               }
             ]
           }
         }
       ]
     }
   }
 }<p>패싯의 구현을 상징하기 위해 아래는 간단한 프런트엔드입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb0d6aa40caf7a91a/6a170b86ab7f0839afdb9eb6/12b6d9d4f4d0985848d92841545fd22b7253ae6d-1600x1288.png" alt="패싯 구현" /><p>제시된 UI 코드는 <a href="https://gist.github.com/andreluiz1987/06d9ec1b381e942e9def0e969bd811a0">여기에</a> 있습니다.</p><h2>결론</h2><p>필터와 패싯을 만드는 두 가지 접근 방식 모두 장점과 우려되는 점이 있습니다. 수동 규칙을 기반으로 하는 고전적인 접근 방식은 제어와 비용 절감 효과를 제공하지만 지속적인 업데이트가 필요하고 새로운 제품이나 기능에 동적으로 적응하지 못합니다.</p><p>반면, AI 및 머신러닝 기반 접근 방식은 패싯 추출을 자동화하여 검색을 더욱 유연하게 만들고 수동 개입 없이 새로운 속성을 발견할 수 있게 해줍니다. 그러나 이 접근 방식은 구현 및 유지 관리가 더 복잡할 수 있으며 일관된 결과를 보장하기 위해 보정이 필요할 수 있습니다.</p><p>기존 방식과 AI 기반 방식 중 어떤 방식을 선택할지는 비즈니스의 요구와 복잡성에 따라 달라집니다. 데이터 속성이 안정적이고 예측 가능한 간단한 시나리오의 경우, 기존 접근 방식이 더 효율적이고 유지 관리가 쉬우며 인프라 및 AI 모델을 통해 불필요한 비용을 피할 수 있습니다. 반면에 ML/AI를 사용하여 패싯을 추출하면 검색 환경을 개선하고 필터링을 더욱 지능적으로 만들어 상당한 가치를 더할 수 있습니다.</p><p>중요한 것은 자동화가 투자를 정당화하는지, 아니면 기존 솔루션이 이미 비즈니스 요구 사항을 효과적으로 충족하는지 평가하는 것입니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/filters-facets-using-ml</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/filters-facets-using-ml</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[ML 연구]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4084864dcdaa25d3/6a170b880c485781f901aaa9/6f196643d573614fe5124705c7e4db9bfce004b0-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 03 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch에서 Amazon Nova 모델 사용]]></title>
    <description><![CDATA[Elasticsearch에서 Amazon Nova 모델을 사용하여 Elasticsearch의 제품 리뷰에서 감정, 진정성, 요약, 키워드를 자동으로 추출하는 방법을 알아봅니다.]]></description>
    <content:encoded><![CDATA[<p>이 글에서는 Amazon의 AI 모델 제품군인 Amazon Nova에 대해 알아보고 이를 Elasticsearch와 함께 사용하는 방법에 대해 알아보겠습니다.</p><h2>Amazon Nova 소개</h2><p>Amazon Nova는 Amazon 인공 지능 모델 제품군으로, 고성능과 비용 효율성을 제공하도록 설계된 Amazon 베드락에서 사용할 수 있습니다. 이러한 모델은 텍스트, 이미지 및 비디오 입력으로 작동하고 텍스트 출력을 생성하며 다양한 정확도, 속도 및 비용 요구 사항에 맞게 최적화되어 있습니다.</p><h3>아마존 노바 주요 모델</h3><ul><li><p>아마존 노바 마이크로: 텍스트에만 초점을 맞춘 빠르고 비용 효율적인 모델로 번역, 추론, 코드 완성 및 수학 문제 해결에 이상적입니다. 초당 200개 이상의 토큰이 생성되므로 즉각적인 응답이 필요한 애플리케이션에 이상적입니다.</p></li><li><p>Amazon Nova Lite: 이미지, 동영상, 텍스트를 빠르게 처리할 수 있는 저비용 멀티모달 모델입니다. 속도와 정확성이 뛰어나며, 비용이 중요한 요소인 대화형 및 대용량 애플리케이션에 적합합니다.</p></li><li><p>아마존 노바 프로: 높은 정확도, 속도 및 비용 효율성을 결합한 가장 진보된 옵션입니다. 동영상 요약, 질문과 답변, 소프트웨어 개발 및 AI 에이전트와 같은 복잡한 작업에 이상적입니다. 전문가 리뷰는 텍스트 및 시각적 이해력, 지침을 따르고 자동화된 워크플로를 실행하는 능력의 우수성을 입증합니다.</p></li></ul><p>Amazon Nova 모델은 콘텐츠 제작 및 데이터 분석부터 소프트웨어 개발 및 AI 기반 프로세스 자동화에 이르기까지 다양한 애플리케이션에 적합합니다.</p><p>아래에서는 자동화된 상품 리뷰 분석을 위해 Elasticsearch와 함께 Amazon Nova 모델을 사용하는 방법을 보여드리겠습니다.</p><p>저희가 할 일</p><ol><li><p>추론 API를 통해 엔드포인트를 생성하여 Amazon Bedrock과 Elasticsearch를 통합합니다.</p></li><li><p>추론 프로세서를 사용하여 파이프라인을 생성하면 추론 API 엔드포인트를 호출할 수 있습니다.</p></li><li><p>파이프라인을 사용하여 제품 리뷰를 색인하고 자동으로 리뷰 분석을 생성합니다.</p></li><li><p>통합 결과를 분석합니다.</p></li></ol><h2>Amazon Nova Lite를 이용해 Inference API에서 엔드포인트 생성</h2><p>먼저, Amazon Bedrock을 Elasticsearch와 통합하도록 추론 API를 구성합니다. <strong>아마존</strong> 노바 라이트, 아이디 amazon.nova-lite-v1:0을 정의합니다, 속도, 정확성, 비용 간의 균형을 제공하기 때문에 사용할 모델로 선택했습니다.</p><p><strong>참고:</strong> Amazon Bedrock을 사용하려면 유효한 자격 증명이 필요합니다. 액세스 키를 얻기 위한 문서는 <a href="https://docs.aws.amazon.com/keyspaces/latest/devguide/create.keypair.html">여기에서</a> 확인할 수 있습니다:</p>PUT _inference/completion/bedrock_completion_amazon_nova-lite
{
   "service": "amazonbedrock",
   "service_settings": {
       "access_key": "#access_key#",
       "secret_key": "#secret_key#",
       "region": "us-east-1",
       "provider": "amazontitan",
       "model": "amazon.nova-lite-v1:0"
   }
}<h2>리뷰 분석 파이프라인 만들기</h2><p>이제 추론 프로세서를 사용하여 검토 분석 프롬프트를 실행하는 처리 파이프라인을 생성합니다. 이 프롬프트는 리뷰 데이터를 Amazon Nova Lite로 전송하여 수행합니다:</p><ul><li><p>감성 분류(긍정, 부정 또는 중립).</p></li><li><p>요약을 검토합니다.</p></li><li><p>키워드 생성.</p></li><li><p>진위 여부 측정(진위 | 의심스러운 | 일반).</p></li></ul>PUT /_ingest/pipeline/review_analyzer_ai
{
      "processors": [
      {
        "script": 
            {
            "source": """ctx.prompt = "Analyze the following product review and return a structured JSON. Task: - Summarize the review concisely. - Detect and classify the sentiment as positive, neutral, or negative.- Generate relevant tags (keywords) based on the review content and detected sentiment. - Evaluate the authenticity of the review (authentic, suspicious, or generic). Review: " + ctx.review + " Respond in JSON format with the following fields: \"review_analyze\": {\"sentiment\": \"&lt;positive | neutral | negative&gt;\", \"authenticity\": \"&lt;authentic | suspicious | generic&gt;\",\"summary\": \"&lt;short review summary&gt;\", \"keywords\": [\"&lt;keyword 1&gt;\", \"&lt;keyword 2&gt;\", \"...\"]}}}"
            """
            }
      },
      {
        "inference": {
          "model_id": "bedrock_completion_amazon_nova-lite",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "gsub": {
          "field": "result",
          "pattern": "```json",
          "replacement": ""
        } 
      },
      {
        "json" : {
          "field" : "result",
          "strict_json_parsing": false,
          "add_to_root" : true
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
}<h2>리뷰 색인화</h2><p>이제 대량 API를 사용하여 제품 리뷰를 색인화합니다. 앞서 생성한 파이프라인이 자동으로 적용되어 Nova 모델에서 생성한 분석이 색인된 문서에 추가됩니다.</p>POST bulk/
{ "index": { "_index" : "products", "_id": 1, "pipeline":"review_analyzer_ai" } }
{ "product": "Pampers Pants Premium Care Fralda", "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks." }
{ "index": { "_index" : "products", "_id": 2, "pipeline":"review_analyzer_ai" } }
{ "product": "Portable Electric Body Massager", "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan." }
{ "index": { "_index" : "products", "_id": 3, "pipeline":"review_analyzer_ai" } }
{ "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset", "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible." }
{ "index": { "_index" : "products", "_id": 4, "pipeline":"review_analyzer_ai" } }
{ "product": "Air Fryer 4L Oil Free Fryer Mondial", "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk." }<h2>결과 쿼리 및 분석</h2><p>마지막으로 쿼리를 실행하여 Amazon Nova Lite 모델이 리뷰를 분석하고 분류하는 방법을 확인합니다. GET products/_search를 실행하면 리뷰 콘텐츠에서 생성된 필드로 이미 보강된 문서를 가져옵니다.</p><p>이 모델은 주된 감정(긍정, 중립, 부정)을 식별하고 간결한 요약을 생성하며 관련 키워드를 추출하고 각 리뷰의 진위 여부를 추정합니다. 이러한 필드는 전문을 읽지 않고도 고객의 의견을 파악하는 데 도움이 됩니다.</p><p>결과를 해석하기 위해 다음을 살펴봅니다:</p><ul><li><p>감성은 제품에 대한 소비자의 전반적인 인식을 나타냅니다.</p></li><li><p>언급된 주요 사항을 강조하는 요약입니다.</p></li><li><p>키워드는 유사한 리뷰를 그룹화하거나 피드백 패턴을 식별하는 데 사용할 수 있습니다.</p></li><li><p>신뢰성: 리뷰의 신뢰성 여부를 나타냅니다. 이는 큐레이션이나 중재에 유용합니다.</p></li></ul>   "hits": [
      {
        "_index": "products",
        "_id": "1",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Pampers Pants Premium Care Fralda",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The reviewer praises the diaper for its great material, high cotton content, and leak-proof design, especially highlighting its effectiveness for their baby.",
            "sentiment": "positive",
            "keywords": [
              "best diaper",
              "great material",
              "cotton",
              "no plastic",
              "leak-proof",
              "baby",
              "effective"
            ],
            "authenticity": "authentic"
          },
          "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks."
        }
      },
      {
        "_index": "products",
        "_id": "2",
        "_score": 1,
        "_source": {
          "product": "Portable Electric Body Massager",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product broke in three months for no apparent reason and the reviewer does not recommend it due to its short lifespan.",
            "sentiment": "negative",
            "keywords": [
              "broke",
              "short lifespan",
              "not recommend"
            ],
            "authenticity": "authentic"
          },
          "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan."
        }
      },
      {
        "_index": "products",
        "_id": "3",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The headset has good sound quality for the price but suffers from poor connectivity, especially when using the microphone or moving the headset. It also has compatibility issues with Linux.",
            "sentiment": "negative",
            "keywords": [
              "sound",
              "connectivity",
              "microphone",
              "compatibility",
              "annoying",
              "turn off and on",
              "Linux",
              "flexible stem",
              "work from home"
            ],
            "authenticity": "authentic"
          },
          "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible."
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Air Fryer 4L Oil Free Fryer Mondial",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product offers value for money but has issues with peeling, rusting, and uneven frying.",
            "sentiment": "negative",
            "keywords": [
              "value for money",
              "peeling",
              "rusting",
              "uneven frying",
              "weaker in the middle"
            ],
            "authenticity": "authentic"
          },
          "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk."
        }
      }
    ]<h2>결론</h2><p>Amazon Nova Lite와 Elasticsearch 간의 통합은 언어 모델이 어떻게 원시 리뷰를 구조화되고 가치 있는 정보로 변환할 수 있는지를 보여주었습니다. 파이프라인을 통해 리뷰를 처리함으로써 감정, 진위 여부, 요약, 키워드를 자동으로 일관성 있게 추출할 수 있었습니다.</p><p>그 결과 이 모델은 리뷰의 맥락을 이해하고, 사용자 의견을 분류하고, 각 경험에서 가장 관련성이 높은 포인트를 강조할 수 있는 것으로 나타났습니다. 이렇게 하면 검색 기능을 개선하는 데 활용할 수 있는 훨씬 더 풍부한 데이터 세트가 생성됩니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbbf13eb690294f1/6a17fddd6df73195190a115a/304713c48b568e17d0bb56b19edb28769f7801b3-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[동의어 API를 사용하여 동의어를 자동화하고 업로드하는 방법]]></title>
    <description><![CDATA[LLM을 사용해 자동으로 동의어를 식별하고 생성하는 방법을 알아보고, 프로그래밍 방식으로 Elasticsearch 동의어 API에 용어를 로드할 수 있는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>효율적인 사용자 경험을 제공하기 위해서는 검색 결과의 품질을 개선하는 것이 필수적입니다. 검색을 최적화하는 한 가지 방법은 동의어를 통해 쿼리된 용어를 자동으로 확장하는 것입니다. 이를 통해 쿼리를 보다 폭넓게 해석하여 언어의 다양성을 포괄하고 결과 매칭을 개선할 수 있습니다.</p><p>이 블로그에서는 대규모 언어 모델(LLM)을 사용해 자동으로 동의어를 식별하고 생성하여 이러한 용어를 프로그래밍 방식으로 Elasticsearch의 동의어 API에 로드할 수 있는 방법을 살펴봅니다.</p><h2>동의어는 언제 사용하나요?</h2><p>동의어를 사용하면 벡터 검색에 비해 더 빠르고 비용 효율적인 솔루션이 될 수 있습니다. 임베딩에 대한 깊은 지식이나 복잡한 벡터 수집 프로세스가 필요하지 않으므로 구현이 더 간단합니다.</p><p>또한 벡터 검색은 임베딩 인덱싱 및 검색을 위해 더 큰 저장 용량과 메모리를 필요로 하기 때문에 리소스 소비가 더 적습니다.</p><p>또 다른 중요한 측면은 검색 지역화입니다. 동의어를 사용하면 현지 언어와 관습에 따라 용어를 조정할 수 있습니다. 이는 임베딩이 지역 표현이나 국가별 용어와 일치하지 않을 수 있는 상황에서 유용합니다. 예를 들어 일부 단어나 약어는 지역에 따라 다른 의미를 가질 수 있지만 현지 사용자에게는 자연스럽게 동의어로 취급됩니다. 브라질에서는 이런 일이 매우 흔합니다. "아바칵시" 와 "아나나스" 는 같은 과일(파인애플)이지만 북동부의 일부 지역에서는 두 번째 용어가 더 일반적으로 사용됩니다. 마찬가지로 동남부에서 잘 알려진 "팡 프랑세스" 는 북동부에서는 "팡 카레카" 로 알려져 있을 수 있습니다.</p><h2>LLM을 사용하여 동의어를 생성하는 방법은 무엇인가요?</h2><p>동의어를 자동으로 구하려면 용어의 문맥을 분석하고 적절한 변형을 제안하는 LLM을 사용할 수 있습니다. 이 접근 방식을 사용하면 동의어를 동적으로 확장할 수 있으므로 고정된 사전에 의존하지 않고도 더 광범위하고 정확한 검색을 보장할 수 있습니다.</p><p>이 데모에서는 LLM을 사용하여 이커머스 제품의 동의어를 생성합니다. 많은 검색에서 쿼리된 용어의 변형으로 인해 결과가 거의 또는 전혀 반환되지 않습니다. 동의어를 사용하면 이 문제를 해결할 수 있습니다. 예를 들어 ' "스마트폰" '을 검색하면 다양한 모델의 휴대폰이 표시되어 사용자가 원하는 제품을 찾을 수 있습니다.</p><h3>필수 구성 요소</h3><p>시작하기 전에 환경을 설정하고 필요한 종속성을 정의해야 합니다. Elastic에서 제공하는 솔루션을 사용해 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">Docker에서 로컬로 Elasticsearch와 Kibana를 실행할</a> 것입니다. 코드는 Python v3.9.6으로 작성되며 다음과 같은 종속성이 있습니다:</p>pip install openai==1.59.8 elasticsearch==8.15.1<h3>제품 색인 생성</h3><p>처음에는 동의어가 지원되지 않는 제품 색인을 생성합니다. 이렇게 하면 쿼리의 유효성을 검사한 다음 동의어가 포함된 인덱스와 비교할 수 있습니다.</p><p>인덱스를 생성하기 위해 Kibana DevTools에서 다음 명령을 사용하여 제품 데이터 세트를 일괄 로드합니다:</p>POST _bulk
{"index": {"_index": "products", "_id": 10001}}
{"category": "Electronics", "name": "iPhone 14 Pro"}
{"index": {"_index": "products", "_id": 10007}}
{"category": "Electronics", "name": "MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10013}}
{"category": "Electronics", "name": "Samsung Galaxy Tab S8"}
{"index": {"_index": "products", "_id": 10037}}
{"category": "Electronics", "name": "Apple Watch Series 8"}
{"index": {"_index": "products", "_id": 10049}}
{"category": "Electronics", "name": "Kindle Paperwhite"}
{"index": {"_index": "products", "_id": 10067}}
{"category": "Electronics", "name": "Samsung QLED 4K TV"}
{"index": {"_index": "products", "_id": 10073}}
{"category": "Electronics", "name": "HP Spectre x360 Laptop"}
{"index": {"_index": "products", "_id": 10079}}
{"category": "Electronics", "name": "Apple AirPods Pro"}
{"index": {"_index": "products", "_id": 10115}}
{"category": "Electronics", "name": "Amazon Echo Show 10"}
{"index": {"_index": "products", "_id": 10121}}
{"category": "Electronics", "name": "Apple iPad Air"}
{"index": {"_index": "products", "_id": 10127}}
{"category": "Electronics", "name": "Apple AirPods Max"}
{"index": {"_index": "products", "_id": 10151}}
{"category": "Electronics", "name": "Sony WH-1000XM4 Headphones"}
{"index": {"_index": "products", "_id": 10157}}
{"category": "Electronics", "name": "Google Pixel 6 Pro"}
{"index": {"_index": "products", "_id": 10163}}
{"category": "Electronics", "name": "Apple MacBook Air"}
{"index": {"_index": "products", "_id": 10181}}
{"category": "Electronics", "name": "Google Pixelbook Go"}
{"index": {"_index": "products", "_id": 10187}}
{"category": "Electronics", "name": "Sonos Beam Soundbar"}
{"index": {"_index": "products", "_id": 10199}}
{"category": "Electronics", "name": "Apple TV 4K"}
{"index": {"_index": "products", "_id": 10205}}
{"category": "Electronics", "name": "Samsung Galaxy Watch 4"}
{"index": {"_index": "products", "_id": 10211}}
{"category": "Electronics", "name": "Apple MacBook Pro 16-inch"}
{"index": {"_index": "products", "_id": 10223}}
{"category": "Electronics", "name": "Amazon Echo Dot (4th Gen)"}<h3>LLM 와 동의어 생성</h3><p>이 단계에서는 LLM을 사용하여 동의어를 동적으로 생성합니다. 이를 위해 OpenAI API를 통합하여 적절한 모델과 프롬프트를 정의할 것입니다. LLM은 제품 카테고리와 이름을 수신하여 동의어가 문맥과 관련이 있는지 확인합니다.</p>import json
import logging

from openai import OpenAI

def call_gpt(prompt, model):
    try:
        logging.info("generate synonyms by llm...")
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            temperature=0.7,
            max_tokens=1000
        )
        content = response.choices[0].message.content.strip()
        return content
    except Exception as e:
        logging.error(f"Failed to use model: {e}")
        return None

def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms<p>생성된 제품 색인에서 "Electronics" 카테고리의 모든 항목을 검색하여 해당 이름을 LLM으로 보냅니다. 예상 출력은 다음과 같습니다:</p>{
  "iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],
  "MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],
  "Samsung Galaxy Tab S8": ["Tab", "Tablet", "Slate", "Pad"],
  "Bose QuietComfort 35 Headphones": ["Headphones", "earphones", "earbuds", "headset"]
}<p>생성된 동의어를 사용하면 동의어 API를 사용하여 Elasticsearch에 동의어를 등록할 수 있습니다.</p><h3>동의어 API로 동의어 관리하기</h3><p>동의어 API는 시스템 내에서 직접 동의어 집합을 효율적으로 관리할 수 있는 방법을 제공합니다. 각 동의어 세트는 동의어 규칙으로 구성되며, 여기서 단어 그룹은 검색에서 동등한 것으로 취급됩니다.</p><p><strong>동의어 집합 생성 예시</strong></p>PUT _synonyms/my-synonyms-set
{
  "synonyms_set": [
    {
      "id": "rule-1",
      "synonyms": "hello, hi"
    },
    {
      "synonyms": "bye, goodbye"
    }
  ]
}<p>
이렇게 하면 "hello" 및 "hi" 가 동등한 것으로 취급되는 "my-synonyms-set,", "bye" 및 "goodbye라는 집합이 만들어집니다."</p><h2>제품 카탈로그에 동의어 생성 구현하기</h2><p>다음은 동의어 집합을 구축하고 이를 Elasticsearch에 삽입하는 방법입니다. 동의어 규칙은 LLM에서 제안한 동의어 매핑을 기반으로 생성됩니다. 각 규칙에는 슬러그 형식의 제품 이름에 해당하는 ID와 LLM에서 계산한 동의어 목록이 있습니다.</p>import json
import logging

from elasticsearch import Elasticsearch
from slugify import slugify

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)

def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       response = es.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Error create synonyms: {str(e)}")
       return None<p>다음은 동의어 집합을 생성하기 위한 요청 페이로드입니다:</p>{
   "synonyms_set":[
      {
         "id": "iphone-14-pro",
         "synonyms": "iPhone, smartphone, mobile, handset"
      },
      {
         "id": "macbook-pro-16-inch",
         "synonyms": "MacBook, Laptop, Notebook, Computer"
      },
      {
         "id": "samsung-galaxy-tab-s8",
         "synonyms": "Tablet, Slate, Pad, Device"
      },
      {
         "id": "garmin-forerunner-945",
         "synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"
      },
      {
         "id": "bose-quietcomfort-35-headphones",
         "synonyms": "Headphones, Earphones, Headset, Cans"
      }
   ]
}<p>클러스터에 동의어 집합이 생성되면 정의된 집합을 사용하여 동의어를 지원하는 새 인덱스를 생성하는 다음 단계로 넘어갈 수 있습니다.</p><p>LLM에서 생성한 동의어와 동의어 API에서 정의한 동의어 세트 생성이 포함된 전체 Python 코드는 아래와 같습니다:</p>import json
import logging

from elasticsearch import Elasticsearch
from openai import OpenAI
from slugify import slugify

logging.basicConfig(level=logging.INFO)

client = OpenAI(
   api_key="your-key",
)

es = Elasticsearch(
    "http://localhost:9200",
    api_key="your_api_key"
)


def call_gpt(prompt, model):
   try:
       logging.info("generate synonyms by llm...")
       response = client.chat.completions.create(
           model=model,
           messages=[{"role": "user", "content": prompt}],
           temperature=0.7,
           max_tokens=1000
       )
       content = response.choices[0].message.content.strip()
       return content
   except Exception as e:
       logging.error(f"Failed to use model: {e}")
       return None


def generate_synonyms(category, products):
   synonyms = {}

   for product in products:
       prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
       prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
       prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
       prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
       prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."

       response = call_gpt(prompt, "gpt-4o")
       synonyms[product] = response

   return synonyms


def get_products(category):
   query = {
       "size": 50,
       "_source": ["name"],
       "query": {
           "bool": {
               "filter": [
                   {
                       "term": {
                           "category.keyword": category
                       }
                   }
               ]
           }
       }
   }
   response = es.search(index="products", body=query)

   if response["hits"]["total"]["value"] &gt; 0:
       product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]
       return product_names
   else:
       return []


def mount_synonyms(results):
   synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
                   results.items()]

   try:
       es_client = get_client_es()
       response = es_client.synonyms.put_synonym(id="products-synonyms-set",
                                                 synonyms_set=synonyms_set)

       logging.info(json.dumps(response.body, indent=4))
       return response.body
   except Exception as e:
       logging.error(f"Erro update synonyms: {str(e)}")
       return None


if __name__ == '__main__':
   category = "Electronics"
   products = get_products("Electronics")
   llm_synonyms = generate_synonyms(category, products)
   mount_synonyms(llm_synonyms)<h3>동의어 지원으로 색인 생성</h3><p><code>products</code> 인덱스의 모든 데이터가 재색인되는 새 인덱스가 생성됩니다. 이 인덱스는 앞서 만든 <code>products-synonyms-set</code> 을 적용하는 <code>synonyms_filter</code> 을 사용합니다.</p><p>다음은 동의어를 사용하도록 구성된 인덱스 매핑입니다:</p>PUT products_02
{
  "settings": {
    "analysis": {
      "filter": {
        "synonyms_filter": {
          "type": "synonym",
          "synonyms_set": "products-synonyms-set",
          "updateable": true
        }
      },
      "analyzer": {
        "synonyms_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "synonyms_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "ID": {
        "type": "long"
      },
      "category": {
        "type": "keyword"
      },
      "name": {
        "type": "text",
        "analyzer": "standard",
        "search_analyzer": "synonyms_analyzer"
      }
    }
  }
}<h3><code>products</code> 색인 재색인하기</h3><p>이제 <strong>재색인 API를</strong> 사용하여 <code>products</code> 인덱스의 데이터를 동의어 지원을 포함하는 새로운 <code>products_02</code> 인덱스로 마이그레이션합니다. 다음 코드는 Kibana 개발자 도구에서 실행되었습니다:
</p>POST _reindex
{
  "source": {
    "index": "products"
  },
  "dest": {
    "index": "products_02"
  }
}<p>마이그레이션이 완료되면 <code>products_02</code> 인덱스가 채워지고 구성된 동의어 집합을 사용하여 검색을 검증할 준비가 됩니다.</p><h3>동의어로 검색 유효성 검사</h3><p>두 색인 간의 검색 결과를 비교해 보겠습니다. 두 인덱스에서 동일한 쿼리를 실행하고 동의어가 결과를 검색하는 데 사용되고 있는지 확인합니다.</p><h4><code>products</code> 색인에서 검색(동의어 제외)</h4><p>Kibana를 사용해 검색을 수행하고 결과를 분석합니다. 분석 &gt; 검색 메뉴에서 생성한 인덱스의 데이터를 시각화할 수 있는 데이터 보기를 만듭니다.</p><p>Discovery에서 데이터 보기를 클릭하고 이름과 인덱스 패턴을 정의합니다. "<strong>products</strong>" 인덱스의 경우 "<strong>products</strong>" 패턴을 사용합니다. 그런 다음 이 과정을 반복하여 "<strong>products_02"</strong>패턴을 사용하여 "<strong>products_02 " 인덱스에 대한</strong> 새 데이터 뷰를 만듭니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte826fd932cfeb9df/6a17fdffec0f8912aa5a6841/3ad4a6891a3905e96532a312932fdf3a8216aec2-1600x599.png" alt="" /><p>데이터 보기를 구성했으면 Analytics &gt; Discovery로 돌아가 유효성 검사를 시작할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba3729c60068e8a0/6a17fe01e9ea87ba2aa9c82a/422c4b2b51abae6580cad25085d1b8a365fc6b9e-1294x850.png" alt="" /><p>여기서 DataView 제품을 선택하고 "태블릿" 이라는 용어를 검색한 후 "Kindle Paperwhite" 및 "Apple iPad Air" 와 같은 제품이 있다는 것을 알고 있음에도 불구하고 결과가 표시되지 않습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c6a383dd4cb0157/6a17fe02577262671d1bce0c/e4ae3a785fdd93f48d7c7d204185ded149126f2c-1600x862.png" alt="" /><h4><code>products_02</code> 색인에서 검색(동의어 지원)</h4><p>동의어를 지원하는 "<strong>products_synonyms</strong>" 데이터 뷰에서 동일한 쿼리를 수행했을 때 제품이 성공적으로 검색되었습니다. 이는 구성된 동의어 세트가 올바르게 작동하여 검색된 용어의 다양한 변형이 예상 결과를 반환하는지 확인합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt986b4706e2f70014/6a17fe043e9e454edbba16d3/e609749c39e90d5c82fa846af6124679dd62bcb8-1600x526.png" alt="" /><p>Kibana 개발자 도구에서 직접 동일한 쿼리를 실행하여 동일한 결과를 얻을 수 있습니다. Elasticsearch 검색 API를 사용해 products_02 인덱스를 검색하기만 하면 됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe829a3c4d7aac60/6a17fe05e8fbce03d73a1bd7/504d0d1f96dcfbceb309063dc0716bcee64ad2f8-1600x870.png" alt="" /><h2>결론</h2><p>Elasticsearch에서 동의어를 구현함으로써 제품 카탈로그 검색의 정확도와 범위가 개선되었습니다. 핵심적인 차별화 요소는 사전 정의된 목록이 필요 없이 상황에 따라 자동으로 동의어를 생성하는 <strong>LLM을</strong> 사용했다는 점입니다. 이 모델은 제품 이름과 카테고리를 분석하여 이커머스와 관련된 동의어를 확보했습니다.</p><p>또한 동의어 <strong>API는</strong> 사전 관리를 간소화하여 동의어 집합을 동적으로 수정할 수 있도록 했습니다. 이러한 접근 방식을 통해 검색은 더욱 유연해지고 다양한 사용자 쿼리 패턴에 적응할 수 있게 되었습니다.</p><p>이 프로세스는 새로운 데이터와 모델 조정을 통해 지속적으로 개선할 수 있어 점점 더 효율적인 연구 환경을 보장합니다.</p><h2>참고 자료</h2><p><strong>로컬에서 Elasticsearch 실행</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html</a></p><p><strong>동의어 API</strong></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-synonyms-automate</guid>
    <category><![CDATA[정확도]]></category>
    <category><![CDATA[기본]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f0247b9bc1d1ccd/6a17fe07ec0f891c745a6845/05a3cfeaa387561d5334ca3f1609035ddfff7481-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Airbyte를 통해 Elasticsearch로 데이터를 수집하는 방법]]></title>
    <description><![CDATA[Airbyte를 사용해 Elasticsearch로 데이터를 수집합니다. 전제 조건, Airbyte 구성 및 단계별 통합에 대해 설명합니다.]]></description>
    <content:encoded><![CDATA[<p>Airbyte는 다양한 소스에서 다양한 대상으로 정보를 자동화되고 확장 가능한 방식으로 이동할 수 있는 데이터 통합 도구입니다. API, 데이터베이스 및 기타 시스템에서 데이터를 추출하여 고급 검색과 효율적인 분석을 제공하는 Elasticsearch와 같은 플랫폼으로 로드할 수 있습니다.</p><p>이 문서에서는 주요 개념, 전제 조건 및 단계별 통합을 다루면서 Elasticsearch로 데이터를 수집하도록 Airbyte를 구성하는 방법에 대해 설명합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99eabff95c587c00/6a17e300e8fbce2d303a18a9/ea7af907dfd4c0b7e8673164467ee236623282d2-1360x802.png" alt="데이터를 Elasticsearch로 수집하도록 Airbyte 구성하기" /><h2>에어바이트 기본 개념</h2><p>에어바이트에는 몇 가지 필수 개념이 있습니다. 아래에서는 주요 내용을 강조합니다:</p><ul><li><p>소스: 추출할 데이터의 출처를 정의합니다.</p></li><li><p>대상: 대상: 데이터를 전송하고 저장할 위치를 정의합니다.</p></li><li><p>연결: 동기화 빈도를 포함하여 소스와 대상 간의 관계를 구성합니다.</p></li></ul><h2>Airbyte와 Elasticsearch 통합</h2><p>이 데모에서는 S3 버킷에 저장된 데이터를 Elasticsearch 인덱스로 마이그레이션하는 통합을 수행합니다. Airbyte에서 소스(S3)와 대상(Elasticsearch)을 구성하는 방법을 보여드리겠습니다.</p><h3>필수 구성 요소</h3><p>이 데모를 따라하려면 다음 전제 조건을 충족해야 합니다:</p><ol><li><p>데이터가 포함된 JSON 파일이 저장될 버킷을 AWS에 생성합니다.</p></li><li><p>Docker를 사용하여 <a href="https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart">로컬에 Airbyte를 설치합니다</a>.</p></li><li><p>수집된 데이터를 저장하기 위해 Elastic Cloud에서 Elasticsearch 클러스터를 생성합니다.</p></li></ol><p>아래에서 이러한 각 단계에 대해 자세히 설명합니다.</p><h4>Airbyte 설치</h4><p>Airbyte는 Docker를 사용하여 로컬에서 실행하거나 사용 비용이 발생하는 클라우드에서 실행할 수 있습니다. 이 데모에서는 Docker가 포함된 로컬 버전을 사용하겠습니다.</p><p>설치하는 데 몇 분 정도 걸릴 수 있습니다. 설치 지침을 따르고 나면 Airbyte를 http://localhost:8000 에서 사용할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f9149be887b5500/6a17e302abe0f2b1c6dfe956/66147b5121413ad9baecb10c6886288e917f7c09-1600x1102.png" alt="Airbyte 설치" /><p></p><p>로그인한 후 연동 구성을 시작할 수 있습니다.</p><h4>버킷 만들기</h4><p>이 단계에서는 S3 버킷을 생성하기 위해 AWS 계정이 필요합니다. 또한 버킷에 대한 액세스를 허용하는 정책과 IAM 사용자를 만들어 올바른 권한을 설정하는 것이 중요합니다.</p><p>버킷에 다양한 로그 레코드가 포함된 JSON 파일을 업로드하고 나중에 Elasticsearch로 마이그레이션할 것입니다. 파일 로그에는 이 내용이 있습니다:</p>{
   "timestamp": "2025-02-15T14:00:12Z",
   "level": "INFO",
   "service": "data_pipeline",
   "message": "Pipeline execution started",
   "details": {
       "pipeline_id": "abc123",
       "source": "MySQL",
       "destination": "Elasticsearch"
   }
}<p>아래는 버킷에 로드된 파일입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf769c5e9bdb5dfe/6a17e3043e9e45a360ba13f7/f3e7f5889002e3a804121a880d97f1d93044e2f7-1600x680.png" alt="Airbyte의 버킷에 로드된 파일" /><h4>Elastic Cloud 구성</h4><p>데모를 더 쉽게 하기 위해 Elastic Cloud를 사용하겠습니다. 아직 계정이 없는 경우 여기에서 무료 체험판 계정을 생성할 수 있습니다: <a href="https://cloud.elastic.co/registration">Elastic Cloud 등록</a>.</p><p>Elastic Cloud에서 배포를 구성한 후에는 다음과 같은 정보를 얻어야 합니다:</p><ul><li><p>Elasticsearch 서버의 URL입니다.</p></li><li><p>Elasticsearch에 액세스하는 사용자입니다.</p></li></ul><p>URL을 얻으려면 배포 &gt; 내 배포로 이동하여 애플리케이션에서 Elasticsearch를 찾아 '엔드포인트 복사'를 클릭합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltddfaaf863168e2bb/6a17e305faa913e29793c7e4/2e38c0bfb2cea83d9ef90dba0e673559fe358199-1368x1056.png" alt="Elastic Cloud 구성" /><p>사용자를 만들려면 아래 단계를 따르세요:</p><ol><li><p>Kibana에 액세스 &gt; 스택 관리 &gt; 사용자.</p></li><li><p>수퍼유저 역할을 가진 새 사용자를 만듭니다.</p></li><li><p>필드를 채워 사용자를 만듭니다.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca15b0d3084f4c67/6a17e3073e9e4507e2ba13fb/54d098805087a2d60772475cbb31784083a38c25-1600x1026.png" alt="Elastic Cloud에서 사용자 생성" /><p>이제 모든 설정이 완료되었으므로 Airbyte에서 커넥터 구성을 시작할 수 있습니다.</p><h3>소스 커넥터 구성</h3><p>이 단계에서는 S3용 소스 커넥터를 만들겠습니다. 이를 위해 Airbyte 인터페이스에 액세스하고 메뉴에서 소스 옵션을 선택합니다. 그런 다음 S3 커넥터를 검색합니다. 아래에서는 커넥터를 구성하는 데 필요한 단계를 자세히 설명합니다:</p><ol><li><p>Airbyte에 액세스하고 소스 메뉴로 이동합니다.</p></li><li><p>S3 커넥터를 검색하여 선택합니다.</p></li><li><p>다음 매개변수를 구성합니다:</p><ol><li><p>소스 이름: 데이터 소스의 이름을 정의합니다.</p></li><li><p>전달 방법: 레코드 복제를 선택합니다(구조화된 데이터에 권장).</p></li><li><p>데이터 형식: JSON 형식을 선택합니다.</p></li><li><p>스트림 이름: Elasticsearch에서 인덱스의 이름을 정의합니다.</p></li><li><p>버킷 이름: AWS에서 버킷의 이름을 입력합니다.</p></li><li><p>AWS 액세스 키와 AWS 비밀 키를 입력합니다: 액세스 자격 증명을 입력합니다.</p></li></ol></li></ol><p>소스 설정을 클릭하고 유효성 검사를 기다립니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdca1fb8d8f5d034f/6a17e30963baff75bc741bb2/f83566ab5ebad07ad9fd61dc20353ca5a95c8c92-1600x1099.png" alt="Airbyte 및 Elasticsearch 데이터 수집에 대한 유효성 검사를 기다립니다." /><h3>구성 대상 커넥터</h3><p>이 단계에서는 대상 커넥터를 구성할 것이며, 이 커넥터는 Elasticsearch가 될 것입니다. 이렇게 하려면 메뉴에 액세스하여 대상 옵션을 선택합니다. 그런 다음 Elasticsearch를 검색하고 반환된 결과를 클릭합니다. 이제 이 연결의 구성을 진행하겠습니다:</p><ol><li><p>Airbyte에 액세스하고 목적지 메뉴로 이동합니다.</p></li><li><p>Elasticsearch 커넥터를 검색하고 선택합니다.</p></li><li><p>다음 매개변수를 구성합니다:</p><ol><li><p>인증 방법: 사용자 이름/비밀번호를 선택합니다.</p></li><li><p>사용자 이름 및 비밀번호: Kibana에서 생성한 자격 증명을 사용합니다.</p></li><li><p>서버 엔드포인트: Elastic Cloud에서 복사한 URL을 붙여넣습니다.</p></li></ol></li></ol><p><strong>대상 설정을</strong> 클릭하고 유효성 검사를 기다립니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72056179d048e027/6a17e30b033c8d5d9d6bb115/f9246b54cc77e0589c0bf658ffc274fd28f766d5-1600x941.png" alt="Airbyte 데이터 수집을 위해 Elastic Cloud에서 대상 만들기" /><h3>소스 및 대상 연결 만들기</h3><p>소스 및 대상이 생성되면 소스 및 대상 간의 연결이 생성되어 통합 생성이 완료됩니다. </p><p>다음은 연결을 만드는 방법에 대한 안내입니다:</p><p>1. 메뉴에서 연결로 이동하여 첫 번째 연결 만들기를 클릭합니다.</p><p>2. 다음 화면에서 기존 소스를 선택하거나 새 소스를 만들 수 있습니다. 이미 생성한 소스가 있으므로 소스 S3를 선택합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7f1e01215a7a328/6a17e30c63baff53d7741bb6/14937ccb2686bbde7cb99ffe7e13f7f4e35d7a31-1600x393.png" alt="Airbyte에서 기존 소스를 선택하거나 새 소스를 생성합니다." /><p>3. 다음 단계는 목적지를 선택하는 것입니다. 이미 Elasticsearch 커넥터를 생성했으므로 구성을 완료하기 위해 이 커넥터가 선택됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltece5c720f28eee00/6a17e30d505ac3dc68ad8aa3/d10962bc175f9ce0b2745ea913d739d3c40ba3b6-1600x431.png" alt="Airbyte에서 목적지 선택" /><p>다음 단계에서는 동기화 모드와 어떤 스키마를 사용할지 정의해야 합니다. 로그 스키마만 생성되었으므로 선택할 수 있는 옵션은 로그 스키마가 유일합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7cbcccad9cfd5a8d/6a17e30f414c64d32d9450e9/d5d3a2e20038d17ef701822fe7ccc38d6e175c50-1600x805.png" alt="에어바이트에서 동기화 모드 정의" /><p>4. 연결 구성 단계로 이동합니다. 여기에서 연결 이름과 통합 실행 빈도를 정의할 수 있습니다. 주파수는 세 가지 방법으로 구성할 수 있습니다:</p><ul><li><p><strong>Cron</strong>: 사용자 정의 크론 표현식(예: 0 0 15 * * ?, 매일 15:00에)에 따라 동기화를 실행합니다;</p></li><li><p><strong>예약됨</strong>: 지정된 시간 간격으로 동기화를 실행합니다(예 24시간마다, 2시간마다);</p></li><li><p><strong>수동</strong>: 수동으로 동기화를 실행합니다.</p></li></ul><p>이 데모에서는 수동 옵션을 선택하겠습니다.</p><p>마지막으로 <strong>연결 설정을</strong> 클릭하면 소스와 대상 간의 연결이 설정됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f5fc0e51035b733/6a17e311af47b67ac8cddeff/1a0470f6dff341cb90e6ecfd8ae87a7d2243cbf4-1600x626.png" alt="Airbyte에서 연결 설정을 클릭합니다." /><h3>S3에서 Elasticsearch로 데이터 동기화하기</h3><p>연결 화면으로 돌아오면 생성된 연결을 확인할 수 있습니다. 프로세스를 실행하려면 동기화를 클릭하기만 하면 됩니다. 그 순간부터 S3에서 Elasticsearch로 데이터 마이그레이션이 시작됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b0ad7a7446f9d58/6a17e3121d1b83b4c593e3c3/c1ce54b129eafb2533b638e4df2b96f3a266ad62-1600x347.png" alt="S3에서 Airbyte의 Elasticsearch로 데이터 동기화하기" /><p>모든 것이 순조롭게 진행되면 동기화 상태가 표시됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt857cdad5bd7fe03f/6a17e313be608646e00046b9/6fe9d5826787066bd8bf0f5e17905df9f8d698e6-1600x361.png" alt="Airbyte의 S3에서 Elasticsearch로 동기화된 상태" /><h3>Kibana에서 데이터 시각화하기</h3><p>이제 Kibana로 이동하여 데이터를 분석하고 데이터가 올바르게 색인되었는지 확인하겠습니다. Kibana Discovery 섹션에서 로그라는 데이터 보기를 생성합니다. 이를 통해 동기화 이후에 생성된 로그 인덱스에만 존재하는 데이터를 탐색할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bc137f7c04e50ee/6a17e3151480094f2eb486cf/6b6909647ac3187d0fee477cfd732741314f82cf-1600x566.png" alt="Kibana에서 데이터 시각화하기: Airbyte와 Elastic" /><p>이제 색인된 데이터를 시각화하고 분석을 수행할 수 있습니다. 이렇게 해서 Airbyte를 사용해 전체 마이그레이션 흐름을 검증하고, 버킷에 있는 데이터를 로드하고 Elasticsearch에서 색인을 생성했습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8c0a36a83ee4bb1/6a17e317b1e1135ea679f20e/ca8e9b3d7f7112291af58acf514f4573b44037e8-1600x806.png" alt="Airbyte와 Elastic: Kibana에서 색인된 데이터를 시각화하고 분석을 수행합니다." /><h2>결론: Airbyte &amp; Elasticsearch 통합</h2><p>Airbyte는 데이터 통합을 위한 효율적인 도구로 여러 소스와 대상을 자동화된 방식으로 연결할 수 있는 것으로 입증되었습니다. 이 튜토리얼에서는 S3 버킷에서 Elasticsearch 인덱스로 데이터를 수집하는 방법을 시연하면서 프로세스의 주요 단계를 강조했습니다.</p><p>이 접근 방식은 대량의 데이터 수집을 용이하게 하고 복잡한 검색, 집계, 데이터 시각화와 같은 분석을 Elasticsearch 내에서 수행할 수 있게 해줍니다.</p><h2>참고 자료</h2><p><strong>에어바이트 퀵스타트:</strong></p><p><a href="https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart#part-1-install-abctl">https://docs.airbyte.com/using-airbyte/getting-started/oss-quickstart#part-1-install-abctl</a></p><p><strong>핵심 개념:</strong></p><p><a href="https://docs.airbyte.com/using-airbyte/core-concepts/">https://docs.airbyte.com/using-airbyte/core-concepts/</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/airbyte-elasticsearch-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/airbyte-elasticsearch-ingest-data</guid>
    <category><![CDATA[인덱스 데이터]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5defe5e12935b233/6a17e318505ac3ed7fad8aa7/dce2bad9949006163af95ed05b5a1eacf5393dc7-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 14 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LlamaIndex를 통해 Elasticsearch로 데이터를 수집하는 방법]]></title>
    <description><![CDATA[라마인덱스를 사용하여 데이터를 수집하고 검색하는 방법에 대한 단계별 안내입니다.]]></description>
    <content:encoded><![CDATA[<p>이 문서에서는 데이터 색인을 생성하기 위해 LlamaIndex를 사용하여 FAQ 검색 엔진을 구현해 보겠습니다. Elasticsearch는 벡터 데이터베이스 역할을 하여 벡터 검색을 가능하게 하고, RAG(검색 증강 세대)는 컨텍스트를 보강하여 보다 정확한 응답을 제공합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5895fbc057ffc1b/6a17f4cf3e9e45288bba15ef/7ac65a686bdd76c145e903f5c3110c62875a525f-972x501.png" alt="LlamaIndex &amp; Elasticsearch: 문서 수집 및 FAQ 검색 구축" /><h2>라마인덱스란 무엇인가요?</h2><p>LlamaIndex는 특정 또는 비공개 데이터와 상호 작용할 수 있는 대규모 언어 모델(LLM)로 구동되는 에이전트 및 워크플로우를 쉽게 만들 수 있는 프레임워크입니다. 다양한 소스(API, PDF, 데이터베이스)의 데이터를 LLM과 통합하여 연구, 정보 추출, 문맥에 맞는 응답 생성 등의 작업을 수행할 수 있습니다.</p><p><strong>주요 개념:</strong></p><ul><li><p>에이전트: 간단한 응답부터 복잡한 작업까지 다양한 작업을 수행하기 위해 LLM을 사용하는 지능형 어시스턴트입니다.</p></li><li><p>워크플로우: 고급 작업을 위해 에이전트, 데이터 커넥터 및 도구를 결합하는 다단계 프로세스입니다.</p></li><li><p>컨텍스트 증강: 외부 데이터로 LLM을 보강하여 학습의 한계를 극복하는 기술입니다.</p></li></ul><p>엘라스틱서치와<strong>라마인덱스</strong> <strong>통합:</strong></p><p>Elasticsearch는 LlamaIndex와 함께 다양한 방식으로 사용할 수 있습니다:</p><ul><li><p>데이터 소스: Elasticsearch Reader를 사용하여 문서를 추출합니다.</p></li><li><p>임베딩 모델: 시맨틱 검색을 위해 데이터를 벡터로 인코딩합니다.</p></li><li><p>벡터 저장소: 벡터화된 문서를 검색하기 위한 리포지토리로 Elasticsearch를 사용하세요.</p></li><li><p>고급 스토리지: 문서 요약 또는 지식 그래프와 같은 구조를 구성하세요.</p></li></ul><h2>LlamaIndex와 Elasticsearch를 사용하여 FAQ 검색 구축하기 </h2><h3>데이터 준비</h3><p><a href="https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html">Elasticsearch 서비스 FAQ를</a> 예로 들어보겠습니다. 각 문제는 웹사이트에서 추출하여 개별 텍스트 파일에 저장했습니다. 어떤 방식으로든 데이터를 정리할 수 있지만, 이 예에서는 파일을 로컬에 저장하는 방법을 선택했습니다.</p><p>예제 파일입니다:</p>File Name: what-is-elasticsearch-service.txt
Content: Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.<p>모든 문제를 저장한 후 디렉토리는 다음과 같이 표시됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43467eb9c5579103/6a17f4d02f4a5cc60bfa8a62/1f367d57f2e650334671a2c03156ef4412c0c615-962x704.png" alt="" /><h3>종속성 설치</h3><p>Python 언어를 사용하여 수집 및 검색을 구현할 것이며, 제가 사용한 버전은 3.9입니다. 전제 조건으로 다음 종속성을 설치해야 합니다:</p>llama-index-vector-stores-elasticsearch
llama-index
openai<p>Elasticsearch와 Kibana는 버전 8.16.2를 실행하도록 docker-compose.yml을 통해 구성된 Docker로 생성됩니다. 이렇게 하면 로컬 환경을 더 쉽게 만들 수 있습니다.</p>version: '3.8'
services:

 elasticsearch:
   image: docker.elastic.co/elasticsearch/elasticsearch:8.16.2
   container_name: elasticsearch-8.16.2
   environment:
     - node.name=elasticsearch
     - xpack.security.enabled=false
     - discovery.type=single-node
     - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
   ports:
     - 9200:9200
   networks:
     - shared_network

 kibana:
   image: docker.elastic.co/kibana/kibana:8.16.2
   container_name: kibana-8.16.2
   restart: always
   environment:
     - ELASTICSEARCH_URL=http://elasticsearch:9200
   ports:
     - 5601:5601
   depends_on:
     - elasticsearch
   networks:
     - shared_network

networks:
 shared_network:<h3>LlamaIndex를 사용한 문서 수집</h3><p>문서는 LlamaIndex를 사용하여 Elasticsearch로 색인됩니다. 먼저 로컬 디렉터리에서 파일을 로드할 수 있는 <strong>SimpleDirectoryReader를</strong> 사용하여 파일을 로드합니다. 문서를 로드한 후 <strong>벡터스토어인덱스를</strong> 사용하여 색인을 생성합니다.</p>documents = SimpleDirectoryReader("./faq").load_data()

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)<p>라마인덱스의 벡터 스토어는 문서 임베딩의 저장 및 관리를 담당합니다. LlamaIndex는 다양한 유형의 벡터 저장소를 지원하며, 이 경우에는 Elasticsearch를 사용하겠습니다. StorageContext에서 Elasticsearch 인스턴스를 구성합니다. 컨텍스트가 로컬이므로 추가 매개 변수가 필요하지 않았습니다. 다른 환경에서의 구성은 설명서를 참조하여 필요한 매개 변수를 확인하세요: <a href="https://docs.llamaindex.ai/en/stable/examples/vector_stores/ElasticsearchIndexDemo/#configuring-elasticsearchstore">ElasticsearchStore 구성</a>.</p><p>기본적으로 LlamaIndex는 OpenAI <strong>텍스트 임베딩-ada-002</strong> 모델을 사용하여 임베딩을 생성합니다. 하지만 이 예제에서는 <strong>텍스트 임베딩 3-소형</strong> 모델을 사용합니다. 이 모델을 사용하려면 OpenAI API 키가 필요하다는 점에 유의하세요.</p><p>아래는 문서 수집을 위한 전체 코드입니다.</p>import openai
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore

openai.api_key = os.environ["OPENAI_API_KEY"]

es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200"
)

def format_title(filename):
   filename_without_ext = filename.replace('.txt', '')
   text_with_spaces = filename_without_ext.replace('-', ' ')
   formatted_text = text_with_spaces.title()

   return formatted_text


embed_model = OpenAIEmbedding(model="text-embedding-3-small")

documents = SimpleDirectoryReader("./faq").load_data()

for doc in documents:
   doc.metadata['title'] = format_title(doc.metadata['file_name'])

storage_context = StorageContext.from_defaults(vector_store=es)
index = VectorStoreIndex(documents, storage_context=storage_context, embed_model=embed_model)<p>실행 후 문서가 아래와 같이 <strong>FAQ</strong> 색인에 색인됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt391ae1bfc1daefbf/6a17f4d22f4a5c9887fa8a66/d59b85ed1f57bf80e84d6cb0d6d722a2d12ae4c0-1600x745.png" alt="" /><h3>RAG로 검색</h3><p>검색을 수행하기 위해 <strong>ElasticsearchStore</strong> 클라이언트를 구성하여 <strong>index_name</strong> 및 <strong>es_url</strong> 필드를 Elasticsearch URL로 설정합니다. <strong>retrieval_strategy에서</strong> 벡터 검색을 위한 <strong>AsyncDenseVectorStrategy를</strong> 정의했습니다. <strong>AsyncBM25Strategy</strong> (키워드 검색) 및 <strong>AsyncSparseVectorStrategy</strong> (희소 벡터)와 같은 다른 전략도 사용할 수 있습니다. 자세한 내용은 <a href="https://docs.llamaindex.ai/en/stable/api_reference/storage/vector_store/elasticsearch/">공식 문서에서</a> 확인할 수 있습니다.</p>es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)<p>다음으로, <strong>VectorStoreIndex</strong> 객체가 생성되며, 여기서 ElasticsearchStore 객체를 사용하여 <strong>vector_store를</strong> 구성합니다. <strong>as_retriever</strong> 메서드를 사용하면 <strong>유사성_top_k</strong> 매개변수를 통해 반환되는 결과의 수를 5로 설정하여 쿼리와 가장 관련성이 높은 문서를 검색합니다.</p>   index = VectorStoreIndex.from_vector_store(vector_store=es)
   retriever = index.as_retriever(similarity_top_k=5)
   results = retriever.retrieve(query)<p>다음 단계는 RAG입니다. 벡터 검색 결과는 LLM을 위한 형식화된 프롬프트에 통합되어 검색된 정보에 따라 상황에 맞는 응답이 가능합니다.</p><p>프롬프트 템플릿에서는 프롬프트 형식을 정의하며, 여기에는 다음이 포함됩니다:</p><ul><li><p>컨텍스트 ({context_str}): 검색기가 검색한 문서입니다.</p></li><li><p>쿼리 ({query_str}): 사용자의 질문입니다.</p></li><li><p>지침: 모델이 외부 지식에 의존하지 않고 상황에 따라 대응할 수 있도록 하는 지침입니다.</p></li></ul>qa_prompt = PromptTemplate(
   "You are a helpful and knowledgeable assistant."
   "Your task is to answer the user's query based solely on the context provided below."
   "Do not use any prior knowledge or external information.\n"
   "---------------------\n"
   "Context:\n"
   "{context_str}\n"
   "---------------------\n"
   "Query: {query_str}\n"
   "Instructions:\n"
   "1. Carefully read and understand the context provided.\n"
   "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
   "3. Do not make up or guess any information.\n"
   "Answer: "
)<p>마지막으로 LLM이 프롬프트를 처리하고 상황에 맞는 정확한 응답을 반환합니다.</p>llm = OpenAI(model="gpt-4o")
context_str = "\n\n".join([n.node.get_content() for n in results])
response = llm.complete(
   qa_prompt.format(context_str=context_str, query_str=query)
)

print("Answer:")
print(response)<p>전체 코드는 아래와 같습니다:</p>es = ElasticsearchStore(
   index_name="faq",
   es_url="http://localhost:9200",
   retrieval_strategy=AsyncDenseVectorStrategy(
   )
)


def print_results(results):
   for rank, result in enumerate(results, start=1):
       title = result.metadata.get("title")
       score = result.get_score()
       text = result.get_text()
       print(f"{rank}. title={title} \nscore={score} \ncontent={text}")


def search(query: str):
   index = VectorStoreIndex.from_vector_store(vector_store=es)

   retriever = index.as_retriever(similarity_top_k=10)
   results = retriever.retrieve(QueryBundle(query_str=query))
   print_results(results)

   qa_prompt = PromptTemplate(
       "You are a helpful and knowledgeable assistant."
       "Your task is to answer the user's query based solely on the context provided below."
       "Do not use any prior knowledge or external information.\n"
       "---------------------\n"
       "Context:\n"
       "{context_str}\n"
       "---------------------\n"
       "Query: {query_str}\n"
       "Instructions:\n"
       "1. Carefully read and understand the context provided.\n"
       "2. If the context contains enough information to answer the query, provide a clear and concise answer.\n"
       "3. Do not make up or guess any information.\n"
       "Answer: "
   )

   llm = OpenAI(model="gpt-4o")
   context_str = "\n\n".join([n.node.get_content() for n in results])
   response = llm.complete(
       qa_prompt.format(context_str=context_str, query_str=query)
   )

   print("Answer:")
   print(response)


question = "Elastic services are free?"
print(f"Question: {question}")
search(question)<p>이제 검색을 수행할 수 있습니다(예: "Elastic 서비스가 무료인가요?" ) 그리고 FAQ 데이터 자체를 기반으로 상황에 맞는 답변을 얻을 수 있습니다.</p>Question: Elastic services are free?
Answer:
Elastic services are not entirely free. However, there is a 14-day free trial available for exploring Elastic solutions. After the trial, access to features and services depends on the subscription level.<p>이 응답을 생성하기 위해 다음 문서가 사용되었습니다:</p>1. title=Can I Try Elasticsearch Service For Free 
score=1.0 
content=Yes, sign up for a 14-day free trial. The trial starts the moment a cluster is created.
During the free trial period get access to a deployment to explore Elastic solutions for Enterprise Search, Observability, Security, or the latest version of the Elastic Stack.

2. title=Do You Offer Elastic S Commercial Products 
score=0.9941274512218439 
content=Yes, all Elasticsearch Service customers have access to basic authentication, role-based access control, and monitoring.
Elasticsearch Service Gold, Platinum and Enterprise customers get complete access to all the capabilities in X-Pack: Security, Alerting, Monitoring, Reporting, Graph Analysis &amp; Visualization. Contact us to learn more.

3. title=What Is Elasticsearch Service 
score=0.9896776845746571 
content=Elasticsearch Service is hosted and managed Elasticsearch and Kibana brought to you by the creators of Elasticsearch. Elasticsearch Service is part of Elastic Cloud and ships with features that you can only get from the company behind Elasticsearch, Kibana, Beats, and Logstash. Elasticsearch is a full text search engine that suits a range of uses, from search on websites to big data analytics and more.

4. title=Can I Run The Full Elastic Stack In Elasticsearch Service 
score=0.9880631561979476 
content=Many of the products that are part of the Elastic Stack are readily available in Elasticsearch Service, including Elasticsearch, Kibana, plugins, and features such as monitoring and security. Use other Elastic Stack products directly with Elasticsearch Service. For example, both Logstash and Beats can send their data to Elasticsearch Service. What is run is determined by the subscription level.

5. title=What Is The Difference Between Elasticsearch Service And The Amazon Elasticsearch Service 
score=0.9835054890793161 
content=Elasticsearch Service is the only hosted and managed Elasticsearch service built, managed, and supported by the company behind Elasticsearch, Kibana, Beats, and Logstash. With Elasticsearch Service, you always get the latest versions of the software. Our service is built on best practices and years of experience hosting and managing thousands of Elasticsearch clusters in the Cloud and on premise. For more information, check the following Amazon and Elastic Elasticsearch Service comparison page.
Please note that there is no formal partnership between Elastic and Amazon Web Services (AWS), and Elastic does not provide any support on the AWS Elasticsearch Service.<h2>결론</h2><p>LlamaIndex를 사용해 벡터 데이터베이스로서 Elasticsearch를 지원하는 효율적인 FAQ 검색 시스템을 만드는 방법을 시연했습니다. 임베딩을 사용해 문서를 수집하고 색인을 생성하여 벡터 검색을 가능하게 합니다. 프롬프트 템플릿을 통해 검색 결과가 컨텍스트에 통합되어 LLM으로 전송되면 검색된 문서를 기반으로 정확한 문맥에 맞는 응답을 생성합니다.</p><p>이 워크플로는 정보 검색과 상황에 맞는 응답 생성을 통합하여 정확하고 관련성 높은 결과를 제공합니다.</p><h2>참고 자료</h2><p><a href="https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html">https://www.elastic.co/guide/en/cloud/current/ec-faq-getting-started.html</a></p><p><a href="https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/">https://docs.llamaindex.ai/en/stable/api_reference/readers/elasticsearch/</a></p><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/">https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_index/</a></p><p><a href="https://docs.llamaindex.ai/en/stable/examples/query_engine/custom_query_engine/">https://docs.llamaindex.ai/en/stable/examples/query_engine/custom_query_engine/</a></p><p><a href="https://www.elastic.co/search-labs/integrations/llama-index">https://www.elastic.co/search-labs/integrations/llama-index</a></p><p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-llamaindex-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-llamaindex-ingest-data</guid>
    <category><![CDATA[인덱스 데이터]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf9f88afa92e90390/6a17f4d33e03d7987d4f2dd0/b8b760bfd8694df43fd74ba90ae5fc1edbe4ce76-1150x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[패싯 검색: AI를 사용하여 검색 범위 및 결과 개선]]></title>
    <description><![CDATA[Elasticsearch에서 패싯 검색을 사용하여 카테고리 내의 옵션을 빠르게 좁히는 방법을 살펴보세요.]]></description>
    <content:encoded><![CDATA[<p>이 글에서는 인공지능(AI)이 특히 GPT-4와 같은 고급 언어 모델을 사용하여 어떻게 더 많은 맥락적 측면을 생성하여 사용자에게 더욱 관련성 있고 유용하게 만들 수 있는지 살펴봅니다.</p><p>패싯 검색은 이커머스 플랫폼에서 강력한 도구입니다. 표시된 항목의 특성에 따라 검색 결과를 정리하고 세분화할 수 있습니다. 흔히 필터와 혼동하기 쉽지만, 패싯은 작동 방식이 다릅니다. 필터는 제품의 카테고리나 형식과 같이 인덱스에 항상 존재하는 정보로 정의되는 고정 속성을 말합니다. 반면에 패싯은 동적이며 실행된 검색에서 반환된 결과에서 생성됩니다.</p><p>"category" (예: 티셔츠, 바지) 또는 "gender" (예: 남성, 여성)와 같은 필드는 검색 결과의 범위를 좁히는 데 도움이 되는 필터입니다. 그러나 패싯은 일반적인 색상, 사용 가능한 크기 또는 재질과 같이 결과에 나타나는 제품의 특정 특성을 반영합니다. 이를 통해 보다 적응력 있고 상황에 맞는 검색 환경을 제공할 수 있습니다.</p><p>아래는 패싯과 상호 작용하여 패싯에 의해 필터링된 검색 결과를 볼 수 있는 이미지입니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4ce8ca7e5b62ad7/6a17f8ba6864a4534bb68979/74d2159706ab7248ebe5efddc74c882f0693db71-600x420.gif" alt="패싯 검색 예제" /><h2>AI가 패싯 생성을 개선하는 방법</h2><p>인공 지능은 종종 시맨틱 검색 및 임베딩과 관련이 있지만 패싯은 어떤가요? AI를 활용하여 각 검색에 더 유용하고 상황에 맞는 패싯을 만들려면 어떻게 해야 할까요?</p><p>한 가지 흥미로운 가능성은 AI를 사용하여 색인의 기존 분류를 뛰어넘는 새로운 분류를 만드는 것입니다. 이러한 새로운 카테고리는 콘텐츠의 특정 특성을 분석하여 더욱 풍부하고 정확한 컨텍스트화를 제공함으로써 사용자의 요구와 더욱 관련성 있고 부합하는 패싯을 만들 수 있습니다. 이렇게 하면 원래 문서 범주에 비해 결과를 더욱 의미 있게 세분화할 수 있습니다.</p><h2>AI가 더 나은 검색을 위해 영화 분류를 세분화하는 방법</h2><p>현재 드라마 장르로 분류된 다음 영화를 분석해 보겠습니다:</p><ul><li><p>꿈을 위한 레퀴엠
재개하기: 마약에 중독된 코니 아일랜드 사람들의 유토피아는 중독이 깊어지면서 산산조각이 납니다.</p></li><li><p>아메리칸 뷰티
이력서: 성적으로 좌절한 교외의 아버지는 딸의 절친한 친구와 사랑에 빠진 후 중년의 위기를 겪습니다.</p></li><li><p>굿 윌 헌팅
이력서: MIT의 청소부인 윌 헌팅은 수학에 재능이 있지만 인생의 방향을 찾기 위해 심리학자의 도움이 필요합니다.</p></li></ul><p>이 장르 분류는 각 영화의 미묘한 차이점이나 고유한 맥락을 포착하지 못합니다. AI를 활용하여 시놉시스와 중심 주제를 분석함으로써 각 영화의 실제 맥락을 더 잘 반영하는 새로운 카테고리를 만들 수 있습니다. 예를 들어</p><ul><li><p>꿈을 위한 레퀴엠 - 새 카테고리: "중독과 의존성"</p></li><li><p>아메리칸 뷰티 - 새 카테고리: "중년의 위기"</p></li><li><p>굿 윌 헌팅 - 새 카테고리: "지적 투쟁"</p></li></ul><p>이러한 새로운 카테고리는 검색의 정확도를 높이는 동시에 사용자에게 보다 의미 있는 필터를 제공하여 검색 결과를 세분화할 수 있게 해줍니다. 이 접근 방식은 기존 카테고리가 지나치게 일반적일 때 특히 효과적이며, 사용자가 원하는 것을 더 쉽게 찾을 수 있도록 도와줍니다.</p><h2>GPT-4로 새 카테고리 만들기: 패싯 검색 예제</h2><p>이 예에서는 AI 모델을 사용하여 보다 정확하고 각 작품의 맥락에 맞는 새로운 영화 카테고리를 생성하는 방법을 보여드리겠습니다. 이 프로세스를 시연하기 위해 Elastic 시뮬레이션 파이프라인과 OpenAI 추론 서비스를 함께 사용하겠습니다. 새 카테고리를 결정할 수 있는 추론 프로세서에서 실행할 프롬프트를 생성하는 스크립트 프로세서를 포함하여 여러 프로세서가 포함된 파이프라인이 만들어집니다. 다른 프로세서는 파이프라인 실행 중에 생성된 데이터와 보조 필드를 조작하는 데 사용됩니다. 이 로직은 다른 유사한 도구나 모델에도 적용할 수 있다는 점을 언급할 가치가 있습니다.</p><p>먼저 추론 엔드포인트를 생성하여 서비스를 OpenAI로 정의하고, 서비스에 액세스하는 데 필요한 토큰과 모델을 정의해야 합니다. 이 예제에서는 gpt-4o-mini를 사용하고 있습니다. OpenAI 추론 서비스에 대한 자세한 내용을 보려면 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">여기를</a> 클릭하세요.</p>PUT _inference/completion/generate_topics_ia
{
    "service": "openai",
    "service_settings": {
        "api_key": "your-token",
        "model_id": "gpt-4o-mini"
    }
}<p>엔드포인트가 생성되었으므로 이제 이를 사용하여 새 카테고리를 만들 준비가 되었습니다. 아래는 문서 데이터 조작 및 프롬프트 생성의 전체 프로세스를 처리하는 파이프라인입니다. 각 프로세서의 기능에 대해 자세히 설명하겠습니다.</p><p>첫 번째 프로세서는 프롬프트 작성을 담당합니다. AI가 주제를 정확하게 분석하고 식별할 수 있도록 지침을 명확하게 자세히 설명하는 것이 매우 중요합니다. 이 프롬프트에서는 영화의 제목, 설명 및 장르 분석을 기반으로 두 가지 주제를 요청합니다.</p>{
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like: 'n1, n2, ...n'. Here is a movie info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }<p>다음 파이프라인은 추론 파이프라인으로, 프롬프트를 수신하여 <strong>generate_topics_ia</strong> 엔드포인트로 전송합니다. 모델에서 생성된 응답은 결과 필드에 저장됩니다.</p>{
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      }<p>다음으로, 제가 만든 임시 필드를 제거하는 것 외에도 응답을 조작하고 주제 필드에 설정하는 데 사용되는 3개의 프로세서가 있습니다.</p><p>이 파이프라인을 실행하면 아래와 같은 결과를 얻을 수 있습니다:</p>{
  "docs": [
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "1",
        "_source": {
          "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
          "model_id": "generate_topics_ia",
          "title": "The Lord of the Rings: The Fellowship of the Ring",
          "genres": [
            "Action",
            "Adventure",
            "Drama"
          ],
          "topics": [
            "Fantasy",
            "Quest"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340010257Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "2",
        "_source": {
          "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
          "model_id": "generate_topics_ia",
          "title": "Interstellar",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "space exploration",
            "human survival"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340413173Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "3",
        "_source": {
          "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
          "model_id": "generate_topics_ia",
          "title": "The Martian",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "survival",
            "ingenuity"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340427965Z"
        }
      }
    }
  ]
}<p>처음에는 같은 장르에 속하는 카테고리도 있지만, 영화의 맥락과 더 관련이 있는 새로운 카테고리가 있다는 점에 유의하세요.</p><p>이제 이 새로운 카테고리를 사용하여 문서와 함께 색인을 생성할 수 있습니다. 이렇게 하면 패싯을 생성할 때 기본 카테고리 외에도 영화의 맥락에 맞는 보다 구체적인 하위 카테고리를 만들 수 있습니다.</p><p>또한 이러한 새로운 카테고리를 벡터화하여 벡터 검색에 사용할 수도 있습니다. 즉, 새로운 카테고리는 필터 역할을 할 뿐만 아니라 검색어와의 의미적 유사성을 계산하는 데도 사용할 수 있어 표시되는 결과의 관련성을 더욱 높일 수 있습니다.</p><p>완전한 파이프라인:</p>POST /_ingest/pipeline/_simulate
{
  "pipeline": {
    "processors": [
      {
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like string: 'n1, n2m ...n'. Here is a movies info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }
      },
      {
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "split": {
          "field": "result",
          "target_field": "topics",
          "separator": ", "
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
  },
  "docs": [
    {
      "_index": "index",
      "_id": "1",
      "_source": {
        "title": "The Lord of the Rings: The Fellowship of the Ring",
        "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
        "genres": [
          "Action",
          "Adventure",
          "Drama"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "2",
      "_source": {
        "title": "Interstellar",
        "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "3",
      "_source": {
        "title": "The Martian",
        "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    }
  ]
}<h2>결론</h2><p>AI를 사용하여 패싯을 개선하면 검색 결과를 더욱 구체적이고 맥락에 맞게 만들어 검색 환경을 변화시킬 수 있습니다. 광범위한 고정 카테고리와 달리 AI가 생성한 카테고리는 문맥을 더 잘 반영할 수 있습니다. 예를 들어, 영화를 재분류할 때 기본 카테고리에서 놓치는 맥락을 포착하여 훨씬 더 관련성 높은 그룹을 제공할 수 있습니다.</p><p>이러한 새로운 카테고리를 인덱스에 추가하면 패싯을 개선할 수 있을 뿐만 아니라 벡터 검색을 활성화할 수도 있습니다. 그 결과, 문맥에 더욱 부합하는 필터를 통해 보다 효율적인 검색 환경을 제공합니다.</p><h2>참고 자료</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2838185214162c8/6a17f8bedbb4ff04affb58a5/25c9f9baa2326b5189ce0b1cc6240475781c755d-721x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Apache Airflow를 통해 Elasticsearch로 데이터를 수집하는 방법]]></title>
    <description><![CDATA[Apache Airflow를 통해 Elasticsearch로 데이터를 수집하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<h2>아파치 에어플로우란 무엇인가요?</h2><p>Apache Airflow는 워크플로우를 생성, 예약 및 모니터링하도록 설계된 플랫폼입니다. ETL 프로세스, 데이터 파이프라인 및 기타 복잡한 워크플로를 오케스트레이션하는 데 사용되며 유연성과 확장성을 제공합니다. 시각적 인터페이스와 실시간 모니터링 기능을 통해 파이프라인을 보다 쉽고 효율적으로 관리할 수 있으며, 실행 진행 상황과 결과를 추적할 수 있습니다. 다음은 네 가지 주요 기둥입니다:</p><ul><li><p><strong>동적: </strong>파이프라인은 파이썬으로 정의되어 동적이고 유연한 워크플로를 생성할 수 있습니다.</p></li><li><p><strong>확장성:</strong> 에어플로우를 다양한 환경과 통합할 수 있고, 사용자 지정 오퍼레이터를 생성할 수 있으며, 필요에 따라 특정 코드를 실행할 수 있습니다.</p></li><li><p><strong>우아함:</strong> 파이프라인은 깔끔하고 명시적인 방식으로 작성됩니다.</p></li><li><p><strong>확장성:</strong> 모듈식 아키텍처는 메시지 대기열을 사용하여 임의의 수의 작업자를 조율합니다.</p></li></ul><p>실제로 Airflow는 다음과 같은 시나리오에서 사용할 수 있습니다:</p><ul><li><p><strong>데이터 가져오기: </strong>Elasticsearch와 같은 데이터베이스로의 일일 데이터 수집을 오케스트레이션하세요.</p></li><li><p><strong>로그 모니터링:</strong> 로그 파일의 수집과 처리를 관리한 다음 Elasticsearch에서 분석하여 오류나 이상 징후를 식별합니다.</p></li><li><p><strong>여러 데이터 소스 통합:</strong> 서로 다른 시스템(API, 데이터베이스, 파일)의 정보를 Elasticsearch의 단일 레이어로 결합하여 검색과 보고를 간소화하세요.</p></li></ul><h2>공기 흐름에서 DAG(방향성 비순환 그래프) 이해하기</h2><p>에어플로우에서 워크플로는 DAG(방향성 비순환 그래프)로 표현됩니다. DAG는 작업이 실행되는 순서를 정의하는 구조입니다. DAG의 주요 특징은 다음과 같습니다:</p><ul><li><p><strong>독립적인 작업별 구성:</strong> 각 작업은 작업의 단위를 나타내며 독립적으로 실행되도록 설계되었습니다.</p></li><li><p><strong>시퀀싱: </strong>작업이 실행되는 순서는 DAG에 명시적으로 정의되어 있습니다.</p></li><li><p><strong>재사용성:</strong> DAG는 반복적으로 실행되도록 설계되어 프로세스 자동화를 용이하게 합니다.</p></li></ul><h2>공기 흐름 구성 요소</h2><p>Airflow 에코시스템은 작업을 조율하기 위해 함께 작동하는 여러 구성 요소로 구성되어 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt25a23489b8725b3e/6a17dfcfe8fbceba103a1846/bacd83aff625026d62f023e0434baa5782a2761a-1046x628.png" alt="공기 흐름 주요 구성 요소" /><ul><li><p><strong>스케줄러:</strong> 스케줄러: DAG를 예약하고 작업자가 실행할 작업을 보내는 역할을 담당합니다.</p></li><li><p><strong>실행자:</strong> 작업 실행을 관리하여 작업자에게 위임합니다.</p></li><li><p><strong>웹 서버:</strong> DAG 및 작업과 상호 작용하기 위한 그래픽 인터페이스를 제공합니다.</p></li><li><p><strong>Dags 폴더:</strong> 파이썬으로 작성된 DAG를 저장하는 폴더입니다.</p></li><li><p><strong>메타데이터:</strong> 스케줄러와 실행자가 실행 상태를 저장하는 데 사용하는 도구의 저장소 역할을 하는 데이터베이스입니다.</p></li></ul><h2>아파치 에어플로우와 Elasticsearch</h2><p>Apache Airflow와 Elasticsearch를 사용하여 Elasticsearch에서 작업과 색인 결과를 오케스트레이션하는 방법을 시연합니다. 이 데모의 목표는 Elasticsearch 인덱스의 레코드를 업데이트하는 작업 파이프라인을 생성하는 것입니다. 이 인덱스에는 사용자가 평점을 매기고 등급을 지정할 수 있는 영화 데이터베이스가 포함되어 있습니다. 매일 수백 개의 등급이 있는 시나리오를 상상해 보면 등급 기록을 계속 업데이트해야 합니다. 이를 위해 새로운 통합 등급을 검색하고 인덱스의 기록을 업데이트하는 DAG가 매일 실행되도록 개발될 것입니다.</p><p>DAG 흐름에는 등급을 가져오는 작업과 결과를 검증하는 작업이 있습니다. 데이터가 존재하지 않으면 DAG는 실패 작업으로 이동합니다. 그렇지 않으면 데이터가 Elasticsearch에서 색인됩니다. 점수 계산을 담당하는 메커니즘이 있는 메서드를 통해 등급을 검색하여 인덱스의 영화 등급 필드를 업데이트하는 것이 목표입니다.</p><h2>Docker와 함께 Apache Airflow 및 Elasticsearch 사용</h2><p>컨테이너화된 환경을 만들기 위해 Docker와 함께 Apache Airflow를 사용하겠습니다. <a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html">"도커에서 에어플로우 실행하기"</a> 가이드의 지침에 따라 에어플로우를 실제로 설정하세요.</p><p>Elasticsearch의 경우 Elastic Cloud의 클러스터를 사용하겠지만, 원하는 경우 Docker로 Elasticsearch를 구성할 수도 있습니다. 이미 영화 카탈로그가 포함된 인덱스가 생성되어 영화 데이터가 색인화되었습니다. 이러한 영화의 '등급' 필드가 업데이트됩니다.</p><h2>DAG 생성</h2><p>Docker를 통해 설치하면 Airflow가 인식할 수 있도록 DAG 파일을 배치해야 하는 dags 폴더를 포함한 폴더 구조가 생성됩니다.</p><p>그 전에 필요한 종속성이 설치되어 있는지 확인해야 합니다. 이 프로젝트의 종속성은 다음과 같습니다:</p>pip install apache-airflow apache-airflow-providers-elasticsearch<p><code>update_ratings_movies.py</code> 파일을 만들고 작업 코딩을 시작합니다.</p><p>이제 필요한 라이브러리를 가져와 보겠습니다:</p>from airflow import DAG
from airflow.operators.python import PythonOperator, BranchPythonOperator
from airflow.providers.elasticsearch.hooks.elasticsearch import ElasticsearchPythonHook<p>연결과 외부 API 사용을 추상화하여 Airflow와 Elasticsearch 클러스터 간의 통합을 간소화하는 구성 요소인 <a href="https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html"><strong>ElasticsearchPythonHook을</strong></a> 사용하겠습니다.</p><p>다음으로, 주요 인수를 지정하여 DAG를 정의합니다:</p><ul><li><p><strong><code>dag_id</code></strong>DAG의 이름입니다.</p></li><li><p><strong><code>start_date</code></strong>DAG가 시작되는 시기입니다.</p></li><li><p><strong><code>schedule</code></strong>: 주기를 정의합니다(이 경우 매일).</p></li><li><p><strong><code>doc_md</code></strong>문서를 가져와 에어플로우 인터페이스에 표시할 수 있습니다.</p></li></ul><h2>작업 정의하기</h2><p>이제 DAG의 작업을 정의해 보겠습니다. 첫 번째 작업은 영화 등급 데이터를 검색하는 작업을 담당합니다. <code>task_id</code> 을 <code>'get_movie_ratings'</code> 으로 설정한 <strong>파이썬 오퍼레이터를</strong> 사용하겠습니다. <code>python_callable</code> 매개변수는 평점 가져오기를 담당하는 함수를 호출합니다.</p>get_ratings_operator = PythonOperator(
   task_id='get_movie_ratings',
   python_callable=get_movie_ratings_task
)<p>다음으로 결과가 유효한지 검증해야 합니다. 이를 위해 <strong>BranchPythonOperator와</strong> 함께 조건문을 사용하겠습니다. <code>task_id</code> 은 <code>'validate_result'</code> 이 되고 <code>python_callable</code> 은 유효성 검사 함수를 호출합니다. <code>op_args</code> 매개 변수는 이전 작업의 결과인 <code>'get_movie_ratings'</code> 을 유효성 검사 함수에 전달하는 데 사용됩니다.</p>validate_result = BranchPythonOperator(
   task_id='validate_result',
   python_callable=validate_result,
   op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
)<p>유효성 검사가 성공하면 <code>'get_movie_ratings'</code> 작업에서 데이터를 가져와서 Elasticsearch로 색인합니다. 이를 위해 새 작업인 <code>'index_movie_ratings'</code> 을 생성하여 <strong>PythonOperator를</strong> 사용합니다. <code>op_args</code> 매개변수는 <code>'get_movie_ratings'</code> 작업의 결과를 인덱싱 함수에 전달합니다.</p>index_ratings_operator = PythonOperator(
   task_id='index_movie_ratings',
   python_callable=index_movie_ratings_task,
   op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
)<p>유효성 검사 결과 실패로 표시되면 DAG는 실패 알림 작업으로 진행합니다. 이 예에서는 단순히 메시지를 인쇄하지만 실제 시나리오에서는 실패에 대해 알리도록 알림을 구성할 수 있습니다.</p>failed_get_rating_operator = PythonOperator(
   task_id='failed_get_rating_operator',
   python_callable=lambda: print('Ratings were False, skipping indexing.')
)<p>마지막으로 작업 종속성을 정의하여 올바른 순서로 실행되도록 합니다:</p>get_ratings_operator &gt;&gt; validate_result &gt;&gt; [index_ratings_operator, failed_get_rating_operator]<p>이제 DAG의 전체 코드를 따르세요:</p>"""
DAG update Rating Movies
"""
import ast
import random

from airflow import DAG
from datetime import datetime

from airflow.operators.python import PythonOperator, BranchPythonOperator
from airflow.providers.elasticsearch.hooks.elasticsearch import ElasticsearchPythonHook


def index_movie_ratings_task(movies):
   es_hook = ElasticsearchPythonHook(hosts=None,
                                     es_conn_args={
                                         "cloud_id": "cloud_id"
                                         "api_key": "api-key"
                                     })
   es_client = es_hook.get_conn
   actions = []
   for movie in ast.literal_eval(movies):
       actions.append(
           {
               "update": {
                   "_id": movie["id"],
                   "_index": "movies"
               }
           }
       )
       actions.append(
           {
               "doc": {
                   "rating": movie["rating"]
               },
               "doc_as_upsert": True
           }
       )
   result = es_client.bulk(operations=actions)
   print(f"Ingestion completed.")
   print(result)
   return True


def get_movie_ratings_task():
   movies = [
       {"id": i, "rating": round(random.uniform(1, 10), 1)}
       for i in range(1, 100)
   ]
   return movies

def validate_result(result):
   if not result:
       return 'failed_get_rating_operator'
   else:
       return 'index_movie_ratings'


with DAG(
       dag_id="update_ratings_movies_2024",
       start_date=datetime(2024, 12, 29),
       schedule="@daily",
       doc_md=__doc__,
):
   get_ratings_operator = PythonOperator(
       task_id='get_movie_ratings',
       python_callable=get_movie_ratings_task
   )

   validate_result = BranchPythonOperator(
       task_id='validate_result',
       python_callable=validate_result,
       op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"],
       provide_context=True
   )

   index_ratings_operator = PythonOperator(
       task_id='index_movie_ratings',
       python_callable=index_movie_ratings_task,
       op_args=["{{ task_instance.xcom_pull(task_ids='get_movie_ratings') }}"]
   )

   failed_get_rating_operator = PythonOperator(
       task_id='failed_get_rating_operator',
       python_callable=lambda: print('Ratings were False, skipping indexing.')
   )

get_ratings_operator &gt;&gt; validate_result &gt;&gt; [index_ratings_operator, failed_get_rating_operator]<h2>DAG 실행 시각화</h2><p>Apache Airflow 인터페이스에서 DAG의 실행을 시각화할 수 있습니다. "DAG" 탭으로 이동하여 생성한 DAG를 찾으면 됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9c5de210a5a264ad/6a17dfd00b0bed0290dd34cf/905b9191c4e3191e8b5608174d4c555370bf25eb-1600x760.png" alt="Elasticsearch를 사용하여 Apache Airflow 인터페이스에서 DAG 실행 시각화하기" /><p>아래에서 작업의 실행과 각 상태를 시각화하여 확인할 수 있습니다. 특정 날짜의 실행을 선택하면 각 작업의 로그에 액세스할 수 있습니다. <strong><code>index_movie_ratings</code></strong> 작업에서 인덱스에서 인덱싱 결과를 확인할 수 있으며, 성공적으로 완료되었음을 알 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0beddf311a808d24/6a17dfd2033c8d76de6bb0ba/73c3f738d27500cf153377bedf1aa2b67a94a8c8-1600x648.png" alt="Elasticsearch를 사용하여 Apache Airflow에서 작업 실행과 그 상태를 시각화합니다." /><p>다른 탭에서는 작업 및 DAG에 대한 추가 정보에 액세스하여 잠재적인 문제를 분석하고 해결하는 데 도움을 받을 수 있습니다.</p><h2>결론</h2><p>이 문서에서는 Apache Airflow와 Elasticsearch를 통합하여 데이터 수집 솔루션을 만드는 방법을 보여드렸습니다. DAG를 구성하고, 동영상 데이터 검색, 유효성 검사, 인덱싱을 담당하는 작업을 정의하고, Airflow 인터페이스에서 이러한 작업의 실행을 모니터링하고 시각화하는 방법을 보여드렸습니다.</p><p>이 접근 방식은 다양한 유형의 데이터와 워크플로에 쉽게 적용할 수 있으므로 Airflow는 다양한 시나리오에서 데이터 파이프라인을 오케스트레이션하는 데 유용한 도구입니다.</p><h2>참고 자료</h2><p>Apache AirFlow</p><p><a href="https://airflow.apache.org/">https://airflow.apache.org/</a></p><p>Docker로 Apache Airflow 설치</p><p><a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html">https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html</a></p><p>Elasticsearch 파이썬 훅</p><p><a href="https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html">https://airflow.apache.org/docs/apache-airflow-providers-elasticsearch/stable/hooks/elasticsearch_python_hook.html</a></p><p>파이썬 연산자</p><p><a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/operator/python.html">https://airflow.apache.org/docs/apache-airflow/stable/howto/operator/python.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/apache-airflow-elasticsearch-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/apache-airflow-elasticsearch-ingest-data</guid>
    <category><![CDATA[인덱스 데이터]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28475ace97d1d989/6a1704c9286714d6dd93e1ec/5d4b47ac5d2ba453fc19dcc15efa2aed5f55d88b-1440x1355.png" length="0" type="image/png"/>
    <pubDate>Fri, 17 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Kafka를 통해 Elasticsearch로 데이터를 수집하는 방법]]></title>
    <description><![CDATA[Python, Docker Compose, Kafka Connect를 사용해 효율적인 데이터 수집, 색인, 시각화를 위해 Apache Kafka와 Elasticsearch를 통합하는 단계별 안내서입니다.]]></description>
    <content:encoded><![CDATA[<p>이 문서에서는 데이터 수집 및 색인을 위해 Apache Kafka와 Elasticsearch를 통합하는 방법을 보여드립니다. Kafka의 개요와 생산자 및 소비자 개념에 대해 설명하고, Apache Kafka를 통해 메시지를 수신하고 색인할 로그 인덱스를 생성합니다. 이 프로젝트는 Python으로 구현되었으며, 코드는 <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-through-apache-kafka">GitHub에서</a> 확인할 수 있습니다.</p><h3><strong>필수 구성 요소</strong></h3><ul><li><p>도커 및 도커 컴포즈: 컴퓨터에 도커 및 도커 컴포즈가 설치되어 있는지 확인합니다.</p></li><li><p>Python 3.x: 생산자 및 소비자 스크립트를 실행합니다.</p></li></ul><h3><strong>아파치 카프카 소개</strong></h3><p>Apache Kafka는 높은 확장성과 가용성, 내결함성을 지원하는 분산형 스트리밍 플랫폼입니다. Kafka에서는 주요 구성 요소를 통해 데이터 관리가 이루어집니다:</p><ul><li><p><strong>브로커</strong>: 생산자와 소비자 간의 메시지 저장 및 배포를 담당합니다.</p></li><li><p><strong>주키퍼</strong>: 클러스터의 상태, 파티션 리더, 소비자 정보를 제어하여 카프카 브로커를 관리하고 조정합니다.</p></li><li><p><strong>주제</strong>: 데이터가 게시되고 소비를 위해 저장되는 채널입니다.</p></li><li><p><strong>소비자와 생산자</strong>: 생산자가 토픽에 데이터를 전송하는 동안 소비자는 해당 데이터를 검색합니다.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4aae32304d7417f6/6a17f7be577262b47c1bcdac/89a37243baec48bbdfa85e3298fc91082322ed4e-1600x868.png" alt="다이어그램 아파치 카프카" /><p>이러한 구성 요소는 함께 작동하여 데이터 스트리밍을 위한 강력한 프레임워크를 제공하는 카프카 생태계를 형성합니다.</p><h3><strong>프로젝트 구조</strong></h3><p>데이터 수집 프로세스를 이해하기 위해 데이터 수집 프로세스를 여러 단계로 나누었습니다:</p><ul><li><p><strong>인프라 프로비저닝</strong>: Kafka, Elasticsearch, Kibana를 지원하기 위한 Docker 환경 설정.</p></li><li><p><strong>프로듀서 만들기</strong>: 로그 주제로 데이터를 전송하는 Kafka 프로듀서 구현하기.</p></li><li><p><strong>소비자 생성</strong>: Elasticsearch에서 메시지를 읽고 색인하기 위한 Kafka 소비자 개발.</p></li><li><p><strong>수집 유효성</strong> 검사: 전송 및 소비된 데이터를 확인하고 유효성을 검사합니다.</p></li></ul><h3><strong>Docker Compose를 사용한 인프라 구성</strong></h3><p>필요한 서비스를 구성하고 관리하기 위해 Docker Compose를 활용했습니다. 아래에는 Apache Kafka, Elasticsearch, Kibana의 통합에 필요한 각 서비스를 설정하여 데이터 수집 프로세스를 보장하는 Docker Compose 코드가 나와 있습니다.</p>version: "3"

services:

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka:latest
    container_name: kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
      - "9094:9094"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST:${HOST_IP}:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.1
    container_name: elasticsearch-8.15.1
    environment:
      - node.name=elasticsearch
      - xpack.security.enabled=false
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - ./elasticsearch:/usr/share/elasticsearch/data
    ports:
      - 9200:9200

  kibana:
    image: docker.elastic.co/kibana/kibana:8.15.1
    container_name: kibana-8.15.1
    ports:
      - 5601:5601
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
      ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'<p>Elasticsearch Labs <a href="https://github.com/andreluiz1987/elasticsearch-labs/tree/supporting-blog/elasticsearch-apache-kafka/supporting-blog-content/elasticsearch-through-apache-kafka">GitHub</a> 리포지토리에서 직접 파일에 액세스할 수 있습니다.</p><h3><strong>카프카 프로듀서를 사용한 데이터 전송</strong></h3><p>프로듀서는 로그 주제에 메시지를 보낼 책임이 있습니다. 메시지를 일괄적으로 전송함으로써 네트워크 사용 효율을 높이고 일괄 전송의 양과 지연 시간을 각각 제어하는 <code>batch_size</code> 및 <code>linger_ms</code> 설정으로 최적화할 수 있습니다. <code>acks='all'</code> 구성은 중요한 로그 데이터에 필수적인 메시지를 영구적으로 저장할 수 있도록 합니다.</p>producer = KafkaProducer(
   bootstrap_servers=['localhost:9092'],  # Specifies the Kafka server to connect
   value_serializer=lambda x: json.dumps(x).encode('utf-8'),  # Serializes data as JSON and encodes it to UTF-8 before sending
   batch_size=16384,     # Sets the maximum batch size in bytes (here, 16 KB) for buffered messages before sending
   linger_ms=10,         # Sets the maximum delay (in milliseconds) before sending the batch
   acks='all'            # Specifies acknowledgment level; 'all' ensures message durability by waiting for all replicas to acknowledge
)


def generate_log_message():
   levels = ["INFO", "WARNING", "ERROR", "DEBUG"]
   messages = [
       "User login successful",
       "User login failed",
       "Database connection established",
       "Database connection failed",
       "Service started",
       "Service stopped",
       "Payment processed",
       "Payment failed"
   ]
   log_entry = {
       "level": random.choice(levels),
       "message": random.choice(messages),
       "timestamp": time.time()
   }
   return log_entry

def send_log_batches(topic, num_batches=5, batch_size=10):
   for i in range(num_batches):
       logger.info(f"Sending batch {i + 1}/{num_batches}")
       for  in range(batch_size):
           log_message = generate_log_message()
           producer.send(topic, value=log_message)
       producer.flush()


if __name__ == "__main__":
   topic = "logs"
   send_log_batches(topic)
   producer.close()<p>프로듀서를 시작할 때 아래와 같이 메시지가 토픽에 일괄적으로 전송됩니다:</p>INFO:kafka.conn:Set configuration …
INFO:log_producer:Sending batch 1/5 
INFO:log_producer:Sending batch 2/5
INFO:log_producer:Sending batch 3/5
INFO:log_producer:Sending batch 4/5<h3><strong>Kafka Consumer를 통한 데이터 소비 및 색인화</strong></h3><p>소비자는 메시지를 효율적으로 처리하도록 설계되어 로그 주제에서 배치를 소비하고 Elasticsearch로 색인합니다. <code>auto_offset_reset='latest'</code> 을 사용하면 소비자가 이전 메시지를 무시하고 가장 최근 메시지부터 처리를 시작하고 <code>max_poll_records=10</code> 은 일괄 처리를 10개의 메시지로 제한합니다. <code>fetch_max_wait_ms=2000</code> 을 사용하면 소비자는 배치 처리 전에 충분한 메시지가 누적될 때까지 최대 2초 동안 기다립니다.</p><p>메인 루프에서 소비자는 로그 메시지를 소비하고, 처리하고, 각 배치를 Elasticsearch로 색인하여 지속적인 데이터 수집을 보장합니다.</p>consumer = KafkaConsumer(
   'logs',                               
   bootstrap_servers=['localhost:9092'],
   auto_offset_reset='latest',            # Ensures reading from the latest offset if the group has no offset stored
   enable_auto_commit=True,               # Automatically commits the offset after processing
   group_id='log_consumer_group',         # Specifies the consumer group to manage offset tracking
   max_poll_records=10,                   # Maximum number of messages per batch
   fetch_max_wait_ms=2000                 # Maximum wait time to form a batch (in ms)
)

def create_bulk_actions(logs):
   for log in logs:
       yield {
           "_index": "logs",
           "_source": {
               'level': log['level'],
               'message': log['message'],
               'timestamp': log['timestamp']
           }
       }

if __name__ == "__main__":
   try:
       print("Starting message processing…")
       while True:

           messages = consumer.poll(timeout_ms=1000)  # Poll receive messages

           # process each batch messages
           for _, records in messages.items():
               logs = [json.loads(record.value) for record in records]
               bulk_actions = create_bulk_actions(logs)
               response = helpers.bulk(es, bulk_actions)
               print(f"Indexed {response[0]} logs.")
   except Exception as e:
       print(f"Erro: {e}")
   finally:
       consumer.close()
       print(f"Finish")<h3><strong>Kibana에서 데이터 시각화하기</strong></h3><p>Kibana를 사용하면 Kafka에서 수집되어 Elasticsearch에서 색인된 데이터를 탐색하고 검증할 수 있습니다. Kibana의 <strong>개발 도구에</strong> 액세스하여 색인된 메시지를 보고 데이터가 예상대로인지 확인할 수 있습니다. 예를 들어, 카프카 프로듀서가 각각 10개의 메시지를 5개의 배치로 전송했다면 인덱스에 총 50개의 레코드가 표시되어야 합니다.</p><p>데이터를 확인하려면 <strong>개발 도구</strong> 섹션에서 다음 쿼리를 사용할 수 있습니다:</p>GET /logs/_search
{
  "query": {
    "match_all": {}
  }
}<p>대응:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc15fb278fe6f984e/6a17f7c0b1e1131ac279f404/f44f95fc27bba50991412d5c7e7728519b9bdec4-688x1024.png" alt="응답 데이터 확인 - Kafka &amp; Elasticsearch​" /><p>또한, Kibana는 분석을 보다 직관적이고 대화형으로 만드는 데 도움이 되는 시각화 및 대시보드를 생성하는 기능을 제공합니다. 아래에서 처리된 정보에 대한 이해를 높이기 위해 다양한 형식으로 데이터를 보여주는 대시보드와 시각화의 몇 가지 예를 볼 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltabc2856c9feedc47/6a17f7c13e9e4522bfba1651/a18e0ebb543e929136d4786651bc6cee32fa69bc-1600x470.png" alt="Kibana 시각화 - Kafka &amp; Elasticsearch​" /><h3><strong>Kafka Connect를 통한 데이터 수집</strong></h3><p>Kafka Connect는 데이터베이스나 파일 시스템과 같은 데이터 소스와 대상(싱크) 간의 통합을 용이하게 하도록 설계된 서비스입니다. 데이터 이동을 자동으로 처리하는 사전 정의된 커넥터로 작동합니다. 저희의 경우, Elasticsearch는 데이터 싱크 역할을 합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53e3acaf62e7dbed/6a17f7c3577262c5151bcdb0/52a6982c864fdc04cb7a8a5fb02e67dca0ba8226-1600x819.png" alt="Kafka Connect를 통한 데이터 수집" /><p>Kafka Connect를 사용하면 데이터 수집 프로세스를 간소화할 수 있으므로 데이터 수집 워크플로우를 Elasticsearch에 수동으로 구현할 필요가 없습니다. 적절한 커넥터를 사용하면 최소한의 설정과 추가 코딩 없이도 Kafka Connect를 통해 Kafka 토픽으로 전송된 데이터를 Elasticsearch에서 직접 색인할 수 있습니다.</p><h4><strong>카프카 커넥트로 작업하기</strong></h4><p>Kafka Connect를 구현하기 위해 Docker Compose 설정에<a href="https://github.com/andreluiz1987/es-apache-kafka/blob/main/docker-compose.yml#L31"> kafka-connect 서비스를 </a>추가합니다. 이 구성의 핵심은 데이터 인덱싱을 처리할 Elasticsearch 커넥터를 설치하는 것입니다.</p><p>서비스를 구성하고 Kafka Connect 컨테이너를 생성한 후에는 Elasticsearch 커넥터를 위한 구성 파일이 필요합니다. 이 파일에는 다음과 같은 필수 매개변수가 정의되어 있습니다:</p><ul><li><p><code>connection.url</code>: Elasticsearch용 연결 URL입니다.</p></li><li><p><code>topics</code>: 커넥터가 모니터링할 카프카 토픽(이 경우 "로그").</p></li><li><p><code>type.name</code>: Elasticsearch의 문서 유형(일반적으로 _doc).</p></li><li><p><code>value.converter</code>: Kafka 메시지를 JSON 형식으로 변환합니다.</p></li><li><p><code>value.converter.schemas.enable</code>: 스키마를 포함할지 여부를 지정합니다.</p></li><li><p><code>schema.ignore</code> 및 <code>key.ignore</code>: 인덱싱 중 Kafka 스키마 및 키를 무시하도록 설정합니다.</p></li></ul><p>아래는 <code>curl</code> 명령어로 Kafka Connect에서 Elasticsearch 커넥터를 생성하는 방법입니다:</p>curl --location '{{url}}/connectors' \
--header 'Content-Type: application/json' \
--data '{
    "name": "elasticsearch-sink-connector",
    "config": {
        "connector.class": "io.confluent.connect.elasticsearch.ElasticsearchSinkConnector",
        "topics": "logs",
        "connection.url": "http://elasticsearch:9200",
        "type.name": "_doc",
        "value.converter": "org.apache.kafka.connect.json.JsonConverter",
        "value.converter.schemas.enable": "false",
        "schema.ignore": "true",
        "key.ignore": "true"
    }
}'<p>이 구성을 사용하면 Kafka Connect는 "logs" 항목으로 전송된 데이터를 자동으로 수집하고 Elasticsearch에서 색인하기 시작합니다. 이 접근 방식을 사용하면 추가 코딩 없이도 완전히 자동화된 데이터 수집 및 인덱싱이 가능하므로 전체 통합 프로세스를 간소화할 수 있습니다.</p><h3><strong>결론</strong></h3><p>Kafka와 Elasticsearch를 통합하면 실시간 데이터 수집과 분석을 위한 강력한 파이프라인이 만들어집니다. 이 가이드는 향후 더 복잡한 요구 사항에 적응할 수 있도록 Kibana에서 원활한 시각화 및 분석을 통해 강력한 데이터 수집 아키텍처를 구축하기 위한 기초적인 접근 방식을 제공합니다.</p><p>또한, Kafka Connect를 사용하면 데이터를 처리하고 색인하기 위한 추가 코드가 필요 없기 때문에 Kafka와 Elasticsearch 간의 통합이 훨씬 더 간소화됩니다. Kafka Connect를 사용하면 최소한의 구성으로 특정 토픽으로 전송된 데이터를 Elasticsearch에서 자동으로 색인할 수 있습니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-apache-kafka-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-apache-kafka-ingest-data</guid>
    <category><![CDATA[인덱스 데이터]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53e3acaf62e7dbed/6a17f7c3577262c5151bcdb0/52a6982c864fdc04cb7a8a5fb02e67dca0ba8226-1600x819.png" length="0" type="image/png"/>
    <pubDate>Tue, 24 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[이커머스 제품 카탈로그에 하이브리드 검색을 사용하는 방법]]></title>
    <description><![CDATA[패싯, 프로모션, 개인화 및 행동 분석을 사용하여 하이브리드 검색을 사용하여 이커머스 제품 카탈로그를 구축하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>이 문서에서는 전체 텍스트 검색 결과와 벡터 검색 결과를 결합하는 하이브리드 검색을 구현하는 방법을 설명합니다. 하이브리드 검색은 이 두 가지 접근 방식을 통합함으로써 두 가지 검색 전략의 장점을 모두 활용하여 검색 결과의 폭을 개선합니다.</p><p>하이브리드 검색을 통합하는 것 외에도 검색 솔루션을 더욱 강력하게 만드는 기능을 추가하는 방법을 보여드리겠습니다. 여기에는 패싯 및 개인화된 제품 프로모션이 포함됩니다. 또한 Elastic의 행동 분석 도구를 사용하여 사용자 상호 작용을 캡처하고 귀중한 인사이트를 생성하는 방법도 보여드립니다.</p><p>이 구현에서는 사용자가 검색 결과를 보고 상호 작용할 수 있는 인터페이스와 정보 반환을 담당하는 API를 모두 구축하는 방법을 살펴봅니다. 소스 코드가 있는 리포지토리에 액세스하려면 아래 링크를 참조하세요:</p><ul><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search</a></p></li><li><p><a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store</a> </p></li></ul><p>이 가이드는 인덱스 생성부터 패싯 및 결과 개인화와 같은 고급 기능 구현에 이르기까지 여러 단계로 나누어 설명합니다. 마지막에는 이커머스 시나리오에서 사용할 수 있는 강력한 검색 솔루션이 준비됩니다.</p><h2>이커머스 하이브리드 검색을 위한 환경 설정</h2><p>구현을 시작하기 전에 환경을 설정해야 합니다. Elastic Cloud의 서비스 또는 컨테이너화된 솔루션을 사용하여 Elasticsearch를 관리하도록 선택할 수 있습니다. 컨테이너화를 선택하는 경우 Docker Compose를 통한 구성은 이 리포지토리에서 찾을 수 있습니다: <a href="https://github.com/andreluiz1987/product-store-search/blob/main/docker/docker-compose.yml">docker-compose.yml</a>.</p><h2>인덱스 생성 및 제품 카탈로그 수집</h2><p>색인은 이름, 설명, 사진, 카테고리, 태그 등의 필드가 포함된 화장품 카탈로그를 기반으로 만들어집니다. "name", "description," 등 전체 텍스트 검색에 사용되는 필드는 <code>text</code> 로 매핑되고, "category", "brand," 등 집계에 사용되는 필드는 <code>keyword</code> 로 매핑되어 패싯을 사용할 수 있습니다.</p><p>"설명" 필드는 제품에 대한 자세한 컨텍스트를 제공하므로 벡터 검색에 사용됩니다. 이 필드는 설명의 벡터 표현을 저장하는 <code>dense_vector,</code> 으로 정의됩니다.</p><p>인덱스 매핑은 다음과 같습니다:</p>{
   "mappings":{
      "properties":{
         "id":{
            "type":"keyword"
         },
         "brand":{
            "type":"text",
            "fields":{
               "keyword":{
                  "type":"keyword"
               }
            }
         },
         "name":{
            "type":"text"
         },
         "price":{
            "type":"float"
         },
         "price_sign":{
            "type":"keyword"
         },
         "currency":{
            "type":"keyword"
         },
         "image_link":{
            "type":"keyword"
         },
         "description":{
            "type":"text"
         },
         "description_embeddings":{
            "type":"dense_vector",
            "dims":384
         },
         "rating":{
            "type":"keyword"
         },
         "category":{
            "type":"keyword"
         },
         "product_type":{
            "type":"keyword"
         },
         "tag_list":{
            "type":"keyword"
         }
      }
   }
}<p>색인 생성을 위한 스크립트는 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/infra/create_index.py">여기에서</a> 확인할 수 있습니다.</p><h2>임베딩 생성</h2><p>제품 설명을 벡터화하기 위해 모든 미니LM-L6-v2 모델을 사용합니다. 이 경우 애플리케이션은 인덱싱하기 전에 임베딩을 생성할 책임이 있습니다. 또 다른 옵션은 모델을 Elasticsearch 클러스터로 가져오는 것이지만, 이 로컬 환경에서는 애플리케이션 내에서 직접 벡터화를 수행하는 방법을 선택했습니다.</p><p><a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">Kaggle에서</a> 제공되는 화장품 데이터 세트를 사용하여 인덱스를 채우고, 데이터 수집의 효율성을 높이기 위해 일괄 처리를 사용했습니다. 동일한 수집 단계에서 "description" 필드에 대한 임베딩을 생성하고 새 필드 "description_embeddings" 로 인덱싱합니다.</p><p>전체 데이터 수집 프로세스는 리포지토리에서 제공되는 <strong>Jupyter Notebook을</strong> 통해 직접 추적하고 실행할 수 있습니다. 이 노트북은 데이터를 읽고, 처리하고, 색인하는 방법에 대한 단계별 가이드를 제공하여 쉽게 복제하고 실험할 수 있도록 해줍니다.</p><p>다음 링크에서 노트북에 액세스할 수 있습니다: <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/ingestion/ingestion.ipynb">수집 노트북.</a></p><h2>하이브리드 검색 구현</h2><p>이제 하이브리드 검색을 구현해 보겠습니다. 키워드 기반 검색의 경우 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html">다중 일치</a> 쿼리를 사용하여 "name," " category," 및 "description 필드를 타겟팅합니다." 이렇게 하면 이러한 필드에 검색어가 포함된 문서가 검색됩니다.</p><p>벡터 검색의 경우 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">KNN 쿼리를</a> 사용합니다. 쿼리를 실행하기 전에 검색어를 벡터화해야 하며, 이는 입력어를 벡터화하는 방법을 사용하여 수행됩니다. 수집 시 사용된 것과 동일한 모델이 검색어에도 사용된다는 점에 유의하세요.</p><p>두 검색의 조합은 두 쿼리의 결과를 병합하고 노이즈를 줄여 검색 정확도를 높이는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">상호 순위 융합(RRF) </a>알고리즘을 사용하여 이루어집니다. RRF를 사용하면 키워드 기반 검색과 벡터 검색이 모두 함께 작동하여 사용자의 쿼리에 대한 이해도를 높일 수 있습니다.</p>query = {
   "retriever": {
       "rrf": {
           "retrievers": [
               {
                   "standard": {
                       "query": organic_query['query']
                   }
               },
               {
                   "knn": {
                       "field": "description_embeddings",
                       "query_vector": vector,
                       "k": 5,
                       "num_candidates": 20
                   }
               }
           ],
           "rank_window_size": 20,
           "rank_constant": 5
       }
   },
   "_source": organic_query['_source']
}<h3>결과 비교: 키워드 검색 대 하이브리드 검색</h3><p>이제 기존 키워드 검색과 하이브리드 검색의 결과를 비교해 보겠습니다. 키워드 검색을 사용하여 "건성 피부용 파운데이션" 을 검색하면 다음과 같은 결과가 표시됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb5405df787bc8f5/6a17023e961e697ca1c4cdb4/52e717aa3c9c1fadfadb639f5fb77cf8e47e3b34-1600x1021.png" alt="결과 비교: 키워드 검색과 하이브리드 검색" /><ol><li><p><strong>중성/건성 피부를 위한 레브론 컬러스테이 메이크업설명</strong>: 레브론 컬러스테이 메이크업은 케이크, 퇴색 또는 문지르지 않는 가벼운 포뮬러로 오래 지속되는 커버력을 제공합니다. 타임 릴리스 \n기술이 적용된 이 오일 프리, 수분 밸런스 포뮬러는 특히 \n중성 또는 건성 피부에 지속적으로 수분을 공급하도록 제조되었습니다.\n특징: 최대 24시간 동안 메이크업이 편안하게 지속됨\n중간 커버부터 풀 커버까지 가능\n다양하고 아름다운 색조로 제공됨
</p></li><li><p><strong>메이블린 드림 스무스 무스 파운데이션설명</strong>: 좋아하는 이유독특한 크림 휘핑 파운데이션이 100% 아기처럼 부드러운 완벽함을 선사합니다.\n\n피부 14시간 동안 촉촉함이 유지되며 거칠거나 건조하지 않습니다\n경량 포뮬러가 완벽한 보습 커버력을 제공합니다\n모공에 매끄럽게 밀착되어 하루 종일 산뜻합니다\n무오일, 무향, 피부과 테스트, 알레르기 테스트, 논코메도제닉 \u2013원\u2019t 모공 막힘\n안전합니다. 민감한 피부를 위한 제품입니다.</p></li></ol><p><strong>분석</strong>: 건성 피부용 파운데이션( ")을 검색할 때" 검색어 키워드와 제품 제목 및 설명이 정확히 일치하는 결과를 얻었습니다. 하지만 이 매치가 항상 최선의 선택을 반영하는 것은 아닙니다. 예를 들어, 건성 피부를 위해 특별히 고안된 <strong>레브론 컬러스테이 메이크업 포 노멀/건성 피부용은</strong> 좋은 선택입니다. 오일 프리 제품이지만 지속적으로 수분을 공급할 수 있도록 포뮬러가 설계되었습니다. 반면 <strong>메이블린 드림 스무스 무스 파운데이션은</strong> 오일 프리이면서 수분 공급을 언급하고 있지만, 일반적으로 오일 프리 제품은 건성 피부에 필요한 추가 수분을 공급하기보다는 유분 조절에 중점을 두는 경향이 있기 때문에 지성 또는 복합성 피부에 더 권장되는 제품입니다. 이는 건성 피부를 가진 사람들의 특정 요구 사항을 완전히 충족하지 못하는 제품을 표시할 수 있는 키워드 기반 검색의 한계를 강조합니다.</p><p>이제 하이브리드 접근 방식을 사용하여 동일한 검색을 수행합니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt412e38b327cf6a4f/6a17024066c4f94c52f8beb9/13b562a5120619355ba0861976102e208964a49f-1600x1021.png" alt="하이브리드 접근 방식을 사용하여 검색 수행" /><ol><li><p><strong>커버걸 아웃라스트 스테이 루미너스 파운데이션 크리미 내추럴 (820):설명</strong>: 커버걸 아웃라스트 스테이 루미너스 파운데이션은 촉촉한 마무리와 은은한 광채를 연출하기에 완벽한 제품입니다. 기름기가 없는 포뮬러로 하루 종일 지속되는 자연스러운 광채를 피부에 선사하는 오일 프리 제품입니다! 이 올데이 파운데이션은 피부에 수분을 공급하고 결점을 완벽하게 커버합니다.

<strong>분석: </strong>이 제품은 건성 피부 사용자에게 중요한 수분 공급을 강조하는 제품이기 때문에 적절하게 어울립니다. "촉촉한 피부" 및 "보송한 마무리" 라는 용어는 건성 피부를 위한 파운데이션을 찾는 사용자의 의도와 일치합니다. 벡터 검색은 수분 공급의 개념을 이해하고 이를 건성 피부를 위한 파운데이션의 필요성과 연결시켰을 가능성이 높습니다.
</p></li><li><p><strong>중성/건성 피부용 레브론 컬러스테이 메이크업:설명:</strong> 레브론 컬러스테이 메이크업은 케이크, 퇴색 또는 문지르지 않는 가벼운 포뮬러로 오래 지속되는 커버력을 제공합니다. 타임 릴리스 기술이 적용된 이 오일 프리 수분 밸런스 포뮬러는 특히 중성 또는 건성 피부를 위해 만들어져 지속적으로 수분을 공급합니다.

<strong>분석: </strong>이 제품은 건성 피부 사용자의 요구를 직접적으로 해결하며, 중성 또는 건성 피부용으로 만들어졌다고 명시적으로 언급하고 있습니다. "수분 밸런스 포뮬러(" )와 지속적인 수분 공급은 건성 피부에 적합한 파운데이션을 찾는 분들에게 적합합니다. 벡터 검색은 키워드 매칭뿐만 아니라 수분 공급에 초점을 맞추고 건성 피부를 타겟 고객층으로 구체적으로 언급했기 때문에 이 결과를 성공적으로 검색할 수 있었습니다.
</p></li><li><p><strong>세럼 파운데이션설명: </strong>세럼 파운데이션은 21가지의 다양한 쉐이드로 제공되는 가벼운 미디엄 커버리지 포뮬러입니다. 이 파운데이션은 매우 가벼운 세럼 느낌으로 자연스러워 보이는 적당한 커버력을 제공합니다. 점도가 매우 낮으며 제공된 펌프를 사용하거나 원하는 경우 별도로 구매할 수 있는 유리 드롭퍼(옵션)를 사용하여 추출할 수 있습니다.

<strong>분석:</strong> 이 경우 설명은 자연스러운 느낌의 가벼운 세럼 파운데이션을 강조하고 있는데, 이는 건성 피부를 가진 사람들이 종종 부드럽고 촉촉하며 케이크처럼 들뜨지 않는 마무리감을 제공하는 제품을 찾기 때문에 이들의 니즈에 부합하는 것입니다. 벡터 검색은 ' "건성 피부" '라는 용어가 명시적으로 언급되지 않았음에도 불구하고 가볍고 자연스러운 커버력과 세럼과 같은 텍스처, 수분 유지 및 편안한 사용감과 관련된 광범위한 맥락에서 건성 피부와 관련이 있는 것으로 판단한 것으로 보입니다.</p></li></ol><h2>패싯 구현</h2><p>패싯은 검색 결과를 효율적으로 구체화하고 필터링하는 데 필수적이며, 특히 전자상거래와 같이 다양한 제품이 있는 시나리오에서 사용자에게 보다 집중된 탐색 기능을 제공합니다. 카테고리, 브랜드, 가격 등의 속성에 따라 검색 결과를 조정할 수 있어 검색 정확도를 높일 수 있습니다. 이 기능을 구현하기 위해 인덱스 생성 단계에서 <code>keyword</code> 로 정의된 <code>category</code> 및 <code>brand</code> 필드에 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html">용어 집계를</a> 사용합니다.</p>    query = build_query(term, categories, product_types, brands)
    query["aggs"] = {
        "product_types": {"terms": {"field": "product_type"}},
        "categories": {"terms": {"field": "category"}},
        "brands": {"terms": {"field": "brand.keyword"}}
    }<p>구현을 위한 전체 코드는 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L144">여기에서</a> 확인할 수 있습니다.</p><p>건성 피부용 파운데이션 "검색의 패싯 결과" 를 아래에서 확인하세요:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31080652a60d8600/6a1702416234e0af7ddb18e7/e8907af359c021eaa38ec7a6a53f10c325ee8ade-1146x1248.png" alt="건성 피부용 파운데이션 &quot;검색의 패싯 결과&quot;" /><h2>결과 사용자 지정: 고정된 쿼리</h2><p>경우에 따라 검색 결과에서 특정 제품을 홍보하는 것이 유리할 수 있습니다. 이를 위해 특정 제품을 결과 상단에 표시할 수 있는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html"><strong>고정 쿼리를</strong></a> 사용합니다. 아래에서는 제품을 홍보하지 않고 "재단" 이라는 용어를 검색합니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31e75c815262993e/6a170243509168ae42e1b95d/9396c4ed358e7a68eed47f17dd6915d0bad492aa-1600x1157.png" alt="제품 홍보 없이 &quot;재단&quot; 이라는 용어를 검색하세요." /><p>이 예제에서는 "글루텐 프리 태그가 있는 제품을 홍보할 수 있습니다." 제품 ID를 사용하면 검색 결과에서 우선순위를 지정할 수 있습니다. 구체적으로 다음 제품을 홍보할 예정입니다: <strong>세럼 파운데이션</strong> (ID: 1043), <strong>커버 파운데이션</strong> (ID: 1042), <strong>리얼리스트 인비저블 세팅 파우더</strong> (ID: 1039).</p>{
   "query":{
      "pinned":{
         "ids":[
            "1043",
            "1042",
            "1039"
         ],
         "organic":{
            "bool":{
               "must":[
                  {
                     "multi_match":{
                        "query":"foundation",
                        "fields":[
                           "name",
                           "category",
                           "description"
                        ]
                     }
                  }
               ]
            }
         }
      }
   }
}<p>특정 제품 ID를 사용하여 쿼리 결과에서 해당 제품이 우선순위를 갖도록 합니다. "쿼리 구조에는" 에 고정되어야 하는 제품 ID 목록(이 경우 ID 1043, 1042, 1039)이 상단에 포함되며, 나머지 결과는 "name", "category", "description" 필드에 텍스트 쿼리와 같은 조건을 조합하여 검색의 자연스러운 흐름을 따르는 구조로 되어 있습니다. 이렇게 하면 항목을 제어된 방식으로 홍보하여 가시성을 확보하는 동시에 나머지 검색은 일반적인 관련성을 기반으로 유지할 수 있습니다.</p><p>아래에서 프로모션된 제품에 대한 쿼리 실행 결과를 확인할 수 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53a6d5cbef227f16/6a1702458b73cb68f0189ef3/8c1a547bfe31360c405eae0891c666635051a51b-1600x1039.png" alt="프로모션 제품에 대한 쿼리 실행 결과" /><p>전체 쿼리 코드는 <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/product-store-search/api/api.py#L112">여기에서</a> 확인할 수 있습니다.</p><h2>행동 분석으로 검색 행동 분석하기</h2><p>지금까지 검색 결과의 연관성을 개선하고 제품을 쉽게 찾을 수 있는 기능을 추가했습니다. 이제 사용자 검색 행동을 분석하여 결과가 있는 쿼리와 없는 쿼리, 검색 결과 클릭 등의 패턴을 식별하는 데 도움이 되는 기능을 포함시켜 검색 솔루션을 완성할 것입니다. 이를 위해 Elastic에서 제공하는 <strong>행동 분석</strong> 기능을 사용할 것입니다. 이를 통해 몇 단계만 거치면 사용자 검색 행동을 모니터링하고 분석하여 검색 환경을 최적화할 수 있는 귀중한 인사이트를 얻을 수 있습니다.</p><h3>행동 분석 컬렉션 만들기</h3><p>첫 번째 작업은 모든 행동 분석 이벤트를 수신할 컬렉션을 만드는 것입니다. 컬렉션을 생성하려면 <strong>검색 &gt; 행동 분석에서</strong> Kibana 인터페이스에 액세스하세요. 아래 예제에서는 <code>tracking-search</code> 라는 이름의 컬렉션을 만들었습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b4cb5480ab0bfef/6a1702474a531b189936a7e7/c05e533212a3690c5ea9f2d5226cbcdc901c482a-1600x1009.png" alt="행동 분석 - 컬렉션 이름 지정하기" /><h3>행동 분석을 인터페이스에 통합하기</h3><p>우리의 <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/hybrid-search-for-an-e-commerce-product-catalogue/app-product-store">프론트엔드</a> 애플리케이션은 JavaScript로 개발되었으며, 행동 분석을 통합하기 위해 공식 Elastic 설명서에 설명된 단계에 따라 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-start.html#behavioral-analytics-start-ui-integration-js-client"><strong>행동 분석 JavaScript 추적기를</strong></a> 설치합니다.</p><h3>자바스크립트 트래커 구현하기</h3><p>이제 트래커 클라이언트를 애플리케이션으로 가져와 <code>trackPageView</code>, <code>trackSearch</code>, <code>trackSearchClick</code> 메서드를 사용하여 사용자 상호작용을 캡처하겠습니다.</p><p><strong>면책</strong> 조항: 사용자 상호작용 데이터를 수집하기 위해 도구를 사용하고 있지만, <strong>GDPR을</strong> 준수하는 것은 필수적입니다. 즉, 사용자에게 어떤 데이터가 수집되고 어떻게 사용되는지 명확하게 알리고 추적 거부 옵션을 제공해야 합니다. 또한 수집된 정보를 보호하고 데이터 액세스 및 삭제와 같은 사용자 권리를 존중하기 위해 강력한 보안 조치를 구현하여 모든 단계가 GDPR 원칙을 준수하도록 해야 합니다.
</p><p><strong>1단계: 트래커 인스턴스 만들기</strong></p><p>먼저 상호작용을 모니터링할 트래커 인스턴스를 생성합니다. 이 구성에서는 대상 엔드포인트, 컬렉션 이름 및 API 키를 정의합니다:</p>createTracker({
  endpoint: "https://endpoint:443",
  collectionName: "tracking-search",
  apiKey: "api-key"
});<p><strong>2단계: 페이지 조회수 캡처</strong></p><p>페이지 조회수를 추적하려면 <code>trackPageView</code> 이벤트를 구성하면 됩니다:</p>    trackPageView({
      page: {
        title: "home-page"
      },
    });<p><code>trackPageView</code> 이벤트에 대한 자세한 내용은 이 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-event-reference.html#behavioral-analytics-event-reference-pageview-fields">문서를</a> 참조하세요.</p><p><strong>3단계: 검색 쿼리 캡처</strong></p><p>사용자의 검색 작업을 모니터링하기 위해 <code>trackSearch</code> 방법을 사용합니다:</p>      trackSearch({
        search: {
          query: searchTerm,
          results: {
            items: documents,
            total_results: response.data.length,
          },
        },
      });<p>여기에서 검색어와 검색 결과를 수집하고 있습니다.</p><p><strong>4단계: 검색 결과 클릭 추적하기</strong></p><p>마지막으로 검색 결과의 클릭 수를 캡처하기 위해 <code>trackSearchClick</code> 방법을 사용합니다:</p>trackSearchClick({
      document: { id: product.id, index: "products-catalog"},
      search: {
        query: searchTerm,
        page: {
          current: 1,
          size: products.length,
        },
        results: {
          items: documents,
          total_results: products.length,
        },
        search_application: "app-product-store"
      },
    });<p>당사는 클릭한 문서의 ID와 검색어 및 검색 결과에 대한 정보를 수집합니다.</p><h3>Kibana에서 데이터 분석하기</h3><p>이제 사용자 상호작용 이벤트가 캡처되고 있으므로 검색 동작에 대한 귀중한 데이터를 얻을 수 있습니다. Kibana는 행동 분석 도구를 사용하여 이 행동 데이터를 시각화하고 분석합니다. 결과를 보려면 <strong>검색 &gt; 행동 분석 &gt; 내 컬렉션으로</strong> 이동하면 캡처된 이벤트의 개요가 표시됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdab4f4507c5a4732/6a17024860084b34ca3c4411/e219c4af2e01b0ccc1b9079459a07832835abc75-1600x1155.png" alt="Kibana에서 데이터 분석하기" /><p>이 개요에서는 인터페이스에 통합된 각 작업에 대해 캡처된 이벤트를 전반적으로 살펴볼 수 있습니다. 이 정보를 통해 사용자 검색 행동에 대한 귀중한 인사이트를 얻을 수 있습니다. 그러나 특정 시나리오와 더 관련성이 높은 메트릭으로 개인화된 대시보드를 만들고 싶다면, Kibana는 대시보드 구축을 위한 강력한 도구를 제공하여 메트릭의 다양한 시각화를 만들 수 있게 해줍니다.</p><p>아래에서는 시간 경과에 따른 가장 많이 검색된 용어, 결과가 없는 쿼리, 가장 많이 검색된 용어를 강조하는 워드 클라우드, 마지막으로 검색 액세스가 어디에서 발생하는지 파악하기 위한 지리적 시각화 등 몇 가지 시각화 및 차트를 만들어 모니터링했습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04ced4406436f942/6a17024a5091685116e1b961/84a2c39dab77a3f9366c258a3a45d2cbc7df125e-1600x689.png" alt="모니터링할 시각화 및 차트" /><h2>결론</h2><p>이 글에서는 키워드 검색과 벡터 검색을 결합한 하이브리드 검색 솔루션을 구현하여 사용자에게 보다 정확하고 관련성 높은 결과를 제공했습니다. 또한 고정 쿼리를 통해 패싯 및 결과 맞춤 설정과 같은 추가 기능을 사용하여 보다 완벽하고 효율적인 검색 환경을 만드는 방법도 살펴봤습니다.</p><p>또한 검색 엔진과 상호 작용하는 동안 사용자 행동을 포착하고 분석하기 위해 Elastic의 <strong>행동 분석을</strong> 통합했습니다. <code>trackPageView</code>, <code>trackSearch</code>, <code>trackSearchClick</code> 와 같은 방법을 사용하여 검색 쿼리, 검색 결과 클릭, 페이지 조회수를 모니터링하여 검색 행동에 대한 귀중한 인사이트를 얻을 수 있었습니다.</p><h2>참고 자료</h2><p>데이터 세트</p><p><a href="https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset">https://www.kaggle.com/datasets/shivd24coder/cosmetic-brand-products-dataset</a></p><p>트랜스포머</p><p><a href="https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2">https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2</a></p><p>상호 등급 융합</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/rrf.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever">https://www.elastic.co/guide/en/elasticsearch/reference/current/retriever.html#rrf-retriever</a></p><p>Knn 쿼리</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html</a></p><p>고정 쿼리</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-pinned-query.html</a></p><p>행동 분석 API</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-apis.html</a></p><p>https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-overview.html</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hybrid-search-ecommerce</guid>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63c711d1bf9b2501/6a17024c66c4f9c2b7f8bebd/05578fc595a12f6b1ebf88a10a2a31e9971b545e-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 12 Nov 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[시맨틱 검색 구현: Elasticsearch로 레시피 검색 구축하기]]></title>
    <description><![CDATA[전자상거래 웹사이트의 맥락에서 시맨틱 검색을 구현합니다.]]></description>
    <content:encoded><![CDATA[<h2>소개</h2><p>많은 전자상거래 웹사이트가 레시피 검색 환경을 개선하는 데 관심이 있습니다. 시맨틱 검색을 올바르게 적용하면 고객은 "발렌타인데이 음식" 또는 "추수감사절 음식과 같이 보다 자연스러운 쿼리를 기반으로 필요한 식재료를 빠르게 찾을 수 있습니다."</p><p>이 문서에서는 Elasticsearch를 사용하여 이러한 쿼리를 지원하는 시맨틱 검색을 구현하는 방법을 보여드리겠습니다. 슈퍼마켓의 식재료 및 제품 카탈로그를 저장하는 인덱스를 구성하고 이 인덱스를 사용하여 레시피 검색을 개선하는 방법을 시연해 보겠습니다. 이 글에서는 이 데이터 구조를 만들고 자연어 처리 기술을 적용하여 고객의 의도에 맞는 관련 결과를 제공하는 방법에 대해 설명합니다.</p><p>이 글에 소개된 모든 코드는 Python으로 개발되었으며 <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/building-a-recipe-search-with-elasticsearch">GitHub에서</a> 사용할 수 있습니다. 리포지토리에 액세스하여 소스 코드를 검토하고 필요에 따라 조정한 후 개발 환경에서 직접 솔루션을 구현할 수 있습니다.</p><h2>시맨틱 검색 구현 시작</h2><p>시맨틱 검색 구현을 시작하려면 먼저 자연어 모델을 정의해야 합니다. Elastic은 자체 모델인 <a href="https://www.elastic.co/guide/en/machine-learning/8.15/ml-nlp-elser.html"><strong>ELSER를</strong></a> 제공할 뿐만 아니라 Hugging Face와 같은 다양한 제공업체의 NLP 모델을 통합할 수 있는 지원도 제공합니다. 이러한 유연성을 통해 필요에 가장 적합한 옵션을 선택할 수 있습니다.</p><p>이 글에서는 NLP 모델 배포 및 관리의 복잡성을 줄여주는 <strong>ELSER를</strong> 사용하겠습니다. 또한 Elastic은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search-semantic-text.html"><strong>semantic_text</strong></a> 기능을 제공하여 프로세스를 크게 간소화합니다. <strong>semantic_text를</strong> 사용하면 전체 임베딩 생성 프로세스가 간단하고 자동화됩니다. 추론 지점을 정의하고 인덱스 매핑에서 임베딩을 수신할 필드를 지정하기만 하면 됩니다. 문서 색인 중에 임베딩이 생성되어 지정된 필드와 자동으로 연결됩니다.</p><h3>설정 단계</h3><p>다음은 시맨틱 검색을 지원하는 색인을 만드는 단계입니다. 이 안내에 따라 인덱스를 구성하고 시맨틱 검색을 위한 준비를 마치면 됩니다:</p><ol><li><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-elser.html"><strong>추론 지점을</strong></a> 만듭니다.</p></li><li><p>임베딩을 수신할 수 있도록 설명 필드를 semantic_text로 설정하여 <a href="https://github.com/andreluiz1987/semantic-search-market/blob/main/infra.py"><strong>인덱스를 생성합니다</strong></a>.</p></li><li><p><a href="https://github.com/andreluiz1987/semantic-search-market/blob/main/ingestion.py"><strong>데이터를</strong></a> 제품 카탈로그를 저장할 식료품 카탈로그 인덱스에 색인합니다. 이 카탈로그는 <a href="https://www.kaggle.com/datasets/bhavikjikadara/grocery-store-dataset?select=GroceryDataset.csv">여기에서</a> 제공되는 데이터 세트에서 가져온 것입니다.</p></li></ol><h2>마트에서 시맨틱 검색 적용하기</h2><p>이제 식료품점 제품 데이터로 인덱스가 채워졌으므로 시맨틱 검색을 사용하여 검색 결과를 개선하기 위해 쿼리를 테스트하고 검증하고 있습니다. Google의 목표는 문맥과 사용자의 의도를 이해하여 보다 관련성 있고 정확한 결과를 제공하는 더 스마트한 검색 환경을 제공하는 것입니다.</p><h3>시맨틱 검색으로 해결한 과제</h3><p>제품 카탈로그를 기반으로 시맨틱 검색이 기존 어휘 검색이 종종 어려움을 겪는 어휘 및 문맥 문제를 해결하여 식료품점의 검색 환경을 어떻게 변화시킬 수 있는지 살펴보겠습니다.</p><h4><strong>1. 요리 의도에 대한 해석</strong></h4><p><strong>문제 01</strong>: 고객이 구이용 해산물 "" 을 검색할 수 있지만 어휘 검색 시스템이 쿼리의 의도를 완전히 이해하지 못할 수 있습니다. 구이에 적합한 모든 해산물 제품을 식별하지 못하고 제품 제목에 "해산물" 또는 "그릴" 이라는 정확한 용어가 포함된 제품만 반환할 수 있습니다.</p><p>먼저 어휘 검색을 수행하고 결과를 분석합니다. 그런 다음 동일한 검색어에 대한 결과를 비교하는 시맨틱 검색으로 동일한 작업을 수행합니다.</p><p><strong>어휘 검색 쿼리</strong></p> response = client.search(
        index="grocery-catalog",
        size=5,
        source_excludes="description_embedding",
        query={
            "multi_match": {
                "query": "seafood for grilling",
                "fields": [
                    "name",
                    "description"]
            }
        }
    )<p><strong>결과:</strong></p><p>검색 유형</p><p>이름</p><p>점수</p><p>어휘</p><p>노스웨스트 피쉬 알래스카 바이르디 대게</p><p>10.453125</p><p>어휘</p><p>요시다 씨, 소스 오리지널 고메</p><p>7.2289705</p><p>어휘</p><p>프리미엄 해산물 버라이어티 팩 - 20개</p><p>7.1924105</p><p>어휘</p><p>미국산 참돔 - 통살, 머리 부분, 세척 완료</p><p>6.998647</p><p>어휘</p><p>랍스터 발톱 &amp; 팔, 지속 가능한 야생 어획물</p><p>6.438654</p><p>어휘 검색 결과 아메리칸 레드 스내퍼, 북서부 생선 알래스카 베어디 대게 등 구이에 적합한 일부 해산물 품목이 반환되었습니다. 그러나 어휘 검색은 해산물 품목이 아닌 고기 소스인 요시다 소스 등 관련성이 낮은 제품을 목록 상단에 표시해 어휘 알고리즘이 구이용 "의 문맥을 완전히 이해하지 못했음을 시사했습니다."</p><p><strong>시맨틱 검색 솔루션</strong></p><p>"해산물" 이라는 용어와 "구이" 와 같은 준비 컨텍스트를 결합하는 쿼리를 사용하여 "구이" 또는 "해산물" 이라는 단어가 제품 이름에 직접 나타나지 않더라도 구이에 적합한 생선 필레, 새우, 가리비 등의 포괄적인 옵션 목록을 반환합니다. 이렇게 하면 검색 결과가 고객의 의도와 더욱 밀접하게 일치합니다.</p><p><strong>시맨틱 검색을 쿼리하세요:</strong></p>es_client.search(
   index="grocery-catalog-elser",
   size=size,
   source_excludes="description_embedding",
   query={
       "semantic": {
           "field": "description_embedding",
           "query": "seafood for grilling"

       }
   })<p>검색 유형</p><p>이름</p><p>점수</p><p>시맨틱</p><p>머리부터 통째로 손질한 브란치노 생선</p><p>16.175909</p><p>시맨틱</p><p>알래스카 대구(검은 담비)</p><p>15.855331</p><p>시맨틱</p><p>아메리칸 레드 스내퍼 - 통째로, 머리째로</p><p>15.454779</p><p>시맨틱</p><p>노스웨스트 피쉬 알래스카 바이르디 대게</p><p>15.855331</p><p>시맨틱</p><p>아메리칸 레드 스내퍼 - 통째로, 머리째로</p><p>15.3892355</p><p>시맨틱 검색은 "해산물," 이라는 용어와 직접적으로 관련된 제품을 반환했을 뿐만 아니라 "구이," 구이에 적합한 통생선 및 필레라는 문맥도 이해했습니다. 여기서 핵심은 구이용으로 흔히 사용되는 브란지노와 알래스카 블랙 대구와 같은 통생선 옵션이 포함된 결과의 정확성입니다.</p><p><strong>문제 02 </strong>: 많은 고객이 긴 하루 일과를 마치고 빠르고 간편한 저녁 식사 솔루션을 검색하며 "쉬운 평일 저녁 식사" 같은 용어를 사용합니다. 기존의 어휘 검색은 간편식의 개념을 완전히 포착하지 못할 수 있으며, 종종 제품명에 "easy" 이라는 단어가 포함된 제품에만 초점을 맞출 수 있습니다.</p><p>이전 문제에서와 마찬가지로 어휘 검색을 수행하는 것으로 시작하겠습니다. 그 후 시맨틱 검색을 이용한 솔루션을 적용합니다.</p><p><strong>어휘 검색 쿼리</strong></p> response = client.search(
        index="grocery-catalog",
        size=5,   
        source_excludes="description_embedding",
        query={
            "multi_match": {
                "query": "easy weeknight meals",
                "fields": [
                    "name",
                    "description"]
            }
        }
    )<p><strong>결과:</strong></p><p>검색 유형</p><p>이름</p><p>점수</p><p>어휘</p><p>에이버리 이지 필 주소 라벨, 4200매</p><p>8.017723</p><p>어휘</p><p>오믈렛 자체 가열 비상/휴대용 식사 32</p><p>6.592727</p><p>어휘</p><p>연안 해산물 황다랑어 큐브 포케</p><p>5.836883</p><p>어휘</p><p>헤비 슈퍼 웨이트 12온스 폼</p><p>5.8116536</p><p>어휘</p><p>베니티 페어 에브리데이 냅킨, 2겹, 110카운트</p><p>5.752989</p><p>어휘 검색은 'Avery Easy Peel 주소 라벨', 'Vanity Fair Everyday Napkins' 등 식사와 전혀 관련이 없는 항목을 포함하여 훨씬 덜 연관성 있는 결과를 반환했습니다. 이러한 제품은 빠른 식사에 대한 사용자의 요구를 충족시키지 못합니다. 어휘 검색에서 유용한 제품(오밀스 셀프 히팅 비상식량)이 한 개 나왔지만, 냅킨이나 라벨과 같은 다른 결과는 설명에 "easy" 또는 "weeknight" 라는 단어만 일치할 뿐, 빠른 식사 솔루션을 원하는 사용자의 의도를 제대로 다루지 못했습니다.</p><p><strong>시맨틱 검색 솔루션</strong></p><p>빠르고 간편한 식사의 의도를 이해하는 쿼리를 구현했습니다. 미리 조리된 육류, 냉동 파스타, 밀키트 등 빠르게 조리할 수 있는 제품은 이름에 "easy" 이라는 단어가 명시적으로 포함되어 있지 않더라도 연관 검색어로 연결됩니다. 이러한 접근 방식을 통해 고객은 평일 저녁 식사를 빠르게 해결할 수 있는 가장 적합한 옵션을 찾을 수 있으며, 편의성에 대한 니즈를 해결할 수 있습니다.</p><p><strong>쿼리 시맨틱 검색</strong></p>es_client.search(
   index="grocery-catalog-elser",
   size=size,
   source_excludes="description_embedding",
   query={
       "semantic": {
           "field": "description_embedding",
           "query": "easy weeknight meals"

       }
   })<p><strong>결과:</strong></p><p>검색 유형</p><p>이름</p><p>점수</p><p>시맨틱</p><p>오믈렛 자체 가열 비상/휴대용 식사 32</p><p>14.610006</p><p>시맨틱</p><p>닛신, 컵누들, 새우, 2.5 온스</p><p>13.751424</p><p>시맨틱</p><p>나마스테 글루텐 프리 와플 &amp; 팬케이크 믹스</p><p>13.73376</p><p>시맨틱</p><p>아이다호 스퍼드, 골든 그릴 해시브라운 감자</p><p>12.549422</p><p>시맨틱</p><p>닛신, 컵 누들, 치킨, 24개입</p><p>12.034527</p><p>시맨틱 검색 결과 평일 저녁에 간편하게 먹을 수 있는 인스턴트 라면(컵라면), 미리 조리된 감자, 팬케이크 믹스 등 빠르고 간편한 식사와 관련된 제품이 주로 검색되었습니다. 이는 시맨틱 검색이 "쉬운 평일 저녁 식사," 빠르고 편리한 식사를 찾고자 하는 사용자의 의도를 파악하는 문구 뒤에 숨은 개념을 파악할 수 있음을 보여줍니다. 흥미롭게도 "탄산음료," 등 다른 카테고리의 제품도 맥락과 관련이 있는 경우 포함될 수 있습니다(예: 식사와 함께 마시는 음료).</p><h4><strong>2. 지역 용어 및 어휘 변형</strong></h4><p><strong>문제</strong>: 한 고객이 "소다," 을 검색하는 반면 다른 고객은 "팝" 을 사용하여 같은 제품을 검색할 수 있습니다. 기존의 어휘 검색은 두 용어가 동일한 항목을 지칭한다는 사실을 인식하지 못합니다.</p><p><strong>어휘 검색 쿼리</strong></p> response = client.search(
        index="grocery-catalog",
        size=5,
        source_excludes="description_embedding",
        query={
            "multi_match": {
                "query": "refreshing pop drink low sugar",
                "fields": [
                    "name",
                    "description"]
            }
        }
    )<p><strong>결과:</strong></p><p>검색 유형</p><p>이름</p><p>점수</p><p>어휘</p><p>프라임 하이드레이션+ 스틱 전해질 음료 믹스</p><p>14.492869</p><p>어휘</p><p>카프리 썬, 100% 주스, 버라이어티 팩</p><p>12.340851</p><p>어휘</p><p>조이버스트 에너지 드링크, 로즈 로즈, 12개입</p><p>11.839179</p><p>어휘</p><p>켈로그 팝 타르트, 프로스트 브라운 슈가 시나몬</p><p>9.97788</p><p>어휘</p><p>종류 미니 바, 버라이어티 팩, 0.7</p><p>9.336912</p><p>어휘 검색은 정확한 단어 일치에 중점을 둡니다. 프라임 하이드레이션, 카프리 썬과 같은 제품을 반환했지만 "팝" 이라는 용어와 직접 일치하면 음료가 아닌 스낵인 켈로그의 팝타르트와 같은 부적절한 결과를 초래하기도 했습니다. 이는 한 용어에 여러 가지 의미가 있거나 모호할 경우 어휘 검색의 효율성이 떨어질 수 있다는 점을 강조합니다.</p><p><strong>시맨틱 검색 솔루션</strong></p><p>의미론적 쿼리에서는 어휘 검색이 해결하지 못하는 어휘 변형 문제를 극복할 수 있습니다. 검색어를 확장하면 문맥적 의미에 기반한 결과를 얻을 수 있어 보다 관련성 있고 포괄적인 답변을 제공할 수 있습니다.</p><p><strong>쿼리:</strong></p>es_client.search(
   index="grocery-catalog-elser",
   size=size,
   source_excludes="description_embedding",
   query={
       "semantic": {
           "field": "description_embedding",
           "query": "refreshing pop drink low sugar"

       }
   })<p><strong>결과:</strong></p><p>검색 유형</p><p>이름</p><p>점수</p><p>시맨틱</p><p>올리팝 12온스 프리바이오틱스 소다 버라이어티</p><p>14.776867</p><p>시맨틱</p><p>바이 항산화 코코퓨전, 버라이어티 팩, 18호</p><p>14.663253</p><p>시맨틱</p><p>몬스터 에너지 드링크, 제로 울트라, 24</p><p>14.486348</p><p>시맨틱</p><p>조이버스트 에너지 버라이어티, 12온스, 12온스</p><p>14.007214</p><p>시맨틱</p><p>조이버스트 에너지 드링크, 로즈 로즈, 12개입</p><p>13.641038</p><p>시맨틱 검색은 제품 이름에 정확한 용어인 "팝" 이 없더라도 "소다" 의 동의어로서 "팝" 의 개념과 직접적으로 일치하는 제품을 반환합니다(예: 올리팝 프리바이오틱스 소다). 검색은 상쾌한 저당 음료라는 사용자의 의도를 파악하고 프리바이오틱 탄산음료(Olipop), 무설탕 에너지 음료(Monster Energy Drink) 등 관련 제품을 반환할 수 있었습니다.</p><h2>결론</h2><p>식료품점 맥락에서 시맨틱 검색을 구현하면 "구이용 해산물" 및 "간단한 평일 저녁 식사 와 같은 복잡한 쿼리를 이해하는 데 매우 효과적임이 입증되었습니다." 이러한 접근 방식을 통해 사용자의 의도를 보다 정확하게 해석하여 관련성이 높은 상품을 추천할 수 있었습니다.</p><p>Elasticsearch를 사용하고 ELSER로 프로세스를 간소화함으로써 시맨틱 검색을 빠르고 효율적으로 적용하여 검색 결과를 크게 개선하고 보다 민첩하고 타깃화된 쇼핑 경험을 제공할 수 있었습니다. 이를 통해 검색 프로세스가 최적화되었을 뿐만 아니라 고객에게 제공되는 결과의 관련성도 높아졌습니다.</p><h2>참고 자료</h2><p>모델 ELSER:</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-elser.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-elser.html</a></p><p></p><p>시맨틱 텍스트:</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html</a></p><p></p><p>데이터 세트:</p><p><a href="https://www.kaggle.com/datasets/bhavikjikadara/grocery-store-dataset?select=GroceryDataset.csv">https://www.kaggle.com/datasets/bhavikjikadara/grocery-store-dataset?select=GroceryDataset.csv</a></p><p></p><p>시맨틱 검색:</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search-semantic-text.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search-semantic-text.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/semantic-search-elasticsearch-ecommerce</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/semantic-search-elasticsearch-ecommerce</guid>
    <category><![CDATA[기본]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c545fc80b6d79d6/6a170214839dfad776dcfd6f/d968e646240cd3ef7c79b5124d562a5f951d812b-1440x840.png" length="0" type="image/png"/>
    <pubDate>Thu, 07 Nov 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Apache Camel을 통해 Elasticsearch로 데이터를 수집하는 방법]]></title>
    <description><![CDATA[실제 예제를 통해 Apache Camel을 통해 Elasticsearch로 데이터를 수집하는 방법을 알아보세요.]]></description>
    <content:encoded><![CDATA[<p>Apache Camel을 사용해 Elasticsearch로 데이터를 수집하는 것은 검색 엔진의 견고함과 통합 프레임워크의 유연성을 결합하는 프로세스입니다. 이 글에서는 Apache Camel이 어떻게 데이터 수집을 간소화하고 Elasticsearch로 최적화할 수 있는지 살펴보겠습니다. 이 기능을 설명하기 위해, Apache Camel을 구성하고 사용하여 Elasticsearch로 데이터를 전송하는 방법을 단계별로 보여주는 입문용 애플리케이션을 구현해 보겠습니다.</p><h2>아파치 카멜이란 무엇인가요?</h2><p>Apache Camel은 다양한 시스템 연결을 간소화하는 오픈소스 통합 프레임워크로, 개발자는 시스템 통신의 복잡성에 대한 걱정 없이 비즈니스 로직에 집중할 수 있습니다. Camel의 중심 개념은 "경로(" )로, 메시지가 출발지에서 목적지까지 이동하는 경로를 정의하며 변환, 유효성 검사, 필터링 등의 중간 단계가 포함될 수 있습니다.</p><h3>아파치 카멜 아키텍처</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05652327efd4d5d1/6a17e6dafbc5f8a588491a8b/bef8145623a8fa80f929f9faa57ce0c460be2d0b-884x458.png" alt="아파치 카멜 아키텍처" /><p>Camel은 "구성 요소" 를 사용하여 데이터베이스 및 메시징 서비스와 같은 다양한 시스템 및 프로토콜에 연결하고 "엔드포인트" 를 사용하여 메시지의 시작 및 종료 지점을 나타냅니다. 이러한 개념은 모듈식의 유연한 설계를 제공하므로 복잡한 통합을 효율적이고 확장 가능하게 구성하고 관리하기가 더 쉬워집니다.</p><h2>Elasticsearch와 Apache Camel 사용</h2><p>Apache Camel을 사용해 데이터를 Elasticsearch 클러스터로 수집하는 간단한 Java 애플리케이션을 구성하는 방법을 보여드리겠습니다. Apache Camel에 정의된 경로를 사용하여 Elasticsearch에서 데이터를 생성, 업데이트 및 삭제하는 프로세스도 다룹니다.</p><h3>1. 종속성 추가하기</h3><p>이 통합을 구성하는 첫 번째 단계는 프로젝트의 <code>pom.xml</code> 파일에 필요한 종속성을 추가하는 것입니다. 여기에는 Apache Camel 및 Elasticsearch 라이브러리가 포함됩니다. 새로운 Java API 클라이언트 라이브러리를 사용할 것이므로 <code>camel-elasticsearch</code> 컴포넌트를 가져와야 하며 버전은 <code>camel-core</code> 라이브러리와 동일해야 합니다.</p><p>Java 로우레벨 Rest 클라이언트를 사용하려면 Elasticsearch 로우레벨 Rest 클라이언트 구성 요소를 사용해야 합니다.</p>&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-core&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-elasticsearch&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;org.apache.camel&lt;/groupId&gt;
   &lt;artifactId&gt;camel-jackson&lt;/artifactId&gt;
   &lt;version&gt;4.7.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
   &lt;groupId&gt;co.elastic.clients&lt;/groupId&gt;
   &lt;artifactId&gt;elasticsearch-java&lt;/artifactId&gt;
   &lt;version&gt;8.14.3&lt;/version&gt;
&lt;/dependency&gt;
<h3>2. Camel 컨텍스트 구성 및 실행</h3><p>구성은 경로를 정의하고 실행하는 기반이 되는 <code>DefaultCamelContext</code> 클래스를 사용하여 새 Camel 컨텍스트를 만드는 것으로 시작됩니다. 다음으로, Apache Camel이 Elasticsearch 클러스터와 상호 작용할 수 있도록 Elasticsearch 구성 요소를 구성합니다. <code>ESlasticsearchComponent</code> 인스턴스는 로컬 Elasticsearch 클러스터의 기본 주소인 <code>localhost:9200</code> 주소에 연결하도록 구성됩니다. 인증이 필요한 환경 설정의 경우 구성 요소를 구성하고 기본 인증을 활성화하는 방법에 대한 설명서( <strong>"구성 요소 구성 및 기본 인증 활성화"</strong> 참조)를 읽어보셔야 합니다.</p>public class ESComponent {

    public static ElasticsearchComponent getInstance() {
        var elasticsearch = new ElasticsearchComponent();
        elasticsearch.setHostAddresses("localhost:9200");
        return elasticsearch;
    }

    public static String getName() {
        return "elasticsearch";
    }
}
<p>그런 다음 이 구성 요소를 Camel 컨텍스트에 추가하여 정의된 경로가 이 구성 요소를 사용하여 Elasticsearch에서 작업을 수행할 수 있도록 합니다.</p>try (var context = new DefaultCamelContext()) {
   context.addComponent(ESComponent.getName(), ESComponent.getInstance());
   context.addRoutes(new OperationBulkRoute());
   context.start();
}
<p>그 후 경로가 컨텍스트에 추가됩니다. 문서 일괄 색인, 업데이트, 삭제를 위한 경로를 생성합니다.</p><h3>3. Camel 경로 구성</h3><h4>데이터 인덱싱</h4><p>첫 번째로 구성할 경로는 데이터 인덱싱을 위한 것입니다. 영화 카탈로그가 포함된 JSON 파일을 사용하겠습니다. 경로는 <a href="https://gist.github.com/andreluiz1987/40756874b5fbea0a29586f9376d7f1f4"><code>src/main/resources/movies.json</code></a> 에 있는 파일을 읽고, JSON 콘텐츠를 Java 객체로 역직렬화한 다음, 여러 메시지를 하나로 결합하는 집계 전략을 적용하여 Elasticsearch에서 일괄 작업을 수행할 수 있도록 구성됩니다. 메시지당 500개 항목의 크기가 구성되었으므로 대량으로 한 번에 500개의 필름을 색인합니다.</p><p>Elasticsearch 작업 일괄 라우팅</p>String URI_BULK_OPERATION = String
       .format("elasticsearch://elasticsearch?operation=%s&amp;indexName=%s",
               IndexOperationConfig.BULK_OPERATION,
               INDEX_NAME);
public class OperationBulkRoute extends RouteBuilder {
   private static final Log log = LogFactory.getLog(OperationBulkRoute.class);
   private static final int BULK_SIZE = 500;

   @Override
   public void configure() {
       from("file:src/main/resources?fileName=movies.json&amp;noop=true")
               .routeId("route-bulk-ingest")
               .unmarshal().json()
               .split(body())
               .aggregate(constant(true), new BulkAggregationStrategy())
               .completionSize(BULK_SIZE)
               .to(URI_BULK_OPERATION)
               .process(exchange -&gt; {
                   var body = exchange.getIn().getBody(String.class);
                   log.info(String.format("Response: %s", body));
               })
               .end();
   }
}
<p>문서 배치가 Elasticsearch의 대량 작업 엔드포인트로 전송됩니다. 이 접근 방식은 대량의 데이터를 처리할 때 효율성과 속도를 보장합니다.</p><h4>데이터 업데이트</h4><p>다음 경로는 문서를 업데이트하는 것입니다. 이전 단계에서 일부 영화를 색인화했으며 이제 참조 코드로 문서를 검색하는 새 경로를 만든 다음 등급 필드를 업데이트합니다.</p><p>Elasticsearch 구성 요소가 등록되고 사용자 정의 경로 IngestionRoute가 추가되는 Camel 컨텍스트 <code>(DefaultCamelContext)</code> 를 설정합니다. 작업은 직접:업데이트-인제스트먼트 엔드포인트에서 경로를 시작하는 ProducerTemplate을 통해 문서 코드를 전송하는 것으로 시작됩니다.</p>try (var context = new DefaultCamelContext()) {
    context.addComponent(ESComponent.getName(), ESComponent.getInstance());
    context.addRoutes(new IngestionRoute());
    context.start();
    ProducerTemplate producerTemplate = context.createProducerTemplate();
    producerTemplate.sendBody("direct:update-ingestion", documentCode);
    Thread.sleep(5000);
}
<p>다음으로, 이 흐름의 입력 엔드포인트인 IngestionRoute가 있습니다. 이 경로는 여러 파이프라인 연산을 수행합니다. 먼저, 코드별로 문서를 찾기 위해 Elasticsearch에서 검색을 수행합니다 <code>(direct:search-by-id)</code>, 여기서 SearchByCodeProcessor는 코드를 기반으로 쿼리를 조합합니다. 그런 다음, 검색된 문서가 UpdateRatingProcessor에 의해 처리되고, 그 결과를 Movie 개체로 변환하고, 영화 등급을 특정 값으로 업데이트하고, 업데이트를 위해 업데이트된 문서를 다시 Elasticsearch로 전송할 수 있도록 준비합니다.</p>public class IngestionRoute extends RouteBuilder {
    private static final Log log = LogFactory.getLog(IngestionRoute.class);

    @Override
    public void configure() throws Exception {

        from("direct:update-ingestion")
                .pipeline()
                .to("direct:search-by-id")
                .to(URI_SEARCH_OPERATION)
                .to("direct:update-rating")
                .to(URI_UPDATE_OPERATION)
                .process(exchange -&gt; {
                    var body = exchange.getIn().getBody(String.class);
                    log.info(String.format("Response: %s", body));
                })
                .end();

        from("direct:search-by-id")
                .process(new SearchByCodeProcessor());

        from("direct:update-rating")
                .process(new UpdateRatingProcessor());
    }
}
<p><code>SearchByCodeProcessor</code> 프로세서는 검색 쿼리를 실행하도록만 구성되었습니다:</p>public class SearchByCodeProcessor implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        var code = exchange.getIn().getBody();

        String query = "{\n" +
                "  \"query\": {\n" +
                "   \"term\": {\n" +
                "     \"code\": {\n" +
                "       \"value\":" + code + "\n" +
                "     }\n" +
                "   }\n" +
                "  }\n" +
                "}";
        exchange.setProperty("document_code", code);
        exchange.getIn().setBody(query);
    }
}
<p><code>UpdateRatingProcessor</code> 프로세서는 등급 필드를 업데이트할 책임이 있습니다.</p>public class UpdateRatingProcessor implements Processor {

    private final ObjectMapper objectMapper;

    public UpdateRatingProcessor() {
        this.objectMapper = new ObjectMapper();
        this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Override
    public void process(Exchange exchange) throws Exception {

        HitsMetadata response = exchange.getIn().getBody(HitsMetadata.class);
        var code = Long.parseLong(exchange.getProperty("document_code").toString());

        if (response != null &amp;&amp; response.hits() != null) {

            var documents = parseToMovies(response);

            var optionalMovie = documents.stream()
                    .filter(document -&gt; code == (document.getSource().getCode())).findAny();

            optionalMovie.ifPresent(document -&gt; {
                document.getSource().setRating(13.0);
                Map&lt;String, Object&gt; updateMap = new HashMap&lt;&gt;();
                updateMap.put("doc", document.getSource());
                exchange.getIn().setHeader("indexId", document.getId());
                exchange.getIn().setBody(updateMap);
            });
        }
    }
<h4>데이터 삭제</h4><p>마지막으로 문서 삭제 경로가 구성됩니다. 여기서는 해당 ID를 사용하여 문서를 삭제합니다. Elasticsearch에서 문서를 삭제하려면 문서 식별자, 즉 문서가 저장되어 있는 인덱스를 알고 삭제 요청을 실행해야 합니다. Apache Camel에서는 아래와 같이 새 경로를 생성하여 이 작업을 수행합니다.</p><p>경로는 진입점 역할을 하는 direct:op-delete 엔드포인트에서 시작됩니다. 문서를 삭제해야 하는 경우 해당 문서의 식별자 <code>(_id)</code> 가 메시지 본문으로 수신됩니다. 그런 다음 경로에서는 메시지 본문에서 _id를 추출하는 간단한<code>("${body}")</code> 을 사용하여 이 식별자 값으로 indexId 헤더를 설정합니다.</p>public class OperationDeleteRoute extends RouteBuilder {
   private static final Log log = LogFactory.getLog(OperationDeleteRoute.class);

   @Override
   public void configure() {
       from("direct:op-delete")
               .routeId("route-delete")
               .setHeader("indexId", simple("${body}"))
               .to(URI_DELETE_OPERATION)
               .process(exchange -&gt; {
                   var body = exchange.getIn().getBody(String.class);
                   log.info(String.format("Response: %s", body));
               })
               .end();
       ;
   }
}
String URI_DELETE_OPERATION = String
       .format("elasticsearch://elasticsearch?operation=%s&amp;indexName=%s",
               IndexOperationConfig.DELETE_OPERATION,
               INDEX_NAME);
<p>마지막으로, 메시지는 URI_DELETE_OPERATION에 의해 지정된 엔드포인트로 전달되며, 이 엔드포인트는 해당 인덱스에서 문서 제거 작업을 수행하기 위해 Elasticsearch에 연결됩니다.
이제 경로를 생성했으므로, Elasticsearch 구성 요소를 포함하도록 구성된 Camel 컨텍스트 <code>(DefaultCamelContext)</code> 를 생성할 수 있습니다.</p>try (var context = new DefaultCamelContext()) {
   context.addComponent(ESComponent.getName(), ESComponent.getInstance());
   context.addRoutes(new OperationDeleteRoute());
   context.start();
   ProducerTemplate producerTemplate = context.createProducerTemplate();
   producerTemplate.sendBody("direct:op-delete", documentId);
}
<p>다음으로 <code>OperationDeleteRoute</code> 클래스에 의해 정의된 삭제 경로가 컨텍스트에 추가됩니다. 컨텍스트가 초기화된 상태에서 <code>ProducerTemplate</code> 을 사용하여 삭제해야 하는 문서의 식별자를 <code>direct:op-delete</code> 엔드포인트로 전달하면 삭제 경로가 트리거됩니다.</p><h2>결론</h2><p>Apache Camel과 Elasticsearch의 통합을 통해 강력하고 효율적인 데이터 수집이 가능하며, 색인, 업데이트, 삭제와 같은 다양한 데이터 조작 시나리오를 처리할 수 있는 경로를 정의할 수 있는 Camel의 유연성을 활용할 수 있습니다. 이 설정을 사용하면 복잡한 프로세스를 확장 가능한 방식으로 오케스트레이션하고 자동화하여 Elasticsearch에서 데이터를 효율적으로 관리할 수 있습니다. 이 예는 이러한 도구를 함께 사용하여 데이터 수집을 위한 효율적이고 적응력 있는 솔루션을 만드는 방법을 보여줍니다.</p><h2>참고 자료</h2><ul><li><p><a href="https://camel.apache.org/manual/">아파치 카멜</a></p></li><li><p><a href="https://camel.apache.org/manual/architecture.html">아파치 카멜 아키텍처</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/eips/aggregate-eip.html">아파치 카멜 집계</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/file-component.html">파일 구성 요소</a></p></li><li><p><a href="https://camel.apache.org/components/4.4.x/elasticsearch-component.html">Elasticsearch 구성 요소</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-apache-camel-ingest-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-apache-camel-ingest-data</guid>
    <category><![CDATA[인덱스 데이터]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05652327efd4d5d1/6a17e6dafbc5f8a588491a8b/bef8145623a8fa80f929f9faa57ce0c460be2d0b-884x458.png" length="0" type="image/png"/>
    <pubDate>Mon, 09 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>