<?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[ES|QL - 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[ES|QL - 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/blog/category/esql</link>
    </image>
    <link>https://www.elastic.co/kr/search-labs/blog/category/esql</link>
    <atom:link href="https://www.elastic.co/kr/search-labs/rss/category/esql.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[kr]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 09:35:55 GMT</lastBuildDate>
  <item>
    <title><![CDATA[인덱싱하지 않은 데이터에 풀텍스트 검색을 제공하는 Elasticsearch ES|QL]]></title>
    <description><![CDATA[MATCH와 TO_TEXT는 인덱싱하지 않은 데이터에 풀텍스트 검색을 제공합니다. ES|QL에서 계산된 열, 매핑되지 않은 필드 및 페더레이션된 소스를 검색합니다.]]></description>
    <content:encoded><![CDATA[<p>ES|QL MATCH는 이제 인덱싱하지 않은 데이터에 대해 풀텍스트 검색을 실행합니다. 계산된 열, 매핑되지 않은 필드, 즉석에서 조합된 스트링, 심지어 S3에 저장된 페더레이션된 데이터까지 모두 포함됩니다. 새로운 TO_TEXT 함수는 ES|QL에 모든 스트링을 분석 가능한 텍스트로 취급하도록 지시하므로, MATCH가 쿼리 수명 동안만 존재하는 값을 토큰화하고 대소문자를 구분하며 검색어 매칭을 수행할 수 있습니다. 이는 대부분의 쿼리 엔진이 인덱싱되지 않은 스트링에 대해 제공하는 LIKE 및 RLIKE 패턴 매칭을 넘어선 진정한 분석입니다. 현재 Elastic Cloud Serverless에서 이용 가능하며, Elasticsearch 9.5에서 기술 미리보기로 제공됩니다.</p><h2>MATCH 및 TO_TEXT가 모든 ES|QL 표현식에 대한 풀텍스트 검색을 가능하게 하는 방법</h2><p>먼저 Elasticsearch 9.4에서 불가능했던 쿼리부터 시작하겠습니다. 이 쿼리는 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL 명령</a>을 사용합니다.</p><p>이 예시에서 요약에는 매핑이나 분석기 구성이 없습니다. 또한 어떤 역 인덱스와도 연관이 없습니다. 이 쿼리의 수명 동안에만 존재하지만, 이제는 검색할 수 있습니다. 두 가지 추가 요소가 이를 가능하게 합니다.</p><p>첫째, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions/match">MATCH</a>는 이제 첫 번째 인수로 매핑된 필드뿐만 아니라 모든 표현식을 지원합니다. 여기에는 EVAL에서 생성된 열과 인라인에서 사용되는 함수 결과도 포함됩니다. 또한 원본 문서에서 직접 불러온 매핑되지 않은 필드도 포함됩니다. 뿐만 아니라, MATCH에서 일반적으로 허용되는 모든 데이터 타입이 이 새로운 사용 사례에서 지원됩니다.</p><p>두 번째 부분은 새로운 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/type-conversion-functions/to_text">TO_TEXT</a> 함수로, 텍스트 형식의 출력을 생성하는 최초의 ES|QL 변환 함수입니다. 지금까지 텍스트 열은 인덱스에 매핑된 필드에서만 나올 수 있었으며, ES|QL 표현식에서 생성된 모든 스트링은 텍스트가 아닌 키워드 값이었습니다. MATCH는 두 값을 다르게 처리하기 때문에 이 구분이 중요합니다. 텍스트 값은 분석되며, 키워드 값은 정확히 비교되는데, 이는 인덱싱된 키워드 필드에 대한 MATCH 쿼리가 검색어 쿼리로 다시 작성되는 방식을 반영합니다. TO_TEXT(x)는 ES|QL에 <em>이 스트링을 풀텍스트로 처리</em>하도록 지시하는 방법입니다.</p><p>이 기능은 Elasticsearch 9.5의 기술 미리보기 버전으로 제공되므로 몇 가지 제한 사항이 있습니다.</p><ul><li><p>현재는 필터링만 지원합니다. 표현식에 대한 MATCH는 아직 관련성 점수에 반영되지 않으며, 점수에는 인덱싱된 필드에서의 매치만 영향을 미칩니다.</p></li><li><p>표현식을 매칭할 때 퍼지와 같은 쿼리 옵션은 아직 지원되지 않습니다.</p></li><li><p>런타임 텍스트는 표준 분석기를 사용하여 분석됩니다. 아직 구성할 수 없습니다.</p></li></ul><p>이러한 한계를 해결하기 위한 작업이 진행 중입니다.</p><h2>ES|QL에서 LIKE 또는 RLIKE 대신 풀텍스트 검색을 사용하는 이유</h2><p>ES|QL에는 이미 인덱스 없이 스트링을 검색하는 두 가지 방법이 있습니다. LIKE(와일드카드 패턴)와 RLIKE(정규 표현식)입니다. 두 함수 모두 모든 스트링 표현식에서 작동하므로 MATCH 함수가 추가로 제공하는 기능에 당연히 궁금증을 갖게 됩니다. 정답은 <a href="https://www.elastic.co/docs/manage-data/data-store/text-analysis">분석</a>입니다. 분석은 어간 추출이나 동의어 검색과 같은 기법을 사용하는 고급 검색 방식입니다. 또한 불용어 처리 기능도 사용합니다.</p><p>LIKE는 스트링을 구성하는 단어에 대한 이해 없이 단순히 부분 스트링을 매칭하는 연산입니다. 예를 들어, 여우(fox)에 관한 로그 메시지를 찾고 있다고 가정합시다.</p><p>대문자 표기로 인해 "Fox spotted near the henhouse"는 놓치게 되고, "Outfoxed by the competition"은 여우와 관련이 없습니다. 대문자 입력에 거짓 음성이 발생하고, 다른 단어들 안에 숨겨진 부분 스트링에 거짓 양성이 나타나 실패합니다.</p><p>정규 표현식은 대소문자 문제를 해결할 수 있으나, 단어 경계 문제는 빠르게 복잡해집니다. 예를 들면 다음과 같습니다.</p><p>하지만 이조차 아직 완벽하지 않습니다. 문장 끝에 있는 fox 다음에 ! 또는 ?가 뒤따르는 경우 놓치게 되고 탭, 따옴표 또는 괄호에 대해서는 언급이 없습니다. 수정할 때마다 패턴이 길어지고, 다음에 쿼리를 읽는 사람은 실제로 무슨 작업을 하는지 역으로 파악해야 합니다.</p><p>MATCH 문을 사용하면 쿼리와 값 모두를 <a href="https://www.elastic.co/docs/reference/text-analysis/analyzer-reference">분석기</a>를 통해 처리하여 텍스트를 소문자로 토큰화한 다음 각 검색어를 서로 비교하기 때문에 문제가 해결됩니다.</p><p>이 쿼리는 "The quick brown fox"와 "FOX spotted near the henhouse" 같은 값과 일치하지만, "Outfoxed by the competition"이나 "FOXTROT protocol enabled"와는 단어 주변의 구두점과 상관없이 일치하지 않습니다. 물론 이 모든 것은 MATCH(TO_TEXT(message), "brown fox")와 같은 다중 검색어 쿼리에서도 예상대로 작동합니다.</p><p><a href="https://www.elastic.co/docs/reference/text-analysis/analysis-lang-analyzer">36개의 전용 언어 분석기</a>를 활용할 수 있도록 하는 작업이 진행 중이며, 이 분석기들은 이전에 인덱싱되거나 매핑된 적이 없는 데이터에 대한 자연어 지원을 제공합니다.</p><h2>인덱싱되지 않고 매핑되지 않은 데이터에 대한 풀텍스트 검색 사용 사례</h2><p>위의 예시들은 <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">매핑된 필드</a>에서 계산된 값을 검색한 것입니다. 표현식에 ES|QL MATCH를 사용하는 더욱 흥미로운 사용 사례는 이전에는 전혀 검색할 수 없었던 데이터와 관련된 경우입니다. 몇 가지 예를 살펴보겠습니다.</p><h3>ES|QL에서 매핑을 추가하지 않고 매핑되지 않은 필드를 검색하는 방법</h3><p>때로는 상세한 스택 추적이나 원시 요청 페이로드와 같은 필드를 매핑에서 의도적으로 제외하는 경우가 있습니다. 디버그 블롭을 생략할 수도 있습니다. 이러한 필드 중 하나를 인덱싱하면 모든 문서에 디스크 및 힙 공간이 소모되므로 분기별로 한 번 정도만 조회할 필드에 대해서는 그럴 만한 가치가 없습니다.</p><p><a href="https://www.elastic.co/search-labs/blog/esql-unmapped-fields">매핑되지 않은 필드</a>는 쿼리에서 전혀 보이지 않았기 때문에 이 결정은 항상 최종적이었습니다. Elasticsearch 9.5에서는 SET unmapped_fields="load"를 사용하여 ES|QL이 원본 문서에서 매핑되지 않은 필드를 키워드로 직접 불러오도록 할 수 있습니다. 그런 다음 TO_TEXT로 감싸면 이제 풀텍스트 검색을 실행할 수 있습니다.</p><p>이 예제에서는 stack_trace가 매핑되지 않았습니다. 모든 값은 원본 문서에서 가져와 실시간으로 분석됩니다. 각 행별로 짝이 맞춰져 있습니다. 실제 작업이며, <a href="https://www.elastic.co/docs/manage-data/data-store/index-basics">역 인덱스</a> 조회만큼 빠르지는 않을 것입니다. 하지만 이제 인덱싱하지 않은 필드는 더 이상 검색 불가능하지 않습니다. 일상적인 경우에는 매핑 규모를 작게 유지하면서도, 중요한 시점에는 분기별로 필요한 질문에 답할 수 있습니다.</p><h3>재인덱싱 없이 키워드 필드에서 풀텍스트 검색 가능</h3><p>키워드 필드는 많은 기능을 수행할 수 있습니다. 정확한 매칭, 빠른 집계 및 정렬 기능을 제공하므로 많은 필드가 이 방식으로 매핑됩니다. 하지만 매핑은 데이터가 도착할 때 결정되기 때문에, 원래 의도했던 것과 다르게 데이터를 처리하고 싶은 상황이 발생하기 쉽습니다. 대시보드가 product_name을 기준으로 집계하므로 이를 키워드로 매핑되었는데, 1년 치 제품 데이터를 받은 후 누군가가 product_name 값 내에서 검색할 수 있기를 원할 수도 있습니다.</p><p>기존 해결책은 매핑을 텍스트로 변경하거나(또는 다중 필드 추가) 모든 것을 다시 인덱싱하는 것입니다. 이는 시간과 비용이 많이 소요될 뿐만 아니라, 많은 경우 사용자가 번거롭게 이 과정을 거치고 싶어 하지 않습니다. 새로운 해결책은 하나의 함수 호출입니다.</p><p>TO_TEXT는 키워드 값을 즉석에서 텍스트로 변환합니다. 따라서 MATCH는 키워드 값을 정확히 비교하지 않고 분석합니다. 이렇게 하면 매핑을 만들거나 소스 문서를 다시 인덱싱하지 않고도 키워드 필드를 쿼리할 수 있습니다. 검색이 일상적인 쿼리가 된다면 장기적으로는 필드를 텍스트로 인덱싱하는 것이 여전히 올바른 방법이지만, TO_TEXT를 사용하면 추가 작업 없이 지금 바로 답을 얻을 수 있습니다.</p><h3>서로 다른 매핑을 가진 인덱스 전반에서 동일한 필드 검색</h3><p><a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-multi-index">ES|QL은 여러 인덱스에 걸쳐 사용될 수 있으며</a>, 동일한 필드가 모든 인덱스에서 항상 동일한 형태를 가질 필요는 없습니다. 같은 필드가 서로 다른 인덱스에서 서로 다른 타입을 가질 때, ES|QL은 이를 유니언 타입으로 처리하며, 변환 함수가 충돌을 해결합니다. 올해 인덱싱 템플릿에서는 메시지 필드의 타입이 텍스트이지만 작년에는 키워드였던 경우를 예로 들어 보겠습니다.</p><p>쿼리 시점에 텍스트 인덱스든 키워드 인덱스든 모든 값이 분석됩니다. 이전 인덱스의 키워드 값은 다른 모든 것과 마찬가지로 토큰화되고 소문자로 변환되어 있어 “connection reset”은 어떤 인덱스에 있든 “Connection RESET by peer”를 찾습니다.</p><p>또 다른 흥미로운 경우는 필드가 한 인덱스에만 매핑되어 있지만 다른 인덱스에도 존재하는(또는 다른 인덱스에는 매핑되지 않은) 경우입니다.</p><p>여기서 주목할 만한 미묘한 차이가 있습니다. error_details가 logs-2026에 매핑되어 있고 logs-2025에는 매핑되지 않았다면, Elasticsearch는 이 쿼리를 <a href="https://lucene.apache.org/">Lucene</a>으로 푸시할 수 없습니다. 필드가 매핑되지 않은 인덱스에서는 오류나 경고 없이 일치하는 항목이 없다고 표시되기 때문입니다. 대신, 플래너는 해당 필드가 매핑되지 않았을 가능성이 있음을 감지하고 행의 출처와 관계없이 전체 MATCH 행을 하나씩 평가합니다. 어떤 인덱스에 필드가 매핑되었는지 알 필요가 없습니다. 쿼리가 그 질문에 대한 답을 제공합니다.</p><h2>ES|QL이 쿼리 시점에 역 인덱스 없이 텍스트를 분석하는 방법</h2><p>ES|QL은 표현식에 대해 MATCH를 계획할 때 쿼리 스트링을 먼저 검색어 집합으로 한 번 분석합니다. 각 행이 어떻게 평가되는지는 표현식의 유형에 따라 다릅니다.</p><p><strong>표현식 유형</strong></p><p><strong>처리</strong></p><p><strong>매칭 동작</strong></p><p>텍스트(TO_TEXT 사용)</p><p>분석기가 값을 소문자 검색어로 토큰화합니다.</p><p>토큰 간 비교. 토큰 중 하나라도 쿼리 검색어와 일치하면 해당 행이 일치하는 것으로 간주합니다(OR 의미론).</p><p>키워드, ip, 날짜, 숫자</p><p>분석 없이, 쿼리 상수를 네이티브 타입으로 한 번 변환합니다.</p><p>행별로 정확하게 비교합니다.</p><p>두 경로 모두 Lucene을 완전히 우회하고 값을 행 단위로 평가합니다. 비텍스트 경로는 해당 필드 타입에 대해 Lucene으로 매치 쿼리가 전달될 때 수행하는 작업을 정확히 반영하므로, 쿼리가 인덱스에 도달하든 상관없이 의미가 일관됩니다.</p><p>역 인덱스 조회는 인제스트 시점에 작업을 수행하며, 쿼리 시점에는 일치하지 않는 문서에 접근하지 않습니다. 런타임 MATCH는 쿼리 시점에 도달하는 모든 행에 대해 해당 분석을 수행합니다. 전자는 이미 작업이 이루어졌기 때문에 빠르며, 후자는 데이터가 전혀 인덱싱될 필요가 없어서 유연합니다.</p><h2>ES|QL 풀텍스트 검색의 다음 단계</h2><p>이 게시물의 모든 내용은 ES|QL에서의 검색이 미리 인덱싱한 항목뿐만 아니라 모든 항목에서 작동하도록 하려는 대규모 노력의 첫 번째 단계를 나타냅니다. 앞서 언급된 제한 사항들은 현재 적극적으로 개선 작업이 진행 중이며, 향후 로드맵은 더욱 발전할 것입니다.</p><ul><li><p><strong>점수.</strong> 런타임 매치는 _score에 기여하므로, 데이터가 인덱싱되지 않았더라도 관련성에 따라 정렬할 수 있습니다.</p></li><li><p><strong>MATCH_PHRASE</strong><strong> 표현식 사용.</strong> 이미 Elastic Cloud Serverless에서 이용할 수 있으며, Elastic Stack 9.6에 도입됩니다.</p></li><li><p><strong>구성 가능한 분석기.</strong> 표현식에 대한 MATCH 및 MATCH_PHRASE 사용 시 분석기 지원을 통해 쿼리 시점의 언어 분석기, 어간 추출, 동의어 기능을 가능하게 합니다.</p></li><li><p><strong>매치 옵션.</strong> 런타임 매치를 위한 퍼지와 연산자 옵션이 있습니다.</p></li><li><p><strong>벡터 검색.</strong> 행별 임베딩을 생성하고 런타임 dense_vector 표현식에 대해 k-최근접 이웃(kNN)을 실행하여 인덱싱되지 않은 데이터에도 의미 검색 기능을 제공합니다.</p></li></ul><h2>지금 표현식에 대한 ES|QL 풀텍스트 검색을 사용해 보세요</h2><p>지금 바로 런타임 검색을 사용해 보세요. 현재 새로운 ES|QL 기능이 먼저 도입되는 Elastic Cloud Serverless에서 이용 가능하며, Elasticsearch 9.5에서 기술 미리보기로 출시됩니다. <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions">검색 함수</a> 참조부터 시작하고, 현재 한계에 대한 <a href="https://www.elastic.co/docs/reference/query-languages/esql/limitations">ES|QL 제한 사항</a> 페이지를 확인하시기 바랍니다. 이번 버전은 기술 미리보기 버전이므로 여러분의 피드백을 기다리고 있습니다. 이전에 인덱싱되지 않은 내용을 검색하여 예상치 못한 결과가 나타난다면(긍정적이든 부정적이든) <a href="https://www.elastic.co/kr/community">언제든지 알려주시기 바랍니다</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/full-text-search-unindexed-data</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/full-text-search-unindexed-data</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[매핑]]></category>
    <dc:creator><![CDATA[Kevin Corcoran,Ioana Tagirta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a74e633d05585a8/6a730774b8c2e64c3ebe0fd4/image1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[1분 만에 프롬프트로 대시보드 완성, 비용은 5분의 1: Kibana의 AI 대시보드와 맞춤형 Vega-Lite 차트]]></title>
    <description><![CDATA[메트릭을 자연어로 설명하기만 하면, Kibana의 AI 채팅이 ES|QL 기반 대시보드와 Vega-Lite 차트를 생성합니다. 산점도부터 조건부 서식, 맞춤형 툴팁까지 모두 가능합니다.]]></description>
    <content:encoded><![CDATA[<p>Kibana의<a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/chat"> AI 채팅</a>은 이제 자연어 프롬프트만으로 1분 이내에 완전한<a href="https://www.elastic.co/docs/explore-analyze/visualize/esorql"> Elasticsearch Query Language 기반(ES|QL 기반)</a> 대시보드를 구축합니다. Elastic 9.5에서 이 기능은 정식 출시(GA) 단계로 전환됩니다(<a href="https://www.elastic.co/kr/search-labs/blog/ai-dashboard-generation-elastic-agent-kibana">9.4에서는 기술 미리보기 단계였습니다</a>). 이번 업데이트에는 실패한 쿼리를 재시도하는 오류 복구 기능, 계층형 모델 라우팅을 통한 ES|QL 생성 비용 5분의 1 절감, 그리고 대화형 필터 제어가 포함됩니다. 이번 릴리스에서는 자연어를 통한<a href="https://www.elastic.co/docs/explore-analyze/visualize/custom-visualizations-with-vega"> Vega-Lite</a> 차트 생성 기능도 추가되었습니다. 산점도, 박스플롯, 조건부 서식, 맞춤형 툴팁 등 기존에는 JSON으로 일일이 직접 코딩해야 했던 요소들까지 포함됩니다.</p><h2>Kibana의 AI 대시보드 생성 기능, 무엇이 새로워졌을까요</h2><p><strong>기능</strong></p><p><strong>기술 미리보기(9.4)</strong></p><p><strong>정식 출시(9.5)</strong></p><p>오류 처리</p><p>실패한 ES|QL 쿼리에 대한 재시도 없음</p><p>쿼리 검사 및 조정을 통해 최대 3회까지 자동 재시도</p><p>ES|QL 생성 비용</p><p>모든 쿼리가 기본 모델을 통해 라우팅됨</p><p>계층형 모델 라우팅, 최대 5분의 1 비용</p><p>기간 범위</p><p>고정된 기본 창</p><p>데이터 시간 분포에 기반한 자동 선택</p><p>필터 제어</p><p>지원되지 않음</p><p>가장 관련성 높은 필드에 대해 자동으로 추가됨</p><p>Vega-Lite 차트</p><p>지원되지 않음</p><p>산점도, 박스플롯, 조건부 서식, 맞춤형 툴팁을 포함한 자연어 기반 생성</p><p>차트 편집</p><p>지원되지 않음</p><p>자연어를 통해 기존 Vega-Lite 패널 편집</p><h3>AI 대시보드 생성을 위한 자동 오류 복구</h3><p>기술 미리보기 단계에서는 에이전트가 실패한 ES|QL 쿼리를 재시도하지 않았습니다. 9.5에서는 쿼리 오류를 감지하면 최대 3회까지 재시도하며, 포기하기 전에 각 오류를 검사하고 쿼리를 조정합니다. 실제로 이를 통해 대부분의 빈 패널 문제가 해결되며, 대시보드가 처음부터 제대로 렌더링됩니다.</p><h3>Elastic 9.5에서 AI 대시보드 생성 비용이 더 저렴해진 이유는 무엇일까요?</h3><p>대시보드 생성의 모든 단계가 동일한 수준의 추론을 필요로 하는 것은 아닙니다. 9.5에서는 ES|QL 생성이 기본적으로 더 가벼운 모델을 통해 처리되며, 필요한 경우에만 기본 모델로 대체됩니다. 사용 중인 <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/connectors">커넥터</a>가 Anthropic의 Claude Opus 4.8을 사용한다면, 모든 패널에 걸쳐 ES|QL 생성 비용이 5분의 1로 줄어든다는 의미입니다</p><h3>데이터에 기반한 자동 기간 범위 선택</h3><p>대시보드는 올바른 데이터 구간을 보여 줄 때만 유용합니다. 이제 에이전트는 사용자가 특정 기간 범위를 요청하지 않는 한, 쿼리하는 데이터에 적합한 기간 범위를 선택하기 위해 개선된 논리를 적용합니다. 고정된 창을 기본값으로 사용하는 대신, 데이터의 시간 분포를 고려해 그에 맞게 조정합니다. 실시간 인시던트라면 지난 1시간, 트렌드 분석이라면 지난 90일이 되는 식입니다.</p><h3>AI 생성 대시보드의 자동 필터 제어</h3><p>이제 대시보드 생성 기능이 제어, 즉 기본 쿼리를 수정하지 않고도 뷰어가 필드 값을 기준으로 대시보드를 좁혀볼 수 있는 대화형 필터를 지원합니다. 대시보드를 생성할 때 에이전트는 필터링하기에 가장 관련성 높은 필드에 대한 제어를 상단에 자동으로 추가합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a5520e66ae76001/6a719a218a155220ed6498e7/image3.png" alt="Kibana AI chat generating an ES|QL-backed host metrics dashboard with automatic filter controls in 71 seconds" /><h2>자연어로 만드는 Vega-Lite 차트: 기본 제공 범위를 넘어선 차트 유형과 서식</h2><p><a href="https://vega.github.io/vega/">Vega</a>와 <a href="https://vega.github.io/vega-lite/examples/">Vega-Lite</a>는 Kibana에서 다양한 차트 유형과 맞춤형 옵션을 지원합니다. 9.5부터는 직접 코드를 작성하지 않고도 일반 언어만으로 이를 만들 수 있습니다. </p><h3>산점도, 박스플롯 및 기타 다양한 Vega-Lite 차트 유형</h3><p>산점도, 박스플롯 차트, 패싯 처리된 소형 다중 차트, 버블 차트, 그리고 (히스토그램과 히트맵을 결합하는 등의) 합성 차트를 비롯한 다양한 차트 유형이 <a href="https://vega.github.io/vega-lite/examples/">Vega-Lite</a>에서 지원됩니다. 예를 들어 <em>응답 시간 대 요청 크기의 산점도를 서비스 이름별로 색상을 구분해서 보여줘</em>와 같은 프롬프트를 입력하면, 올바른 데이터 매핑이 적용된 Vega-Lite 패널이 생성됩니다. 이때 차트는 Kibana의 기본 색상 팔레트를 사용해 대시보드의 나머지 요소와 자연스럽게 어우러집니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1651fe7ac903d47/6a719a49f124649f746fc1b4/image5.png" alt="Kibana dashboard with four Vega-Lite charts: box plot, bubble chart, faceted small multiples, and heatmap." /><h3>표준 차트에 조건부 서식, 맞춤형 툴팁, 레이블 적용</h3><p>막대, 선, 영역 차트처럼 이미 대시보드에 기본으로 내장된 차트 유형이라 하더라도, 기본 기능만으로는 부족한 세밀한 제어가 필요할 때가 있습니다. 이럴 때 채팅을 통한 Vega-Lite가 그 부족한 부분을 채워 줍니다. 몇 가지 예는 다음과 같습니다.</p><ul><li><p><strong>조건부 색상 서식:</strong> 임계값을 초과하는 데이터 요소를 다른 색상으로 표시합니다. 예를 들어 특정 메트릭이 서비스 수준 목표(SLO)를 초과해 급증할 때, 선이나 막대의 데이터 요소를 빨간색으로 바꾸는 식입니다. 에이전트에게 <em>내 라인 차트에서 500ms를 초과하는 포인트는 모두 빨간색으로 바꿔줘</em>와 같이 요청해 보세요. </p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1211784e9033557/6a719a6ab966e1736163d7d1/image1.png" alt="Vega-Lite line and bar charts in Kibana with conditional colour formatting showing data points above a threshold in red" /><p></p></li><li><p><strong>사용자 지정 표시 및 레이블:</strong> 데이터 포인트에 이모티콘, 기호 또는 텍스트 레이블을 추가하여 한눈에 상태를 파악할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3c59c3748484bc1/6a719aa02888394fdc07bac1/image2.png" alt="Lite horizontal bar chart in Kibana with emoji flag labels and custom tooltip showing requests by country" /><p></p></li><li><p><strong>맞춤형 툴팁:</strong> 차트의 축에는 포함되지 않는 추가 메트릭, 맥락 정보, 계산된 값 등으로 마우스 오버 시 표시되는 정보를 더욱 풍부하게 만듭니다. <em>각 막대의 전체 레코드 수와 비율(%)을 보여 주는 툴팁을 추가해줘</em>와 같이 요청해 보세요.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43f241f4382b5b90/6a719ab7ded0cf3367f49275/image4.png" alt="Vega-Lite stacked bar chart in Kibana with custom tooltip showing total records and percentage of total by extension" /><p></p></li></ul><p>이 기능은 기존 Vega 차트를 편집할 때도 동일하게 작동합니다. 색상 스케일 변경, 축 조정, 마크 유형 전환처럼 Vega-Lite 패널에 손질이 필요할 때, JSON 코드를 직접 파고들 필요 없이 채팅에서 원하는 변경 사항을 설명하기만 하면 됩니다.</p><h2>Kibana에서 자연어 기반 Vega-Lite 생성 기능을 구축한 방법</h2><p>문장 하나로 Vega-Lite 차트를 생성하는 것은 <em>모델에게 JSON을 달라고</em> 한 번에 요청하는 단순한 프롬프트 작업이 아닙니다. 저희는 자연어로 표현된 의도를 검증되고 데이터에 기반한 차트로 전환하는 소규모 에이전틱 파이프라인을 구축했습니다.</p><p>요청이 들어오면 에이전트는 먼저 Vega-Lite가 적합한지 판단합니다. Vega-Lite 요청인 경우, Elasticsearch에 대한 실제 ES|QL 쿼리를 바탕으로 시각화를 구성한 다음, 모델을 사용해 Vega-Lite 코드를 생성합니다. 렌더링 전에 결과는 정규화 계층을 거치며, 여기서 스키마를 수정하고 표준 쿼리를 연결합니다. 또한 렌더링 안전성을 위한 변환도 적용합니다. </p><p>몇 가지 설계 선택이 이 워크플로우의 신뢰성을 뒷받침합니다.</p><ul><li><p><strong>유형이 지정된 도구 호출</strong>: 차트 생성은 대화창에 자유 형식의 Vega-Lite 코드를 붙여넣는 방식이 아니라, 구조화된 도구 호출로 이루어집니다.</p></li><li><p><strong>제약된 생성</strong>: 모델은 정의된 스키마 내에서 Vega-Lite 코드를 생성하므로, 출력 결과가 더 예측 가능하고 검증하기 쉬워집니다.</p></li><li><p><strong>엄선된 예시:</strong> 패싯 처리, 레이어드 마크, 히트맵과 같은 구조적 패턴이 기본 데이터를 그대로 복사하지 않으면서도 지침을 제공합니다.</p></li><li><p><strong>실행 및 검증 루프</strong>: 쿼리는 차트 작성 전에 실행되며, 검증에 실패하면 ES|QL 생성을 위한 수정 재시도가 트리거됩니다.</p></li></ul><h2>Kibana에서 AI 대시보드 생성과 Vega-Lite 차트 사용해 보기</h2><p>자연어 기반 대시보드 생성과 Vega-Lite 차트를 사용해 보려면, <strong>Elastic 9.5</strong>로 업그레이드하거나(또는 <a href="https://cloud.elastic.co/registration">무료 체험 시작하기</a>), Kibana에서 <strong>채팅</strong>을 열어 보세요. 그런 다음 여러분의 데이터로 대시보드를 만들어달라고 요청해 보세요. Vega-Lite의 경우, 산점도나 버블 차트와 같이 평소 만들어보고 싶었지만 시도하지 못했던 차트 유형을 요청해 보세요. 결과가 원하는 대로 나오지 않으면, 에이전트에게 무엇을 바꿔야 할지 알려주세요. 에이전트가 여러분과 함께 반복하며 수정해 나갑니다.</p><p>이 경우 엔터프라이즈 라이선스가 필요합니다. <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/chat#get-started">시작해 보세요</a>.</p><p><em>이 게시물에서 설명된 모든 기능이나 성능의 출시와 일정은 Elastic의 단독 재량에 따라 결정됩니다. 현재 제공되지 않는 기능이나 성능은 예정된 시간에 출시되지 않을 수도 있으며 아예 제공되지 않을 수도 있습니다.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-dashboards-kibana-vega-lite</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-dashboards-kibana-vega-lite</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Marta Bondyra,Teresa Alvarez Soler]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54406ad0378bc5fc/6a7199ffed03ccee0dac9d7c/image6.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LINQ to Elasticsearch ES|QL: C# 작성, Elasticsearch 쿼리]]></title>
    <description><![CDATA[Elasticsearch .NET 클라이언트의 새로운 LINQ to Elasticsearch ES|QL 제공자를 살펴보세요. 이 제공자를 사용하면 C# 코드를 작성하여 ES|QL 쿼리로 자동 변환할 수 있습니다.]]></description>
    <content:encoded><![CDATA[<p><strong>v9.3.4</strong> 및 <strong>v8.19.18</strong>부터 Elasticsearch .NET 클라이언트에는 런타임에 C# LINQ 표현식을 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">Elasticsearch Query Language(ES|QL)</a> 쿼리로 변환하는 <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Language Integrated Query (LINQ) </a>제공자가 포함되어 있습니다. ES|QL 스트링을 직접 작성하는 대신 <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> 및 기타 표준 연산자를 사용하여 쿼리를 작성합니다. 제공자는 변환, 매개변수화, 결과 역직렬화를 처리하며, 결과 세트 크기와 관계없이 메모리 사용량을 일정하게 유지하는 행별 스트리밍도 포함됩니다.</p><h2>첫 번째 쿼리</h2><p>먼저 Elasticsearch 인덱스에 맵핑되는 일반 CLR 객체(POCO)를 정의합니다. 속성 이름은 표준 <code>System.Text.Json</code> 속성(예: <code>[JsonPropertyName]</code>) 또는 구성된 <code>JsonNamingPolicy</code>을(를) 통해 ES|QL 열 이름으로 확인됩니다. 클라이언트의 나머지 부분에 적용되는 <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">소스 직렬화</a> 규칙이 여기에도 동일하게 적용됩니다.</p>using System.Text.Json.Serialization;

public class Product
{
    [JsonPropertyName("product_id")]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Brand { get; set; }

    [JsonPropertyName("price_usd")]
    public double Price { get; set; }

    [JsonPropertyName("in_stock")]
    public bool InStock { get; set; }
}<p>유형이 지정되면 쿼리는 다음과 같습니다.</p>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync&lt;Product&gt;(q =&gt; q
    .From("products")
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}<p>제공자는 이를 다음과 같은 ES|QL로 변환합니다.</p><p>참고할 만한 몇 가지 세부 사항은 다음과 같습니다.</p><ul><li><p><strong>속성 이름 확인:</strong> <code>p.Price</code>은(는) <code>[JsonPropertyName]</code> 속성 때문에 <code>price_usd</code>이(가) 되고, <code>p.Brand</code>은(는) 기본 camelCase 명명 정책에 따라 <code>brand</code>이(가) 됩니다.</p></li><li><p><strong>매개변수 캡처:</strong> C# 변수 <code>minPrice</code> 및 <code>brand</code> 은(는) 명명된 매개변수(<code>?minPrice</code>, <code>?brand</code>)(으)로 캡처됩니다. 이러한 변수는 JSON 페이로드에서 쿼리 스트링과 별도로 전송되므로 인젝션을 방지하고 서버 측 쿼리 계획 캐싱을 활성화합니다.</p></li><li><p><strong>스트리밍:</strong> <code>QueryAsync&lt;T&gt;</code> 은(는)<code>IAsyncEnumerable&lt;T&gt;</code>을(를) 반환합니다. 행은 Elasticsearch에서 도착하는 대로 한 번에 하나씩 구체화됩니다.</p></li></ul><p>다음과 같이 생성된 쿼리와 해당 매개변수를 실행하지 않고도 확인할 수 있습니다.</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Where(p =&gt; p.InStock &amp;&amp; p.Price &gt;= minPrice &amp;&amp; p.Brand == brand)
    .OrderByDescending(p =&gt; p.Price)
    .Take(10);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE (in_stock == true AND price_usd &gt;= 100) | SORT price_usd DESC | LIMIT 10

Console.WriteLine(query.ToEsqlString(inlineParameters: false));
// FROM products | WHERE (in_stock == true AND price_usd &gt;= ?minPrice AND brand == ?brand) | SORT price_usd DESC | LIMIT 10

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }<h2>작동 원리: LINQ 핵심 개념 되짚어보기</h2><p>LINQ 제공자를 가능하게 하는 메커니즘은 <code>IEnumerable&lt;T&gt;</code>와(과) <code>IQueryable&lt;T&gt;</code>의 구분입니다.</p><p><code>IEnumerable&lt;T&gt;</code> 에 대해<code>.Where(p =&gt; p.Price &gt; 100)</code> 을(를) 호출하면 lambda는 런타임이 프로세스 중에 실행하는 일반 델리게이트인 <code>Func&lt;Product, bool&gt;</code> (으)로 컴파일됩니다. 이것이 LINQ-to-Objects입니다.</p><p><code>IQueryable&lt;T&gt;</code>에서 동일한 메서드를 호출하면 C# 컴파일러는 lambda를<code>Expression&lt;Func&lt;Product, bool&gt;&gt;</code> (으)로 래핑합니다. 이는 실행 가능한 형태가 아닌 코드의 <em>구조</em>를 나타내는 데이터 구조입니다. 표현식 트리는 런타임에 검사, 분석 및 다른 언어로 변환될 수 있습니다.</p>// IEnumerable: the lambda is a compiled delegate
IEnumerable&lt;Product&gt; local = products.Where(p =&gt; p.Price &gt; 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable&lt;Product&gt; remote = queryable.Where(p =&gt; p.Price &gt; 100);<p><code>IQueryProvider</code> 인터페이스는 확장 지점입니다. 모든 제공자는 <code>CreateQuery&lt;T&gt;</code> 및 <code>Execute&lt;T&gt;</code>을(를) 구현하여 이러한 표현식 트리를 대상 언어로 변환할 수 있습니다. Entity Framework는 이를 사용하여 SQL을 출력합니다. LINQ to ES|QL 제공자는 이를 사용하여 ES|QL을 출력합니다.</p><p>위 쿼리의 표현식 트리는 다음과 같습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt521838e8b9c36649/6a1705b1839dfa5f40dcfdfe/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" alt="예제 쿼리의 표현식 트리입니다." /><p><em>예제 쿼리의 표현식 트리입니다.</em></p><p><code>Take</code> 은(는)<code>OrderByDescending</code> 을(를) 감싸고, 이는 <code>Where</code> 을(를) 감싸고, 이는 <code>From</code> 을(를) 감싸고, 이는 <code>EsqlQueryable&lt;Product&gt;</code> 상수를 감싸는 식으로 트리가 안쪽에서 바깥쪽으로 중첩됩니다. <code>Where</code> 술어는 그 자체로 <code>&amp;&amp;</code>, <code>&gt;=</code>, <code>==</code> 연산자에 대한 <code>BinaryExpression</code> 노드의 하위 트리이며, <code>MemberExpression</code> 은(는) 속성 액세스 및 <code>minPrice</code> 및 <code>brand</code> 변수에 대한 클로저 캡처를 위한 리프입니다. 이는 제공자가 최종 ES|QL을 생성하기 위해 거치는 데이터 구조입니다.</p><h2>작동 원리 살펴보기: 변환 파이프라인</h2><p>LINQ 표현식에서 쿼리 결과에 이르는 경로는 다음과 같이 6단계 파이프라인을 따릅니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt930670a505dd61ea/6a1705b3b339d58a54769ecf/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" alt="변환 파이프라인 개요." /><p><em>변환 파이프라인 개요.</em></p><h3>1. 표현식 트리 캡처</h3><p><code>.Where()</code>, <code>.OrderBy()</code>, <code>.Take()</code> 및 기타 연산자를 <code>IQueryable&lt;T&gt;</code>에 연결하면 표준 LINQ 인프라가 표현식 트리를 구축합니다. <code>EsqlQueryable&lt;T&gt;</code>은(는) <code>IQueryable&lt;T&gt;</code>을(를) 구현하고 <code>EsqlQueryProvider</code>에 위임합니다.</p><h3>2. 번역</h3><p>쿼리가 실행될 때(열거, <code>ToList()</code> 호출 또는 <code>await foreach)</code> 사용), <code>EsqlExpressionVisitor</code>은(는) 표현식 트리를 안쪽에서 바깥쪽으로 탐색합니다. 각 LINQ 메서드 호출을 전문 방문자에게 전달합니다.</p><p>방문자</p><p>번역합니다</p><p>안으로</p><p>WhereClauseVisitor</p><p>.Where(predicate)</p><p>WHERE condition</p><p>SelectProjectionVisitor</p><p>.Select(선택기)</p><p>EVAL + KEEP + RENAME</p><p>GroupByVisitor</p><p>.GroupBy().Select()</p><p>STATS ... BY</p><p>OrderByVisitor</p><p>.OrderBy() / .ThenBy()</p><p>SORT 필드 [ASC\|DESC]</p><p>EsqlFunctionTranslator</p><p>EsqlFunctions.*, Math.*, 스트링 메서드</p><p>80개 이상의 ES|QL 함수</p><p>변환 과정에서 표현식에서 참조되는 C# 변수는 명명된 매개변수로 캡처됩니다.</p><h3>3. 쿼리 모델</h3><p>방문자는 스트링을 직접 생성하지 않습니다. 대신 <code>QueryCommand</code> 객체, 즉 불변의 중간 표현을 생성합니다. <code>FromCommand</code>, <code>WhereCommand</code>, <code>SortCommand</code>, <code>LimitCommand</code>은(는) 각각 하나의 ES|QL 처리 명령을 나타냅니다. 이들은 <code>EsqlQuery</code> 모델로 수집됩니다.</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt788c9936976f2f62/6a1705b50e2e4910da419ff0/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" alt="쿼리 모델 및 명령 패턴입니다." /><p><em>쿼리 모델 및 명령 패턴입니다.</em></p><p>이 중간 모델은 표현식 트리 및 출력 형식에서 모두 분리되어 있습니다. 형식을 지정하기 전에 검사하거나, 가로채거나(<code>IEsqlQueryInterceptor</code>을(를) 통해), 수정할 수 있습니다.</p><h3>4. 형식 지정</h3><p><code>EsqlFormatter</code> 각 <code>QueryCommand</code>을(를) 순서대로 방문하여 최종 ES|QL 스트링을 생성합니다. 각 명령은 ES|QL이 처리 명령을 연결하는 데 사용하는 파이프(|) 연산자로 구분되어 한 줄로 표시됩니다. 특수 문자가 포함된 식별자는 백틱으로 자동 이스케이프 처리됩니다.</p><h3>5. 실행</h3><p>형식화된 ES|QL 스트링과 캡처된 매개변수는 JSON 페이로드로 Elasticsearch의 <code>/_query</code> 엔드포인트로 전송됩니다. <code>IEsqlQueryExecutor</code> 인터페이스는 계층형 패키지 아키텍처가 적용되는 전송 계층을 추상화합니다.</p><h3>6. 구체화</h3><p><code>EsqlResponseReader</code> 전체 결과 세트를 메모리에 버퍼링하지 않고 JSON 응답을 스트리밍합니다. 쿼리당 한 번씩 미리 계산되는 <code>ColumnLayout</code> 트리는 플랫 ES|QL 열 이름(예: <code>address.street</code>, <code>address.city</code>)을(를) 중첩된 POCO 속성에 맵핑합니다. 각 행은 <code>T</code> 인스턴스로 조립되어 <code>IEnumerable&lt;T&gt;</code> 또는 <code>IAsyncEnumerable&lt;T&gt;</code>을(를) 통해 한 번에 하나씩 제공됩니다.</p><h2>계층 아키텍처</h2><p>LINQ to ES|QL 기능은 다음과 같이 세 개의 패키지로 나뉩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt662bd0dd8861b6b6/6a1705b7a929cf7086ae08a2/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" alt="패키지 아키텍처." /><p><em>패키지 아키텍처.</em>
<a href="https://www.nuget.org/packages/Elastic.Esql"><strong><code>Elastic.Esql</code></strong></a> 은(는) 순수 변환 엔진입니다. HTTP 종속성이 전혀 없으며 표현식 방문자, 쿼리 모델, 포맷터 및 응답 판독기를 포함합니다. Elasticsearch 연결 없이 독립적으로 사용하여 ES|QL 쿼리를 구축하고 검사할 수 있어 테스트, 쿼리 로깅 또는 자체 실행 계층을 구축하는 데 유용합니다.</p>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable&lt;Product&gt;(provider)
    .From("products")
    .Where(p =&gt; p.InStock)
    .OrderByDescending(p =&gt; p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC<p><a href="https://www.nuget.org/packages/Elastic.Clients.Esql"><strong><code>Elastic.Clients.Esql</code></strong></a> 경량 독립형 ES|QL 클라이언트입니다. <code>Elastic.Transport</code>을(를) 통해 <code>Elastic.Esql</code> 위에 HTTP 실행 기능을 추가합니다. 애플리케이션에 ES|QL만 필요하고 다른 Elasticsearch API는 필요하지 않은 경우, 이것이 최소한의 종속성 옵션입니다.</p><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch"><strong><code>Elastic.Clients.Elasticsearch</code></strong></a> 완전한 Elasticsearch .NET 클라이언트입니다. 이 또한 <code>Elastic.Esql</code>을(를) 기반으로 하며 <code>client.Esql</code> 네임스페이스를 통해 LINQ 제공자를 노출합니다. 대부분의 애플리케이션에 권장되는 진입점입니다.</p><p>두 실행 계층 패키지 모두 변환과 전송을 연결하는 전략 인터페이스인 <code>IEsqlQueryExecutor</code>의 자체 구현을 제공합니다.</p><p>세 패키지 모두 소스에서 생성된 <code>JsonSerializerContext</code>와(과) 함께 사용할 경우 Native AOT와 호환됩니다. 전체 클라이언트에 대한 자세한 내용은 <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization#native-aot">Native AOT 설명서</a>를 확인하세요.</p><h2>기본을 넘어서</h2><p>위의 예에서는 필터링, 정렬 및 페이지 매김을 다뤘습니다. 공급자는 더 광범위한 작업을 지원합니다.</p><h3>집계</h3><p><code>GroupBy</code><code>Select</code>의 집계 함수와 결합하면 ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS ... BY</code></a>(으)로 변환됩니다.</p>var stats = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .GroupBy(p =&gt; p.Brand)
    .Select(g =&gt; new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p =&gt; p.Price),
        MaxPrice = g.Max(p =&gt; p.Price)
    }));

// -&gt; FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand<h3>프로젝션</h3><p><code>Select</code>익명 유형은 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval"><code>EVAL</code></a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/keep"><code>KEEP</code></a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/rename"><code>RENAME</code></a> 명령을 생성합니다.</p>var query = client.Esql.CreateQuery&lt;Product&gt;()
    .Select(p =&gt; new { ProductName = p.Name, p.Price, p.InStock });

// -&gt; FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName<h3>풍부한 함수 라이브러리</h3><p><code>EsqlFunctions</code> 클래스를 통해 날짜/시간, 스트링, 수학, IP, 패턴 매칭 및 스코어링을 포함한 80개 이상의 ES|QL 함수를 사용할 수 있습니다. 다음과 같이 표준 <code>Math.*</code> 및 <code>string.*</code> 메서드도 변환됩니다.</p>.Where(p =&gt; p.Name.Contains("Pro"))       // -&gt; WHERE name LIKE "*Pro*"
.Where(p =&gt; EsqlFunctions.CidrMatch(      // -&gt; WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))<h3>조회 조인</h3><p>교차 인덱스 조회는 ES|QL <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join"><code>LOOKUP JOIN</code></a>(으)로 변환됩니다.</p>var enriched = client.Esql.Query&lt;Product, object&gt;(q =&gt; q
    .LookupJoin&lt;Product, CategoryLookup, string, object&gt;(
        "category-lookup-index",
        product =&gt; product.Id,
        category =&gt; category.CategoryId,
        (product, category) =&gt; new { product.Name, category!.CategoryLabel }));<h3>원시 ES|QL 이스케이프 해치</h3><p>LINQ 제공자가 아직 지원하지 않는 ES|QL 기능의 경우 다음과 같이 원시 조각을 추가할 수 있습니다.</p>var results = client.Esql.Query&lt;Product&gt;(q =&gt; q
    .Where(p =&gt; p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));<h3>서버 측 비동기 쿼리</h3><p>장기 실행 쿼리의 경우 다음과 같이 서버에서 백그라운드 처리를 위해 제출하세요.</p>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync&lt;Product&gt;(
    q =&gt; q.Where(p =&gt; p.InStock),
    asyncQueryOptions: new EsqlAsyncQueryOptions
    {
        WaitForCompletionTimeout = TimeSpan.FromSeconds(5),
        KeepAlive = TimeSpan.FromMinutes(10)
    });

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>서버 측 비동기 쿼리는 일반적인 시간 초과 임계값을 초과할 수 있는 장기 실행 분석 쿼리/대규모 데이터 세트 처리에 특히 유용합니다. 또한 엄격한 HTTP 시간 초과를 적용하는 로드 밸런서, API 게이트웨이 또는 프록시가 있는 시간 초과에 민감한 환경에서도 유용합니다. 비동기 쿼리는 제출과 결과 검색을 분리하여 연결 끊김을 방지합니다.</p><h2>시작하기</h2><p>LINQ to ES|QL은 다음 버전부터 사용 가능합니다.</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x 브랜치)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (8.x 브랜치)</p></li></ul><p>NuGet에서 설치:</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>진입점은 <code>client.Esql</code> 에 있습니다.</p><p>메서드</p><p>반환</p><p>사용 사례</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>동기식 실행</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>비동기 스트리밍</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>고급 구성 및 검사</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>장기 실행 서버 측 쿼리</p><p>쿼리 옵션, 다중 필드 액세스, 중첩 객체, 다중 값 필드 처리 등 전체 기능에 대한 참조는 <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">LINQ to ES|QL 설명서</a>를 확인하세요.</p><h2>결론</h2><p>LINQ to ES|QL은 C# LINQ의 모든 표현력을 Elasticsearch의 ES|QL 쿼리 언어로 제공하여 쿼리 스트링을 직접 작성하지 않고도 강력한 형식의 조합 가능한 쿼리를 작성할 수 있도록 해줍니다. 자동 매개변수 캡처, 스트리밍 구체화, 그리고 독립 실행형 변환부터 전체 Elasticsearch 클라이언트까지 확장 가능한 계층형 패키지 아키텍처를 통해 모든 규모의 .NET 애플리케이션에 자연스럽게 통합됩니다. 최신 클라이언트를 설치하고 LINQ 표현식에서 인덱스를 지정하기만 하면 나머지는 제공자가 처리합니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[벡터 데이터베이스]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[스위스식 해시 테이블을 사용한 더 빠른 ES|QL 통계 처리]]></title>
    <description><![CDATA[스위스식 해싱과 SIMD 친화적인 설계가 Elasticsearch 쿼리 언어(ES|QL)에서 일관되고 측정 가능한 속도 향상을 실현하는 방법.]]></description>
    <content:encoded><![CDATA[<p>최근 Elasticsearch의 해시 테이블 구현의 핵심적인 부분을 스위스식 설계로 교체하였으며, 균일하고 카디널리티가 높은 워크로드에서 빌드 및 반복 시간이 최대 2~3배 빨라지는 결과를 확인했습니다. 결과적으로 Elasticsearch 쿼리 언어(ES|QL) 통계 및 분석 작업에서 더 낮은 지연 시간, 더 나은 처리량 및 더 예측 가능한 성능을 구현할 수 있게 되었습니다.</p><h2>이것이 중요한 이유</h2><p>대부분의 일반적인 분석 워크플로우는 결국 데이터를 그룹화하는 작업으로 귀결됩니다. 호스팅당 평균 바이트 계산, 사용자당 이벤트 계산 또는 다양한 차원에 걸친 메트릭 집계 등의 작업을 수행하더라도 핵심 작업은 동일합니다. 키를 그룹에 맵핑하고 실행 중인 집계를 업데이트하는 것입니다.</p><p>작은 규모에서는 거의 모든 합리적인 해시 테이블이 잘 작동합니다. 대규모 환경(수억 건의 문서와 수백만 개의 개별 그룹)에서는 아주 작은 세부 사항들이 중요해지기 시작합니다. 로드 팩터, 탐색 전략, 메모리 레이아웃 및 캐시 동작이 선형적인 성능 향상을 이뤄내느냐, 혹은 캐시 미스의 장벽에 가로막히느냐를 결정짓는 차이를 만듭니다.</p><p>Elasticsearch는 수년간 이러한 워크로드를 지원해 왔지만, 핵심 알고리즘을 현대화할 수 있는 기회를 항상 모색하고 있습니다. 따라서 스위스 테이블에서 영감을 받은 새로운 접근 방식을 평가하고 이를 ES|QL의 통계 계산 방식에 적용했습니다.</p><h2>스위스 테이블의 본질은 무엇인가요?</h2><p>스위스 테이블은 Google의 SwissTable로 대중화되고 나중에 Abseil 및 기타 라이브러리에서 채택된 현대적인 해시 테이블의 한 계열입니다.</p><p>기존의 해시 테이블은 포인터를 쫓아가거나 키를 로드하는 데 많은 시간을 소비하지만, 그 결과가 일치하지 않는 경우가 많습니다. 스위스 테이블의 주요 특징은 키와 값과는 별도로 저장되는 <em>제어 바이트</em>라는 작은 캐시 상주 배열 구조를 사용하여 대부분의 탐색을 거부함으로써 메모리 트래픽을 획기적으로 줄일 수 있다는 점입니다.</p><p>각 제어 바이트는 하나의 슬롯을 나타내며, 저희의 경우에는 두 가지 정보를 인코딩합니다. 바로 해당 슬롯이 비어 있는지 여부와 해시값에서 추출한 짧은 지문입니다. 이러한 제어 바이트는 일반적으로 16개의 그룹으로 메모리에 연속적으로 배치되어 <a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">단일 명령, 다중 데이터</a>(SIMD) 처리에 적합합니다.</p><p>스위스 테이블은 한 번에 하나의 슬롯을 탐색하는 대신 벡터 명령어를 사용하여 전체 제어 바이트 블록을 스캔합니다. CPU는 단 한 번의 연산으로 새로 들어온 키의 지문을 16개의 슬롯과 비교하고 비어 있는 항목들을 걸러냅니다. 이 빠른 경로에서 살아남은 소수의 후보만 실제 키를 로드하고 비교해야 합니다.</p><p>이 설계는 약간의 추가 메타데이터를 사용하는 대신, 훨씬 뛰어난 캐시 지역성을 확보하고 무작위 로드 횟수를 획기적으로 줄입니다. 테이블이 커지고 탐색 체인이 길어질수록 이러한 속성의 가치는 점점 더 중요해집니다.</p><h2>핵심에 자리잡은 SIMD</h2><p>이 모든 것의 진정한 주인공은 SIMD입니다.</p><p>제어 바이트는 단순히 컴팩트할 뿐만 아니라 벡터 명령어로 처리되도록 명시적으로 설계되었습니다. 단일 SIMD 비교를 통해 한 번에 16개의 지문을 확인할 수 있어, 일반적으로 루프로 처리되는 작업을 몇 가지 광범위한 작업으로 전환할 수 있습니다. 그 예는 다음과 같습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f710e87dd749ab3/6a170cc46234e052dadb1a49/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" alt="Elasticsearch의 핵심에 자리잡은 SIMD" /><p>실제로 이는 다음과 같은 의미를 지닙니다.</p><ul><li><p>분기 명령 감소.</p></li><li><p>탐색 체인 단축.</p></li><li><p>키 및 값 메모리로부터의 데이터 로드 횟수 감소.</p></li><li><p>CPU 실행 유닛의 활용도 대폭 향상.</p></li></ul><p>대부분의 조회 작업은 제어 바이트 스캔 단계에서 마무리됩니다. 다음 단계로 넘어가는 경우, 남은 작업은 매우 집중적이며 예측 가능합니다. 최신 CPU가 잘 처리하는 워크로드는 바로 이런 종류의 워크로드입니다.</p><h2>SIMD의 작동 원리 살펴보기</h2><p>애플리케이션의 내부 작동 원리를 살펴보고 싶은 독자를 위해, 테이블에 새 키를 삽입할 때 어떤 일이 일어나는지 알려드리겠습니다. 128비트 벡터를 사용하는 파나마 벡터 API를 활용하여 16개의 제어 바이트를 병렬로 처리합니다.</p><p>다음 스니펫은 Intel Rocket Lake에서 AVX-512로 생성된 코드를 보여 줍니다. 명령어는 해당 환경을 반영하지만, 설계는 AVX-512에 의존하지 않습니다. 동일한 고수준 벡터 연산은 동등한 명령어(예: AVX2, SSE 또는 NEON)를 사용하여 다른 플랫폼에서도 실행됩니다.</p>; Load 16 control bytes from the control block
vmovdqu xmm0, XMMWORD PTR [r9+r10*1+0x10]

; Broadcast the 7-bit fingerprint of the new key across the vector
vpbroadcastb xmm1, r11d

; Compare all 16 control bytes to the new fingerprint
vpcmpeqb k7, xmm0, xmm1
kmovq rbx, k7

; Check if any matches were found
test rbx, rbx
jne &lt;handle_match&gt;<p>각 명령어는 삽입 과정에서 명확한 역할을 수행합니다.</p><ul><li><p><code>vmovdqu</code>: 128비트 <code>xmm0</code> 레지스터에 연속된 16개의 제어 바이트를 로드합니다.</p></li><li><p><code>vpbroadcastb</code>새로운 키의 7비트 지문을 <code>xmm1</code> 레지스터의 모든 레인에 복제합니다.</p></li><li><p><code>vpcmpeqb</code>: 각 제어 바이트를 브로드캐스트된 지문과 비교하여 일치할 가능성이 있는 마스크를 생성합니다.</p></li><li><p><code>kmovq</code> + <code>test</code>: 마스크를 범용 레지스터로 이동하고 일치하는 항목이 있는지 빠르게 확인합니다.</p></li></ul><p>벤치마킹 결과에 따르면 더 넓은 레지스터를 사용하여 32바이트 또는 64바이트로 확장해도 측정 가능한 성능 이점을 얻을 수 없었기 때문에, 최종적으로 한 번에 16개의 제어 바이트로 구성된 프로빙 그룹을 사용하기로 결정했습니다.</p><h2>ES|QL과의 통합</h2><p>Elasticsearch에서 스위스식 해싱을 채택한 것은 단순히 기존 방식을 대체하는 것이 아니었습니다. ES|QL은 메모리 회계, 안정성, 및 나머지 연산 엔진과의 통합 측면에서 매우 엄격한 요구 사항을 가지고 있습니다.</p><p>새로운 해시 테이블을 페이지 리사이클러 및 서킷 브레이커 회계 기능을 포함한 Elasticsearch의 메모리 관리 기능과 긴밀하게 통합하여 할당이 가시적이고 제한된 범위 내에서 이루어지도록 했습니다. Elasticsearch의 집계는 밀집된 형태로 저장되고 그룹 ID로 색인되어 메모리 레이아웃을 컴팩트하게, 반복 작업 속도를 빠르게 유지할 뿐만 아니라 임의 접근을 허용함으로써 특정 성능 최적화를 가능하게 합니다.</p><p>가변 길이 바이트 키의 경우, 그룹 ID와 함께 전체 해시를 캐시합니다. 이렇게 하면 탐색 과정에서 비용이 많이 드는 해시 코드를 다시 계산하지 않아도 되고, 관련 메타데이터를 서로 가깝게 유지하여 캐시 로컬리티를 개선할 수 있습니다. 리해싱 시에 실제 값을 검사할 필요 없이, 캐싱된 해시 값과 제어 바이트에만 의존하여 처리할 수 있으므로 리사이징 비용을 낮게 유지할 수 있습니다.</p><p>구현에서 한 가지 중요한 단순화 포인트는 항목을 절대 삭제하지 않는다는 점입니다. 이를 통해 <em>툼스톤</em>(이전에 점유했던 슬롯을 식별하는 마커)이 필요 없으며, 빈 슬롯을 완전히 비어 있는 상태로 유지할 수 있게 되었습니다. 이는 탐색 동작을 더욱 개선하고 제어 바이트 스캔을 효율적으로 유지합니다.</p><p>그 결과, Elasticsearch의 실행 모델에 자연스럽게 녹아들면서도 스위스 테이블만의 매력적인 성능 특성을 유지하는 설계가 탄생했습니다.</p><h2>성능은 어떻습니까?</h2><p>작은 카디널리티에서 스위스 테이블은 기존 구현과 거의 동등한 성능을 발휘합니다. 이는 예상된 결과입니다. 테이블의 크기가 작을 때는 캐시 효과의 영향이 적고, 최적화할 만한 탐색 과정이 거의 없기 때문입니다.</p><p>카디널리티가 증가함에 따라 상황은 급격히 달라집니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09b13af2fe162f59/6a170cc66f7f04485c9148b8/24900afc47ab07b0e9933f6117b99d0f4613f794-962x599.png" alt="스위스식 해시 테이블을 사용한 ES|QL 통계" /><p>위의 히트맵은 1,000개에서 최대 10,000,000개 그룹에 이르는 카디널리티 범위에 걸쳐, 다양한 키 크기(8, 32, 64, 128바이트)별 시간 개선 지수를 보여 줍니다. 카디널리티가 증가함에 따라 개선 지수는 꾸준히 증가하며, 균등 분포의 경우 최대 2~3배에 달합니다.</p><p>이러한 추세는 설계 단계에서 예측한 바와 정확히 일치합니다. 기존의 해시 테이블은 카디널리티가 높아질수록 탐색 체인이 길어지지만, 스위스식 탐색은 대부분의 조회를 SIMD 연산에 최적화된 제어 바이트 블록 내에서 해결합니다.</p><h2>캐시 동작이 알려 주는 이야기</h2><p>속도 향상이 가능했 이유를 더 잘 이해하기 위해, 동일한 JMH <a href="https://github.com/elastic/elasticsearch/pull/139343/files#diff-d0e0cc91a7495bf36b2d44eacce95f5185d01879e5f6c38089ac7a89aad17da7"><code>benchmarks</code></a>를(을) Linux <code>perf</code> 환경에서 실행하여 캐시 및 TLB 통계를 캡처했습니다.</p><p>원본 구현과 비교할 때 스위스 버전은 전반적으로 약 60% 적은 캐시 참조를 수행합니다. 최종 레벨 캐시 로드가 4배 이상 감소하고, LLC 로드 미스는 6배 이상 감소합니다. LLC 미스는 보통 메인 메모리 접근으로 곧장 이어지기 때문에, 이러한 감소 만으로도 전체 성능 향상의 상당 부분을 설명할 수 있습니다.</p><p>CPU와 더 가까울수록 L1 데이터 캐시 미스가 감소하고, 데이터 TLB 미스는 거의 6배나 줄어어, 공간 지역성이 더 치밀해지고 메모리 접근 패턴이 더 예측 가능해졌음을 의미합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb987a5bd98c0d7eb/6a170cc8a929cf9655ae0a25/6e49b7609fba83e33692cb9834552b6ca7e42a83-998x499.png" alt="캐시 동작: 기존 구현과 Swiss식 해시 테이블을 사용한 ES|QL 통계 비교" /><p>이것이 바로 SIMD 친화적인 제어 바이트가 가져다주는 실질적인 이득입니다. 메모리에 흩어져 있는 키와 값을 반복해서 로드하는 대신, 대부분의 탐색 과정을 캐시에 상주하는 컴팩트한 구조를 스캔하여 해결합니다. 메모리 접근 횟수가 줄어들면 미스가 줄고, 미스가 적을수록 쿼리가 더 빨라집니다.</p><h2>결론</h2><p>스위스식 해시 테이블 설계를 채택하고 SIMD 친화적인 탐색을 적극적으로 활용함으로써, 카디널리티가 높은 ES|QL 통계 워크로드에서 2~3배의 속도 향상과 더불어 더욱 안정적이고 예측 가능한 성능을 달성했습니다.</p><p>이 연구는 최신 CPU 인식 데이터 구조가 해시 테이블과 같이 이미 잘 알려진 문제에서도 상당한 성능 향상을 가져올 수 있음을 보여줍니다. 여기에는 추가적인 기본 유형 특수화 및 조인과 같은 다른 고카디널리티 경로에서의 사용 등 탐구할 여지가 더 많습니다. 이 모든 것은 Elasticsearch 내부를 지속적으로 현대화하기 위해 광범위하세 진행 중인 노력의 일부일 뿐입니다.</p><p>자세한 내용이 궁금하시거나 작업 과정을 살펴보고 싶으시다면, 이 <a href="https://github.com/elastic/elasticsearch/pull/139343">풀 리퀘스트</a> 및 <a href="https://github.com/elastic/elasticsearch/issues/138799">메타 이슈</a> 추적 진행 상황을 Github에서 확인하세요.</p><p>해싱을 즐기십시오!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-swiss-hash-stats</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Chris Hegarty,Matthew Alp,Nik Everett]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf76dd688c5b737e6/6a170cc9839dfa7fc7dcff40/21036e031070f14faccb2b53b22723de2750c391-1280x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Google MCP Toolbox for Databases, 새롭게 추가된 Elasticsearch 지원]]></title>
    <description><![CDATA[이제 Google MCP Toolbox for Databases에서 Elasticsearch 지원이 추가되었습니다. ES|QL 도구를 활용해 인덱스를 다양한 MCP 클라이언트와 안전하게 통합해 보세요.]]></description>
    <content:encoded><![CDATA[<p>이 글에서는 <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a>와 Google MCP Toolbox를 함께 사용해 Elasticsearch 인덱스에서 정보를 추출하는 간단한 도구를 만드는 방법을 단계별로 살펴봅니다.</p><p>최근 Elastic은 <a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a> 오픈소스 프로젝트에 Elasticsearch를 데이터베이스로 사용할 수 있는 지원 기능을 추가했습니다.</p><p>이 새로운 기능을 통해 Google MCP Toolbox로 Elasticsearch에 연결하고 데이터와 직접 "대화하듯" 상호작용할 수 있습니다.</p><h2>Elasticsearch</h2><p>Elasticsearch 인스턴스가 실행 중이어야 합니다. <a href="https://www.elastic.co/cloud">Elastic Cloud</a>에서 무료 체험을 활성화하거나 <a href="https://github.com/elastic/start-local">start-local</a> 스크립트를 사용해 로컬에 설치할 수 있습니다.</p>curl -fsSL https://elastic.co/start-local | sh<p>이 명령을 실행하면 컴퓨터에 Elasticsearch와 Kibana가 설치되고 Google MCP Toolbox 설정에 사용할 API 키가 생성됩니다.</p><p>API 키는 이전 명령의 출력 결과로 표시되며 elastic-start-local 폴더 내 .env 파일에 저장됩니다.</p><h2>예제 데이터 세트 설치하기</h2><p>설치가 완료되면 사용자 이름 <em>elastic</em>과 start-local 스크립트로 생성된 비밀번호(.env 파일에 저장됨)를 사용해 Kibana에 로그인할 수 있습니다.</p><p>Kibana에서 제공되는 <strong>eCommerce orders</strong> 데이터 세트를 설치할 수 있습니다. 이 데이터 세트에는 <strong>kibana_sample_data_ecommerce</strong>라는 단일 인덱스가 포함되어 있으며 전자상거래 웹사이트의 4,675개 주문 정보를 담고 있습니다. 각 주문에는 다음과 같은 정보가 포함되어 있습니다.</p><ul><li><p>고객 정보(이름, ID, 생년월일, 이메일 등)</p></li><li><p>주문 날짜</p></li><li><p>주문 ID</p></li><li><p>제품(가격, 수량, ID, 카테고리, 할인 등을 포함한 전체 제품 목록)</p></li><li><p>SKU</p></li><li><p>총액(세전, 세후)</p></li><li><p>총 수량</p></li><li><p>지리 정보(도시, 국가, 대륙, 위치, 지역)</p></li></ul><p>샘플 데이터를 설치하려면 Kibana의 <strong>통합</strong> 페이지를 열고(상단 검색창에서 "통합"을 검색) "Sample Data"를 설치하세요. 자세한 내용은 다음 문서에서 확인할 수 있습니다: <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a></p><p>이 글의 목표는 Google MCP Toolbox를 설정해 Elasticsearch에 연결하고 자연어로 <strong>kibana_sample_data_ecommerce</strong> 인덱스와 손쉽게 상호작용하는 방법을 보여주는 것입니다.</p><h2>Google MCP Toolbox</h2><p>Google MCP Toolbox는 애플리케이션과 AI 에이전트가 데이터베이스와 안전하고 효율적으로 상호작용할 수 있도록 설계된 오픈 소스 MCP 서버입니다. 이전에는 'GenAI Toolbox for Databases'로 알려졌으나 <a href="https://www.anthropic.com/news/model-context-protocol">Model Context Protocol</a>(MCP)을 전면 지원하게 되면서 현재의 이름으로 변경되었습니다. 이 프로젝트의 목적은 연결 풀링, 인증, 통합 가시성 등과 같은 다양한 운영 관리 요소들을 백그라운드에서 처리하여 에이전트를 데이터베이스에 연결할 때 기존에 필요하던 번거로운 작업을 줄이는 데 있습니다.</p><p>Toolbox의 핵심은 개발자가 데이터베이스 상호작용을 캡슐화한 재사용 가능한 고수준 도구를 정의할 수 있도록 한다는 점입니다. 이러한 도구는 MCP 호환 클라이언트(예: AI 에이전트)에서 호출할 수 있으며 클라이언트가 저수준 SQL 쿼리를 구현하거나 데이터베이스 연결을 직접 관리할 필요가 없습니다. 이러한 접근 방식은 데이터베이스 인식 에이전트를 구축하는 데 필요한 보일러플레이트 코드(boilerplate code)의 양을 크게 줄여 단 몇 줄의 애플리케이션 로직만으로도 고급 데이터 처리 기능을 통합할 수 있게 합니다. 한 번 정의된 도구는 여러 에이전트, 프레임워크, 언어 간에 손쉽게 공유할 수도 있습니다(그림 1).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte90070297ee83546/6a16fa29964cea694b08b972/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" alt="" /><p>Toolbox를 사용할 때의 큰 장점 중 하나는 기본으로 제공되는 보안 모델입니다. OAuth2와 OIDC 같은 인증 흐름을 기본적으로 지원하므로 개발자는 에이전트에서 민감한 데이터베이스 자격 증명을 직접 다루거나 저장하지 않아도 됩니다. 또한 이 플랫폼은 OpenTelemetry를 통해 메트릭과 트레이싱을 포함한 통합 가시성 기능을 제공하며 이는 디버깅, 모니터링 및 프로덕션 배포에 필수적입니다. 종합하면 MCP Toolbox는 MCP를 지원하는 모든 시스템에서 데이터와 상호작용하기 위한 통합되고 안전하며 확장 가능한 인터페이스 역할을 합니다.</p><h2>MCP Toolbox 설치 방법</h2><p>다음 명령어를 사용하여 Linux에서 MCP Toolbox 서버를 설치할 수 있습니다.</p>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox<p>macOS 또는 Windows에 설치하려면 <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">여기</a>에 있는 자세한 안내를 따르세요.</p><h2>Toolbox를 Elasticsearch용으로 구성하기</h2><p>Elasticsearch용 MCP Toolbox를 구성하려면 다음과 같이 <strong>tools.yaml</strong> 파일을 생성해야 합니다.</p>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: &lt;insert-here-api-key&gt;

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders<p><strong>&lt;insert-here-api-key&gt;</strong> 값을 유효한 Elasticsearch API 키로 교체해야 합니다. 로컬에서 start-local로 Elasticsearch를 실행하고 있는 경우 생성된 .env 파일의 <strong>ES_LOCAL_API_KEY</strong> 변수에서 API 키를 확인할 수 있습니다. Elastic Cloud를 사용하는 경우 <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">여기</a>에 설명된 절차를 따라 API 키를 생성할 수 있습니다.</p><p>이전 도구에는 Elasticsearch를 위한 다음 ES|QL 쿼리가 포함되어 있습니다:</p><p>ES|QL에 익숙하지 않다면 간단히 이렇게 이해할 수 있습니다. ES|QL은 Elastic이 개발한 쿼리 언어로 SQL과 비슷한 방식으로 하나 이상의 인덱스를 대상으로 데이터를 검색할 수 있습니다. ES|QL에 대한 더 자세한 내용은 <a href="https://www.elastic.co/docs/reference/query-languages/esql">여기</a>에서 공식 문서를 통해 확인할 수 있습니다.</p><p>위의 쿼리는 <strong>?name</strong> 매개변수(물음표는 매개변수를 의미)를 사용해 지정된 고객 이름이 포함된 모든 주문을 <strong>kibana_sample_data_ecommerce</strong> 인덱스에서 검색합니다.</p><p>고객 이름은 앞서 작성한 YAML 구성에서 string 타입과 'The customer name' 설명으로 정의되었습니다.</p><p>이 도구를 사용하면 <em>고객인 Foo는 2025년 10월에 몇 건의 주문을 했나요?</em>와 같은 고객의 주문 관련 질문에 답할 수 있습니다.</p><p>도구와 그 매개변수에 대한 설명은 사용자의 자연어 요청에서 관련 정보를 추출하는 데 매우 중요합니다. 이러한 정보 추출은 대규모 언어 모델(LLM)의 <strong>함수 호출</strong> 기능을 통해 수행됩니다. 실제로 LLM은 필요한 정보를 얻기 위해 실행해야 할 함수(도구)와 해당 함수에 필요한 매개변수를 자동으로 식별할 수 있습니다.</p><p>함수 호출에 대한 자세한 내용은 Ashish Tiwari가 작성한 <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">OpenAI function calling with Elasticsearch(Elasticsearch와 함께 사용하는 OpenAI 함수 호출)</a>라는 글을 참고하세요.</p><h2>Toolbox 서버 실행하기</h2><p>앞서 작성한 tools.yaml 파일을 사용해 MCP Toolbox를 다음 명령어로 실행할 수 있습니다.</p>./toolbox --tools-file tools.yaml --ui<p><strong> -ui</strong> 매개변수를 사용하면 <a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a>에서 웹 애플리케이션이 실행됩니다(그림 2).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fb3953fae603572/6a16fa2aa6c2b92763e794fb/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" alt="" /><p><strong>Tools</strong> &gt; <strong>customer-orders</strong>를 선택한 뒤 <strong>name</strong> 매개변수에 고객 이름(예: Gwen Sanders)을 입력하고 <strong>Run Tool</strong> 버튼을 클릭할 수 있습니다. 그림 3에 표시된 것과 같은 JSON 응답이 표시됩니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca02df8c78cb39ff/6a16fa2c961e6909b5c4cd22/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" alt="" /><p>이로써 설정이 완료되며 MCP Toolbox는 <strong>customer-orders</strong> 도구를 실행해 Elasticsearch와 통신하고 ES|QL 쿼리를 실행할 수 있습니다.</p><h2>Gemini CLI와 함께 MCP Toolbox 사용하기</h2><p>데이터베이스용 MCP Toolbox와의 통신에는 어떤 MCP 클라이언트든 사용할 수 있습니다. 예를 들어 Gemini를 사용하기 위한 명령줄 도구인 <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>를 사용할 수 있습니다. Gemini CLI는 <a href="https://geminicli.com/docs/get-started/installation/">여기</a>에 안내된 지침에 따라 설치할 수 있습니다.</p><p>Gemini CLI에는 MCP Toolbox용으로 사전 구성된 확장 프로그램이 제공되며 <a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>에서 사용할 수 있습니다. 다음 명령어를 실행해 이 확장 프로그램을 설치할 수 있습니다.</p>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox<p>설치가 완료되면 MCP Toolbox용 tools.yaml 설정 파일을 저장한 디렉터리로 이동한 뒤 다음과 같이 Gemini CLI를 실행해야 합니다. 이는 Gemini CLI가 MCP Toolbox와 자동으로 구성되기 위해 필요한 단계입니다.</p>gemini<p>그림 4에 표시된 출력 결과를 확인할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7245c10b9e32cd6/6a16fa2d964cea073208b976/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" alt="" /><p>다음 명령어로 MCP Toolbox가 연결되어 있는지 확인할 수 있습니다.</p>/mcp list<p><strong>mcp_toolbox</strong> 항목 아래에 <strong>customer-orders</strong> 도구가 나열된 것을 확인할 수 있습니다(그림 5).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte857b42dbe604203/6a16fa2f8b73cb531b189e33/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" alt="" /><p>MCP Toolbox가 Gemini CLI에 연결되었다면 이제 ”<em>고객인 Gwen Sanders의 주문 내역을 알려주세요</em>”와 같은 질문을 해볼 수 있습니다. 그러면 Gemini CLI가 mcp_toolbox 서버에서 customer-orders 도구를 실행할 수 있는 권한을 요청합니다(그림 6 참조).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb7ea752c1a39da7/6a16fa30cdacbfdf937d27ff/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" alt="" /><p>확인되면 Gemini CLI는 MCP Toolbox에 요청을 보내 JSON 응답을 받은 다음 이를 바탕으로 최종 응답을 구성합니다(그림 7).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9f6e04987137a9f/6a16fa320811ae2297e9fef7/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" alt="" /><p>Gemini CLI의 응답에는 Gwen Sanders가 2개 상품을 한 번만 주문했으며 총액은 132유로라는 내용이 표시됩니다.</p><h2>MCP Toolbox SDKs</h2><p>Google MCP Toolbox는 Go, Python, JavaScript로 작성된 프로그램에서 모든 기능에 접근할 수 있는 SDK도 제공합니다.</p><p>예를 들어 Python SDK는 다음의 GitHub 페이지에서 이용할 수 있습니다: <a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python</a></p><p>MCP Toolbox에 연결하기 위해 간단한 에이전트를 만들어야 합니다. 이를 위해 다음 패키지를 설치해야 합니다.</p>pip install toolbox-core
pip install google-adk<p>그리고 아래 명령어로 새로운 에이전트 프로젝트를 생성합니다.</p>adk create my_agent<p>이 명령을 실행하면 <strong>agent.py</strong> 파일이 포함된 <strong>my_agent</strong>라는 새 디렉터리가 생성됩니다.</p><p>Toolbox에 연결하기 위해 <strong>my_agent/agent.py</strong>를 다음 내용으로 업데이트합니다.</p>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")<p>Google API 키를 포함한 <strong>.env</strong> 파일을 생성합니다.</p>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' &gt; my_agent/.env<p>마지막으로 에이전트를 실행하여 결과를 확인할 수 있습니다. 에이전트를 실행하려면 아래 명령어를 입력합니다.</p>adk run my_agent<p>또는 웹 인터페이스로 제공할 수도 있습니다.</p>adk web --port 8000<p>두 경우 모두 Q&amp;A 인터페이스를 통해 MCP Toolbox와 상호작용할 수 있습니다. 예를 들어 이전 예시처럼 <em>고객인 Gwen Sanders의 주문 내역을 보여주세요</em>라고 질문할 수 있습니다.</p><p>다양한 SDK에 대한 자세한 내용은 <a href="https://googleapis.github.io/genai-toolbox/sdks/">이 문서 페이지</a>를 참고하세요.</p><h2>결론</h2><p>이번 글에서는 Google MCP Toolbox for Databases와 Elasticsearch를 통합하는 방법을 시연했습니다. 간단한 YAML 구성 파일만으로 ES|QL 언어를 활용해 자연어 질문을 Elasticsearch 쿼리로 변환하는 도구 세트를 정의할 수 있습니다.</p><p>또한 전자상거래 웹사이트의 주문 데이터를 포함하고 있는 kibana_sample_data_ecommerce 데이터 세트와 상호작용하는 방법을 시연했습니다. 이 설정 파일을 사용하면 MCP Toolbox 서버를 간단히 실행하고 모든 MCP 클라이언트에서 해당 서버에 연결할 수 있습니다.</p><p>마지막으로 Gemini CLI를 클라이언트로 사용해 MCP Toolbox for Databases에 연결하고 Elasticsearch에 저장된 전자상거래 데이터를 조회하는 과정을 시연했습니다. 특정 고객 이름을 기준으로 주문 정보를 조회하는 자연어 쿼리도 직접 실행해 보았습니다.</p><p>MCP 생태계가 계속 성장함에 따라 안전하고 프로덕션 환경에 바로 투입할 수 있는 인프라를 기반으로 한 경량 도구 정의 방식은 적은 노력으로도 점점 더 고도화되고 데이터에 정통한 에이전트를 구축할 수 있는 새로운 기회를 열어 줍니다. 로컬 환경에서 Elastic의 샘플 데이터 세트로 실험하는 경우나 대규모 애플리케이션에 검색 기능을 통합하는 경우에도 MCP Toolbox는 자연어를 통해 Elasticsearch 데이터와 상호작용할 수 있는 신뢰성 높고 확장 가능한 기반을 제공합니다.</p><p>에이전틱 AI 애플리케이션 개발에 대해 더 알고 싶다면 Anish Mathur와 Dana Juratoni가 작성한 <a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Building AI Agentic Workflows with Elasticsearch(Elasticsearch로 AI 에이전틱 워크플로우 구축하기)</a>라는 글을 참고해 보세요.</p><p>Google MCP Toolbox 관련 추가 정보는 <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/</a>에서 확인할 수 있습니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[에이전틱 AI]]></category>
    <dc:creator><![CDATA[Enrico Zimuel,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72d49893c51407cf/6a16fa33cf4f2502bab2cf7e/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES|QL 9.2: 스마트한 LOOKUP JOIN 및 시계열 지원]]></title>
    <description><![CDATA[Elasticsearch 9.2에서 ES|QL에 대한 세 가지 개별 업데이트, 즉 보다 표현력 있는 데이터 상관관계를 위한 향상된 LOOKUP JOIN, 시계열 분석을 위한 새로운 TS 명령, 집계를 위한 유연한 INLINE STATS 명령을 살펴보세요.]]></description>
    <content:encoded><![CDATA[<p>10월에 출시된 Elasticsearch 9.2는 데이터를 그 어느 때보다 더 빠르고 유연하며 쉽게 분석할 수 있도록 중요한 개선 사항이 반영되었습니다. 이번 릴리즈의 핵심은 최종 사용자에게 더 큰 가치를 직접 제공하도록 설계된 파이프 쿼리 언어인 ES|QL의 중요한 기능 향상입니다.</p><p>ES|QL을 통해 데이터 분석 워크플로우를 혁신할 Elasticsearch 9.2의 기능을 살펴보겠습니다.</p><h2>데이터 상관 관계 혁신: 더 스마트하고 빠르며 유연한 조회 조인</h2><p>ES|QL의 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join">LOOKUP JOIN</a> 명령은 Elasticsearch 9.2에서 상당한 변화를 거쳐 효율성과 활용도가 크게 향상되었습니다. LOOKUP JOIN은 ES|QL 쿼리 결과 테이블의 데이터를 지정된 조회 모드 인덱스의 일치하는 레코드와 결합합니다. 조인 필드의 일치하는 값을 기준으로 조회 인덱스의 필드를 결과 테이블에 새로운 열로 추가합니다. 이전에는 데이터 조인이 단일 필드와 단순 동등성으로 제한되었지만, 이제는 그렇지 않습니다. 이러한 향상된 기능을 통해 복잡한 데이터 상관관계 시나리오를 손쉽게 처리할 수 있습니다.</p><p><strong>Lookup Join의 주요 개선 사항은 다음과 같습니다:</strong></p><ul><li><p><strong>다중 필드 조인:</strong> 여러 필드를 쉽게 조인할 수 있습니다. 예를 들어 <code>application_logs</code> 와(과)<code>service_registry</code> 을(를)<code>service_name</code>, <code>environment</code>, 버전을 기준으로 조인하려면 다음과 같이 합니다. <code>version:</code></p></li></ul>FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment, version<ul><li><p><strong>표현식으로 복잡한 조인 술어 활용(기술 미리 보기):</strong></p></li></ul><p>더 이상 단순한 등식에 국한되지 않습니다. LOOKUP JOIN을 사용하면 상관관계에 대한 <strong>여러 기준</strong>을 지정하고 ==, !=, &lt;, &gt;, &lt;=, &gt;= 등 다양한 <strong>이항 연산자</strong>를 사용할 수 있습니다. 즉, 고도로 정교한 조인 조건을 생성하여 데이터에 대해 훨씬 더 정교한 질문을 던질 수 있습니다.</p><p>예시 1: 서비스별 SLA 임계값을 사용하여 애플리케이션 메트릭 찾기</p>FROM application_metrics
| LOOKUP JOIN sla_thresholds
      ON service_name == sla_service AND response_time &gt; sla_response_time<p>예시 2: 이 쿼리는 시간에 따라 변동하는 지역별 가격 정책을 기반으로 납부 금액을 계산합니다. 복잡한 날짜 범위와 등식 조건을 기반으로 세 개의 데이터 세트를 결합하여 최종 <code>due_amount</code>을(를) 계산합니다. 두 번째 LOOKUP JOIN은 <code>meter_readings</code> 인덱스의 <code>measurement_date</code> 필드와 <code>customers</code> 인덱스의 <code>region_id</code> 필드를 사용하여 <code>pricing_policies</code> 인덱스에 조인하고 특정 <code>region</code> 및 <code>measurement_date</code>에 대한 올바른 가격 정책을 찾습니다.</p>FROM meter_readings
| LOOKUP JOIN customers
      ON meter_id
| LOOKUP JOIN pricing_policies
      ON
        region_id == region AND
          measurement_date &gt;= policy_begin_date AND
          measurement_date &lt; policy_end_date
| EVAL due_amount = (kwh_consumed * rate_per_kwh + base_charge) * (1 + tax_rate)
| EVAL period = policy_name
| KEEP customer_name, period, due_amount, measurement_date, kwh_consumed,
    rate_per_kwh, base_charge, tax_rate
| SORT measurement_date<ul><li><p><strong>필터링된 조인의 대규모 성능 향상 </strong></p></li></ul><p>조회 테이블 조건을 사용하여 필터링되는 '확장 조인'의 성능을 개선했습니다. 확장 조인은 입력 행당 여러 개의 일치 항목을 생성하여 큰 중간 결과 세트를 생성할 수 있습니다. 이러한 행 중 상당수가 후속 필터에 의해 삭제되는 경우 상황은 더 악화됩니다. 9.2에서는 조회 데이터에 필터를 적용할 때 불필요한 행을 필터링하여 삭제될 행의 처리를 방지함으로써 이러한 조인을 최적화합니다. 때에 따라 이러한 조인은 최대 <strong>1,000배 더 빨라질</strong> 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce2c948a9a00a348/6a17f1a20b0bed8c08dd368b/002c014ee29b1aaf9ddeb8c554bb76efe3ed180c-1572x954.png" alt="필터링된 조인으로 성능 향상" /><p>이러한 최적화는 조회가 초기에 많은 잠재적 일치 항목을 생성할 수 있는 '확장 조인'을 처리할 때 매우 중요합니다. 지능적으로 필터를 적용하면 관련 데이터만 처리되어 쿼리 실행 시간이 크게 단축되고 방대한 데이터 세트에 대한 실시간 분석이 가능해집니다. 즉, 매우 크거나 복잡한 조인 작업에서도 훨씬 빠르게 인사이트를 얻을 수 있습니다.</p><p><strong>LOOKUP JOIN 클러스터 간 검색(CCS) 호환성:</strong></p><p>LOOKUP JOIN이 8.19와 9.1 버전에서 정식 출시되었을 당시에는 클러스터 간 검색(CCS) 지원이 부족했습니다. 여러 클러스터에서 운영되는 조직의 경우 LOOKUP JOIN은 이제 9.2에서 CCS와 완벽하게 통합됩니다. 조인을 수행하려는 모든 원격 클러스터에 조회 인덱스를 추가하기만 하면 ES|QL이 자동으로 이러한 원격 조회 인덱스를 활용하여 원격 데이터와 조인합니다. 이를 통해 분산 데이터 분석이 간소화되고 전체 Elasticsearch 배포 환경에서 일관된 데이터 보강이 보장됩니다.</p><p>이러한 개선 사항을 통해 전례 없는 정밀도, 속도, 용이성으로 다양한 데이터 세트를 상호 연관시켜 복잡한 해결 방법이나 사전 처리 단계 없이 더 깊고 실행 가능한 인사이트를 발견할 수 있습니다.</p><h2>조회 인덱스용 Kibana Discover UX를 사용해 간편하게 데이터 보강</h2><p>데이터 보강은 간단해야 하며, 장애물이 되어서는 안 됩니다. Kibana의 Discover에서 조회 인덱스를 생성하고 관리하기 위해 새롭고 환상적인 사용자 환경을 도입했습니다.</p><p><strong>직관적인 워크플로우:</strong> Discover의 포괄적인 자동 완성 기능은 ES|QL 편집기에서 조회 인덱스와 조인 필드를 제안하여 프로세스를 안내해 주므로, 업로드한 데이터를 기존 인덱스와 매우 쉽게 연결할 수 있습니다. 존재하지 않는 조회 인덱스 이름을 입력하면 클릭 한 번으로 조회 편집기에 바로 액세스하여 인덱스를 생성할 수 있습니다. 기존 조회 인덱스 이름을 입력하면 다음과 같이 편집 옵션을 제안해 드립니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20a75bcfd25eb156/6a17f1a46864a4864db688a1/d36fd6ffd6bc0bf8d31067f6266445c68d15c71c-1400x184.png" alt="" /><p><strong>인라인 관리(CRUD):</strong> Discover에서 직접 인라인 편집 기능(생성, 읽기, 업데이트, 삭제)을 사용하여 참조 데이터 세트를 최신 상태로 유지하세요.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6263c5dd8c2c9d7e/6a17f1a5af47b614dbcde095/a0e4aa66540b1f725c24ccb0519d978415073bb6-1453x842.png" alt="LOOKUP 쿼리의 예" /><p><strong>간편한 파일 업로드: </strong>이제 CSV와 같은 파일을 Discover 내에서 직접 업로드하고 <code>LOOKUP JOIN</code>에서 즉시 사용할 수 있습니다. Kibana의 다른 영역을 오가며 컨텍스트를 전환할 필요가 없습니다!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltddfcc3580af0e5c6/6a17f1a73e9e45965aba1587/0f5dc2c712af4c4cada50292a7c8b836eb02aa67-1600x748.png" alt="조회 인덱스에 데이터를 쉽게 추가하고 LOOKUP JOIN에서 활용" /><p>사용자 ID를 이름에 매핑하거나 비즈니스 메타데이터를 추가하거나 정적 참조 파일을 조인할 때, 이 기능은 데이터 보강을 대중화하고 모든 사용자가 조인 기능을 직접 사용할 수 있도록 합니다. 빠르고 간편하며 모든 것이 한 곳에서 가능합니다.</p><h2>컨텍스트 보존: INLINE STATS 소개 (기술 미리 보기)</h2><p>데이터 집계는 중요하지만, 때로는 집계된 데이터를 원본 데이터와 <em>함께</em> 확인해야 할 때가 있습니다. <strong>기술 미리 보기</strong> 기능으로 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by">인라인 통계</a>를 소개하게 되어 기쁩니다.</p><p><code>STATS</code> 명령은 입력 필드를 집계된 출력으로 대체하지만, <code>INLINE STATS</code> 명령은 원래 입력 필드를 그대로 유지하고 집계된 새 필드를 추가합니다. 이는 집계 <em>후</em> 원래 입력 필드에서 추가 작업을 수행할 수 있게 하여 보다 지속적이고 유연한 워크플로우를 제공합니다.</p><p>예를 들어 개별 비행 행을 유지하면서 평균 비행 거리를 계산하려면 다음을 수행합니다.</p>FROM kibana_sample_data_flights
 | KEEP Carrier, Dest, DistanceMiles
 | INLINE STATS avgDist = ROUND(AVG(DistanceMiles))
       BY Dest
 | WHERE DistanceMiles &gt; avgDist<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9e0c7209db8a67ad/6a17f1a9e8fbcee5233a1a53/6eea943035e0ab371270084c504a06bb89f8b82b-1496x290.png" alt="평균 거리를 사용하여 평균보다 거리가 먼 항공편 결과 필터링 " /><p>이 쿼리에서는 그룹화 기준으로 사용된 해당 <code>Dest</code>(ination)을(를) 사용하여 각 행에 <code>avgDist</code>을(를) 추가한 다음, 항공편 정보 열이 남아 있으므로 평균보다 거리가 먼 항공편으로 결과를 필터링할 수 있습니다.</p><h2>ES|QL의 시계열 지원 (기술 미리 보기)</h2><p>Elasticsearch는 <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds">시계열 데이터 스트림</a>을 사용하여 메트릭을 저장합니다. <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> 소스 명령을 통해 ES|QL에서 시계열 집계에 대한 지원을 추가하고 있습니다. 이 기능은 Elastic Cloud Serverless 및 9.2 기본 버전에서 기술 미리 보기로 제공됩니다.</p><p>시계열 분석은 주로 시간 버킷에 대한 메트릭 값을 하나 이상의 필터링 차원으로 분할하여 요약하는 집계 쿼리를 기반으로 합니다. 대부분의 집계 쿼리는 2단계 처리 방식을 사용하는데, (a) 시계열별 값을 요약하는 내부 집계 함수와 (b) 시계열 전반에 걸쳐 (a)의 결과를 결합하는 외부 집계 함수가 있습니다.</p><p><a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> 소스 명령어와 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS</code></a>을(를) 결합하면 시계열 데이터에 대한 쿼리를 간결하면서도 효과적으로 표현할 수 있습니다. 좀 더 구체적으로 호스팅 및 시간당 총 요청 비율을 계산하는 다음 예시를 살펴보겠습니다.</p>TS my_metrics
| WHERE @timestamp &gt; NOW() - 1 day
| STATS SUM(RATE(requests))
      BY host, TBUCKET(1h)<p>이 경우 시계열 집계 함수 <code>RATE</code>이(가) 먼저 시계열 및 시간별로 평가됩니다. 그런 다음 <code>SUM</code>을(를) 사용하여 생성된 부분 집계를 결합하여 호스팅 및 시간당 최종 집계 값을 계산합니다.</p><p>사용 가능한 시계열 집계 함수 목록은 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/time-series-aggregation-functions">여기</a>에서 확인하세요. <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds#time-series-metric">카운터</a> 비율이 이제 지원되며, 이는 카운터 처리를 위한 가장 중요한 집계 함수라고 할 수 있습니다.</p><p><code>TS</code> 소스 명령은 <code>STATS</code>와(과) 결합하여 사용할 수 있도록 설계되었으며, 시계열 집계를 효율적으로 지원하도록 실행이 최적화되었습니다. 예를 들어 데이터는 <code>STATS</code>(으)로 이동하기 전에 정렬됩니다. <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork"><code>FORK</code></a> 또는 <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by"><code>INLINE STATS</code></a>와(과) 같은 시계열 데이터 또는 그 순서를 보강하거나 변경할 수 있는 처리 명령은 현재 <code>TS</code>와(과) <code>STATS</code> 사이에 허용되지 않습니다. 이 제한은 향후 해제될 수 있습니다.</p><p><code>STATS</code> 표 형식 출력은 해당 명령을 사용하여 추가로 처리할 수 있습니다. 예를 들어 다음 쿼리는 호스팅 및 시간당 평균 <code>cpu_usage</code>와(과) 호스팅당 최대값의 비율을 계산합니다.</p>TS my_metrics
| STATS avg_usage = AVG(AVG_OVER_TIME(cpu_usage))
      BY host, time_bucket = TBUCKET(1h)
| INLINE STATS max_avg_usage = MAX(avg_usage)
      BY host
| EVAL ratio = avg_usage / max_avg_usage
| KEEP host, time_bucket, ratio
| SORT host, time_bucket DESC<p>시계열 데이터는 Lucene doc 값으로 구동되는 기본 열 형식 저장 공간 엔진에 저장됩니다. TS 명령은 ES|QL 컴퓨팅 엔진을 통해 벡터화된 쿼리 실행 기능을 추가합니다. 동급 <a href="https://www.elastic.co/docs/reference/query-languages/querydsl">DSL</a> 쿼리에 비해 쿼리 성능이 10배 이상 향상되는 경우가 많습니다. 향후 자세한 아키텍처 및 성능 분석을 제공할 예정이니 기대해 주시기 바랍니다.</p><h2>툴킷 확장: 새로운 ES|QL 함수</h2><p>ES|QL의 유용성과 다용성을 더욱 높이기 위해 다음과 같이 새로운 <a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-functions-operators">함수</a> 모음을 추가했습니다.</p><p><strong>스트링 조작: </strong>더욱 강력한 텍스트 및 URL 처리를 위해 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-contains">CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/mv-functions#esql-mv_contains">MV_CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode">URL_ENCODE</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode_component">URL_ENCODE_COMPONENT</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_decode">URL_DECODE</a>을(를) 제공합니다.</p><p><strong>시계열 및 위치 정보:</strong> 유연한 시간 버킷을 위한 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/grouping-functions#esql-tbucket">TBUCKET</a>, 벡터 연산을 위한 TO_DENSE_VECTOR, 고급 위치 기반 분석을 위한 <code>ST_GEOHASH</code>, <code>ST_GEOTILE</code>, <code>ST_GEOHEX</code>, <code>TO_GEOHASH</code>, <code>TO_GEOTILE</code>, <code>TO_GEOHEX</code> 와 같은 포괄적인 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/spatial-functions">위치 정보 함수</a> 세트를 제공합니다.</p><p><strong>날짜 형식:</strong> 가독성 높은 날짜 표현을 위해 <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-day_name">DAY_NAME</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-month_name">MONTH_NAME</a>을 제공합니다.</p><p>이러한 함수는 ES|QL 내에서 직접 데이터를 조작하고 분석할 수 있는 더욱 풍부한 도구 세트를 제공합니다.</p><h2>내부적인 개선을 통한 성능 및 효율성 향상</h2><p>Elasticsearch 9.2에는 강조된 기능 외에도 ES|QL 전반에 걸쳐 다양한 성능 최적화가 포함되어 있습니다. <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/where#like-and-rlike">RLIKE (LIST)</a> 함수가 여러 개의 유사한 RLIKE 쿼리를 대체하는 경우 푸시다운을 사용하여 속도를 높였습니다. <code>RLIKE</code> (LIST)를 사용하면 이러한 쿼리를 단일 Automaton로 병합하여 여러 Automaton 대신 하나의 Automaton를 적용할 수 있습니다. 또한 인덱스 정렬을 통해 키워드 필드를 더 빠르게 로딩하고 일반적인 쿼리를 최적화하여 ES|QL 쿼리를 그 어느 때보다 효율적으로 실행할 수 있도록 개선했습니다.</p><h2>오늘 바로 시작해 보세요!</h2><p>Elasticsearch 9.2는 ES|QL의 획기적인 도약을 의미하며, 데이터 분석 워크플로우에 전례 없는 지원과 유연성을 제공합니다. 이러한 새로운 기능을 살펴보고 그 차이를 경험해 보시기 바랍니다.</p><p>Elasticsearch 9.2의 모든 변경 사항과 향상된 기능에 대한 전체 목록은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/9.2/release-notes-9.2.0.html">공식 릴리즈 노트</a>에서 확인하세요. 쿼리 작업을 즐겨보세요!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Tyler Perkins,Kostas Krikellas,Julian Kiryakov]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd30ea8cac3809cc3/6a17f1aa4b055dd8734322f8/415894e21e7758c907d6e60d4efc94230349beef-2012x1164.png" length="0" type="image/png"/>
    <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch의 ES|QL 편집기 환경과 OpenSearch의 PPL 이벤트 분석기 비교]]></title>
    <description><![CDATA[ES|QL 에디터의 고급 기능이 어떻게 워크플로우를 가속화하는지, OpenSearch의 PPL 이벤트 분석기의 수동 방식과 직접 비교하여 알아보세요. 
]]></description>
    <content:encoded><![CDATA[<p>8.14부터 정식 버전으로 제공되는 <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">Elasticsearch 쿼리 언어</a> (ES|QL)는 검색, 통합 가시성 및 보안 조사를 위해 특별히 설계된 쿼리 언어와 엔진을 도입합니다. 기존 파이프 언어에서 많은 부분을 차용한 OpenSearch의 파이프 처리 언어(PPL)와 달리, ES|QL은 처음부터 세련미, 사용성, Kibana 플랫폼 전반의 원활한 통합에 중점을 두고 구축되었습니다.</p><p>이 블로그에서는 Elasticsearch 9.1의 ES|QL 편집기의 개발자 환경을 OpenSearch 3.2의 이벤트 분석기(줄여서 PPL)와 비교하여 살펴보겠습니다.</p><p>ES|QL 편집기는 지능형 자동 완성, 상황에 맞는 도움말, 추천 쿼리 및 클러스터 간 쿼리 지원을 제공하여 초급 사용자뿐만 아니라 전문가 수준의 사용자도 역량을 강화할 수 있습니다. ES|QL 저작을 위한 사려 깊은 설계는 예를 들어 최근 쿼리와 같은 통합 쿼리 검사 및 Kibana 워크플로우를 통한 전체적인 통합에서 더욱 잘 드러납니다.</p><p>반면 PPL은 자동 완성, 문맥 안내 및 분산 쿼리에 대한 지원이 부족하여 학습 곡선이 가파르고 시행착오가 더 많이 발생합니다.</p><h2>ES|QL을 더 쉽게 배우고 사용하기</h2><p>새로운 쿼리 언어를 시작하는 것은 종종 부담스럽게 느껴질 수 있습니다. <strong>Kibana Discover에</strong>직접내장된 ES|QL 편집기는 쿼리 생성 및 디버깅을 지원할 뿐만 아니라 사용자가 언어에 익숙해지고 편안해지는 속도를 가속화하여 이러한 프로세스를 간소화하도록 설계되었습니다. 에디터가 일상적인 작업의 마찰을 줄여주므로 구문과 시행착오에서 벗어나 문제 해결에 집중할 수 있습니다. 이러한 원칙과 이를 에디터에 통합한 방법에 대한 자세한 내용은 <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">여기에서</a> 확인할 수 있습니다.</p><p>이 편집기 환경은 Discover에만 국한된 것이 아니라 재사용 가능한 코드 모듈로, 대시보드, Kibana 알림, Kibana 지도와 같은 <strong>Kibana의 다른 부분에도 통합하기</strong> 위해 노력하고 있습니다.</p><h3>지능형 자동 완성: 쿼리 생성 가속화</h3><p>ES|QL 편집기의 자동 완성 기능은 포괄적이며 호환 가능한 함수, 인수, 리터럴, 심지어 중첩된 함수에 대한 제안을 제공하며, PPL에서는 특히 부족한 기능입니다. 사실, <a href="https://www.elastic.co/search-labs/blog/esql-autocomplete-rebuilt">여기에</a> 설명된 대로 완전히 새롭게 재구축되었습니다.</p><p>유효성 검사는 <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">여기에</a> 설명된 대로 사용자가 입력하는 대로 실행되며, 필드를 제안하고 오류를 사용자에게 알립니다. 이렇게 하면 사용자의 정신적 부담이 줄어들고 쿼리 작성 프로세스 초기에 오류를 방지할 수 있습니다.</p><p>예시: 이 중첩에는 필드와 호환 가능한 함수가 제안됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb186fc80dcc66f5/6a17f311e9ea8737a5a9c720/a4d7b2819c34fab31bced7873257b8932b623fba-1502x473.png" alt="필드 및 호환 함수 중첩 제안." /><p>PPL은 지원하지 않습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt250ae79e8f33b598/6a17f3127f6f150d3dc09c74/6f3a89b1255b8a3a762022a2704fdd1c2987e5f9-1013x335.png" alt="PPL은 지원하지 않는 기능입니다." /><p>지능형 자동 완성 기능이 호환 가능한 함수, 인수 및 중첩 함수를 안내해 주더라도 사용 가능한 옵션에 대해 더 깊이 이해하고 싶을 수 있습니다. 쿼리 개발을 명확히 하고 개선할 수 있도록 에디터 내에서 즉각적인 지원을 제공하는 ES|QL 에디터의 상황별 도움말은 바로 이 지점에서 매우 유용합니다.</p><h3>상황에 맞는 도움말을 손끝으로</h3><p>자동 완성으로 생성된 명령에 대한 추가 정보는 Ctrl-스페이스키를 클릭하면 확인할 수 있습니다. 해당 함수, 인수 또는 필드에 대한 세부 정보가 포함된 패널이 즉시 나타납니다. 이 가벼운 상호작용은 개발자가 에디터를 종료하거나 외부 문서를 검색할 필요 없이 적시 안내를 제공하여 개발자의 흐름을 유지합니다. 이렇게 하면 구문 조회에 낭비되는 시간이 줄어들고 일반적인 실수를 사전에 방지할 수 있습니다.</p><p>실제 모습은 다음과 같습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt407e42acc7f53f15/6a17f3134b055d128d43231a/2797f9b5e002dbd83c46475c4ed4dcdc86144a01-1343x522.gif" alt="추가 컨텍스트를 위해 Ctrl-스페이스를 사용하여 자동 완성으로 생성된 명령입니다." /><p>PPL에는 이러한 수준의 임베디드 가이드가 없기 때문에 사용자는 외부 문서나 시행착오에 의존해야 합니다. 이러한 부재는 단순히 기능의 부재가 아니라 디자인 철학의 광범위한 차이를 강조합니다. ES|QL은 사용자의 데이터와 워크플로에 맞춰 조정되는 사려 깊고 컨텍스트 인식적인 경험을 우선시합니다. 쿼리의 복잡성이 커질수록 이러한 차이는 더욱 뚜렷해지며, ES|QL 편집기는 학습 및 프로덕션 사용 모두에 더욱 효율적이고 안정적인 환경을 제공합니다.</p><h3>데이터 컨텍스트를 인식하는 권장 쿼리</h3><p>ES|QL 편집기는 로그와 같이 작업 중인 데이터에 맞게 자동으로 조정되는 추천 쿼리를 제공합니다. 빈 편집기를 표시하는 대신 일반적인 사용 사례에 가장 적합한 시작점을 표시합니다. 추천 쿼리를 선택하면 즉시 사용할 수 있는 표준 쿼리가 생성되며 필요에 따라 더 세분화할 수 있습니다. 이 접근 방식은 특히 아직 전체 구문을 모르는 신규 사용자의 경우 쿼리 개발을 가속화합니다.</p><p>다음은 사용자가 '변경 지점 감지' 쿼리를 선택하는 예시입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc6c41f9e1e3cb40/6a17f3156864a43791b688d0/3284c9340d41298820fbf8c7702abad946b48248-925x370.gif" alt="사용자가 '변경점 감지' 쿼리를 선택하면 어떻게 되는지 알아보세요." /><p>이를 PPL 경험과 비교해 보세요:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6511bb51659feaf3/6a17f3166864a427c2b688d4/5c3e59dadc6210aede3366bdd081887bcbae7a54-969x798.png" alt="기본적인 자동 완성 기능만 갖춘 PPL 경험." /><p>반면 PPL은 기본적인 자동 완성 기능만 제공하므로 문맥이나 구조 없이 쿼리를 조합해야 합니다. 이러한 지침의 부재는 좌절과 시행착오로 이어질 수 있습니다.
ES|QL 편집기의 데이터 인식 추천 쿼리를 사용하면 일상적인 작업을 처음부터 시작하거나 구문을 외우지 않아도 됩니다. 편집기는 인지 부하를 줄이고 오류를 방지하며, 쿼리 구성과 씨름하는 대신 문제 해결과 클러스터 간 검색 실행과 같은 더 광범위한 목표에 집중할 수 있게 해줍니다.</p><h2>직관적인 클러스터 간 쿼리</h2><p>ES|QL 편집기의 자동 완성 기능은 <a href="https://elastic.aiops.work/search-labs/blog/esql-cross-cluster-search">CCS를 사용하여</a> 여러 원격 클러스터로 작업하는 경우에도 여전히 우수합니다. 그 이유는 다음과 같습니다:</p><h3>클러스터 간에도 원활한 자동 완성 기능을 제공하는 ES|QL 편집기</h3><p>ES|QL 편집기의 자동 완성 기능은 클러스터 이름뿐만 아니라<strong> 로컬 및 원격 인덱스도</strong> 모두 지원합니다. <a href="https://www.elastic.co/search-labs/blog/esql-cross-cluster-search">여기서</a> 다룬 바와 같이, 이는 로컬 노드로 전송할 쿼리 계획을 검증 및 생성하고, 쿼리를 실행하고, 결과를 집계한 후 사용자에게 다시 전송하는 코디네이터 노드 아키텍처 덕분에 작동합니다. 전체 원격 클러스터 이름을 입력하지 않고 ":"를 입력하면 원격 인덱스에 대한 자동 완성 프로세스가 시작됩니다. 그리고 접두사에 국한되지 않습니다.</p><p>따라서 명명 규칙을 외우거나 컨텍스트를 전환하지 않고도 분산된 데이터 집합을 쉽게 검색하고 쿼리할 수 있습니다.</p><p>다음은 사용자가 원격 인덱스를 찾기 위해 "clu:g"만 입력하는 예제입니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4fadd61aa6d2a212/6a17f3186864a4a385b688d8/bae1fbacb2320e4d07f41291ea57c9bcf15bf8a5-1092x523.gif" alt="사용자가 원격 인덱스를 찾기 위해 &quot;clu:g&quot;만 입력하는 예제입니다." /><p>이와는 대조적으로 PPL은 로컬 인덱스에 대한 기본 완성도만 제공하며, 제안은 접두사 일치로 제한됩니다. 원격 클러스터는 수동으로 입력해야 하므로 오류 발생 가능성이 높아지고 쿼리 생성 속도가 느려집니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3949402084cf303a/6a17f31a6864a482b1b688dc/e38793c0cc7c6cc7dc0fd4779a3e24ffbb6e0838-1094x263.gif" alt="PPL이 로컬 인덱스에 대해 기본 완성만 제공하고 접두사 일치로 제안을 제한하는 방법의 예시입니다." /><p>PPL은 로컬 인덱스에 대해서만 완성을 제공하며 제안은 접두사로 제한됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcaa81c403f354e1f/6a17f31c6864a4e620b688e0/5310f824942f94485cace2558ea72c56a0971e22-862x197.png" alt="PPL이 로컬 인덱스에 대해서만 완성을 제공하고 제안은 접두사로 제한되는 또 다른 예입니다." /><p>ES|QL은 음수 부호를 사용하여 직접 <a href="https://www.elastic.co/docs/solutions/search/cross-cluster-search#exclude-problematic-clusters">제외를 허용함으로써</a> 탐색에 참여하는 클러스터를 세밀하게 제어할 수 있습니다. 이 기능은 클러스터 간 조사 중에 특정 데이터 세트를 포함하거나 생략할 수 있는 하이브리드 환경에서 작업할 때 특히 유용합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf0ca78bfbceb60ae/6a17f31d6864a4199bb688e4/f23ca17f58fbf8e6d27419c028274cb91f30a549-937x78.png" alt="클러스터 간 조사의 코딩 예시입니다." /><p>이러한 개선 사항은 클러스터 간 검색에서 마찰을 줄이는 데 중점을 둔 Elasticsearch의 광범위한 노력을 반영합니다. 분산 쿼리를 더 쉽게 구성하고 관리할 수 있게 해주는 ES|QL 편집기는 분석가와 개발자가 구문보다는 인사이트에 집중할 수 있게 해주는 반면, PPL은 이러한 부담을 사용자에게 더 많이 떠넘깁니다. 또한 ES|QL 편집기는 클러스터 간 쿼리 생성을 간소화할 뿐만 아니라 이러한 쿼리가 실행되는 방식을 검사하는 도구도 제공하여 여러 클러스터에서 투명성 및 성능 모니터링을 보장합니다.</p><h3>검사 도구를 사용하여 클러스터 간 검색 세부 정보 분석하기</h3><p>ES|QL 편집기에서 액세스할 수 있는 검사 도구는 모든 클러스터에서 쿼리 실행에 대한 명시적인 정보가 포함된 메타데이터를 제공하도록 설계되었습니다. 이 기능은 Kibana Discover에서 활성화되며 쿼리 검사기에서 직접 액세스할 수 있어 검색 진행 상황과 세부 정보를 분석할 수 있으며, 이는 특히 <strong>클러스터 간 검색</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-cross-clusters">(CCS)</a>에 매우 중요합니다. 이 기능을 사용하면 검색 진행 상황을 모니터링하고 분산된 데이터 세트에서 쿼리가 어떻게 수행되는지 파악할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf79eaeedac3600b9/6a17f31fabe0f26f06dfeb4b/5d1c204f70171526fff924c30ea8ad08121a0f8d-919x523.gif" alt="클러스터 간 검색 세부 정보를 분석하는 검사 도구입니다." /><p>특히 복잡한 분산 검색의 경우 쿼리 실행에 대한 자세한 가시성을 통해 최적의 성능과 문제 해결을 보장할 수 있습니다.</p><p>개별 쿼리의 메커니즘을 이해하는 것 외에도, ES|QL 편집기는 전체 Kibana 플랫폼에 걸쳐 필수 기능을 심층적으로 내장함으로써 사용자 여정을 더욱 향상시켜 원활하고 중단 없는 워크플로우를 촉진합니다.</p><h2>ES|QL과 Kibana의 통합 쿼리 환경</h2><p>쿼리 기반 분석에서 가장 일반적인 마찰의 원인 중 하나는 컨텍스트 전환입니다. 이미 작성한 쿼리를 다시 불러와야 하는 경우가 종종 있습니다. 방해가 있을 때마다 집중력이 흐트러지고 조사 속도가 느려집니다. ES|QL 편집기는 Kibana 전체에 쿼리 기록을 통합하여 이 문제를 해결합니다.</p><h3>최근 쿼리</h3><p>ES|QL 편집기의 <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">최근 쿼리</a> 기능은 과거 작업에 즉시 액세스할 수 있도록 하여 작업 흐름을 유지하는 데 도움이 됩니다. Discover의 ES|QL 편집기에서 최근 20개의 쿼리를 보고, 다시 실행하고, 별표를 표시할 수 있어 자주 사용하거나 복잡한 쿼리를 클릭 한 번으로 쉽게 찾을 수 있습니다. 이렇게 저장된 쿼리는 대시보드, 시각화, 알림, 지도와 통합되어 Kibana 전체에 적용되므로 현재 화면을 떠나거나 명령을 처음부터 다시 입력할 필요가 없습니다. 이를 통해 반복적인 작업을 줄이고 조사 속도를 높이며 오류 위험을 최소화할 수 있습니다.</p><p>예를 들어 사용자는 Discover의 ES|QL 편집기에서 최근 쿼리를 활용하고 별표를 표시할 수 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49033a46a0805ec8/6a17f321e9ea87a505a9c724/eb0f9fe37b92dec421c394d31ae7d90afebe062e-1421x793.png" alt="ES|QL 편집기의 최근 쿼리를 발견에서 사용하는 예(및 별표 표시 방법)입니다." /><p>최근 쿼리가 대시보드에 통합됩니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2f7c745cdc5e5f1/6a17f323a2929932a1d02da9/b84cd3a9bdec58812360d2aba4fc7713363ee3cc-1411x797.png" alt=" 최근 쿼리가 대시보드에 통합되었습니다." /><p>PPL은 이에 필적할 만한 기능을 제공하지 않기 때문에 사용자는 수동 복사-붙여넣기나 외부 메모에 의존하여 쿼리를 재사용해야 합니다. 이 차이는 편의성 그 이상입니다. Kibana 에코시스템 내에서 진정한 통합 언어로 ES|QL을 구축하려는 Elastic의 전략이 반영된 것입니다. 최근 쿼리와 같은 기능을 통해 ES|QL 편집기는 일상적인 워크플로를 간소화할 뿐만 아니라 현재 기술 미리보기에서 고급 기능을 위한 기반을 마련하여 지속적으로 발전하는 환경을 보장합니다.</p><h2>결론</h2><p>ES|QL은 단순한 구문이 아니라 사용자가 데이터를 검색, 탐색, 분석하는 방식을 개선하기 위한 Elastic의 전략을 반영합니다. 지능형 자동 완성, 컨텍스트 인식 추천 쿼리, 편집기 내 안내, Inspect와 같은 도구를 통해 ES|QL 편집기는 학습을 가속화하고 오류를 줄이며 클러스터 간 분석과 같은 복잡한 워크플로를 간소화합니다. Kibana 전체에 통합되어 쿼리를 대시보드, 알림, 시각화에 원활하게 연결하여 중단 없는 워크플로우를 제공합니다.</p><p>요약하자면, ES|QL은 단순히 또 하나의 파이프 언어가 아니라, 데이터와 상호 작용하는 방식을 근본적으로 재정의하는 직관적인 UI와 결합된 세심하게 설계된 쿼리 엔진으로, 종종 순차적이고 안내가 부족한 OpenSearch PPL과는 확연히 대조되는 통합적이고 지능적이며 지속적으로 진화하는 경험을 제공합니다.</p><h2>다음 단계</h2><p>이 블로그는 ES|QL의 표면적인 부분만 다루고 있습니다. 다음 게시물에서는 OpenSearch PPL과의 비교를 자세히 살펴보고, <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls">컨트롤</a> (대시보드에서 이미 사용 가능), 다중 데이터 탐색 탭, 배경 검색, 더 풍부한 쿼리 기록 및 FUSE와 같은 지리적 공간, 시각화 및 곧 출시될 에디터 기능에 대해 살펴볼 예정입니다.</p><h2>지금 ES|QL 체험하기</h2><p><a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">무료 체험</a> 판으로 완전 관리형 <a href="https://www.elastic.co/cloud/serverless">Elasticsearch 서버리스</a> 프로젝트에서 ES|QL을 확인해 보세요. 8.11 버전에서도 사용할 수 있지만 <a href="https://www.elastic.co/blog/whats-new-elastic-9-1-0">8.19와 9.1에서</a> 가장 잘 사용할 수 있습니다.</p><p>명령 한 번으로 로컬 환경에서 몇 분 안에 시작할 수 있습니다:</p>curl -fsSL https://elastic.co/start-local | sh]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-ppl-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/opensearch-vs-elasticsearch-ppl-esql</guid>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Libby Lin,George Kobar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte51b193824ff8084/6a17f3257f6f154b7bc09c7c/f1ff4ff4a00b3e5b084d4116cea6cabc82a2d816-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch Ruby 클라이언트를 위한 ES|QL 쿼리 빌더 소개]]></title>
    <description><![CDATA[최근 출시된 Elasticsearch Ruby 클라이언트용 ES|QL 쿼리 빌더를 사용하는 방법을 알아보세요. 루비 코드로 ES|QL 쿼리를 더 쉽게 작성할 수 있는 도구입니다.]]></description>
    <content:encoded><![CDATA[<p>최근에 Apache 2 라이선스에 따라 게시된 루비 보석( <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a>)을 출시했습니다. 이 보석을 사용하면 관용적 Ruby로 Elastic의 <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL</a> 쿼리를 빌드한 다음 ES|QL 쿼리 API와 함께 사용할 수 있습니다. ES|QL을 사용하면 개발자가 쿼리를 통해 Elasticsearch에 저장된 데이터를 필터링, 변환, 분석할 수 있습니다. "파이프" ( <code>|</code> )를 사용하여 단계별로 데이터를 작업합니다. 이 보석은 대신 Ruby 함수를 사용하며, 이를 원래 객체에 연결하여 더 복잡한 쿼리를 만들 수 있습니다:</p><p><strong>ESQL:</strong></p><p><strong>Ruby:</strong></p>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending<h2>설치</h2><p>이 젬은 루비젬스에서 다음을 사용하여 설치할 수 있습니다:</p>gem install elastic-esql<p>또는 프로젝트의 젬파일에 추가할 수도 있습니다:</p>gem 'elastic-esql'<h2>사용법</h2><p>전체 쿼리를 한 번에 빌드하거나 <code>from</code> 또는 <code>row</code> 같은 소스 명령으로 쿼리 개체를 만든 다음 ES|QL 메서드를 체인으로 연결하여 빌드할 수 있습니다.</p>query = Elastic::ESQL.from('sample_data')
query.limit(2).sort('@timestamp')<p>겜은 <code>to_s</code> 메서드에서 코드를 ES|QL로 변환하므로 ES|QL 쿼리가 출력되거나 문자열로 캐스팅될 때 반환합니다:</p>query = Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp DESC"<p>각 함수의 <code>!</code> 등가물을 사용하여 쿼리 객체를 인스턴스화하고 초기 상태를 변경할 수 있습니다:</p>query = Elastic::ESQL.from('sample_data')
query.to_s
# =&gt; "FROM sample_data"
query.limit!(2).sort!('@timestamp')
query.to_s
# =&gt; "FROM sample_data | LIMIT 2 | SORT @timestamp"<p>이 도구는 <code>enrich</code> 및 <code>sort</code> 과 같은 추가 단계를 ES|QL 함수에 연결하는 편리한 방법을 제공합니다. <code>Elastic::ESQL</code> 객체에서 <code>enrich</code> 을 호출하면 <code>on</code> 과 <code>with</code> 을 연결할 수 있습니다:</p>esql.enrich!('policy').on('a').with({ name: 'language_name' })<p><code>sort</code> 을 사용한 후 <code>desc</code>, <code>asc</code>, <code>nulls_first</code>, <code>nulls_last</code> 을 쿼리에 연결할 수도 있습니다:</p>Elastic::ESQL.from('sample_data').sort('@timestamp').asc.to_s
# =&gt; 'FROM sample_data | SORT @timestamp ASC'

Elastic::ESQL.from('sample_data').sort('@timestamp').desc.nulls_first.to_s
# =&gt; 'FROM sample_data | SORT @timestamp DESC NULLS FIRST'<p>또한 ES|QL 쿼리를 직접 작성하거나 아직 라이브러리에 추가되지 않은 기능을 사용하려는 경우 사용자 정의 문자열을 지원합니다. <code>custom</code> 은 쿼리 끝에 있는 문자열을 결합합니다. 파이프 문자를 추가하지 않고 함수에 전송되는 대로 추가합니다. 나머지 쿼리에는 공백 문자로 결합됩니다.</p>esql = Elastic::ESQL.from('sample_data')
esql.custom('| MY_VALUE = "test value"').to_s
# =&gt; 'FROM sample_data | MY_VALUE = "test value"'<p><code>custom</code> 함수를 연결할 수도 있습니다:</p>esql.custom('| MY_VALUE = "test value"').custom('| ANOTHER, VALUE')
'FROM sample_data | MY_VALUE = "test value" | ANOTHER, VALUE'<h2>루비 클라이언트와 함께 ES|QL 쿼리 빌더 사용하기</h2><p>쿼리 빌더는 쿼리 객체를 전송하여 <a href="https://github.com/elastic/elasticsearch-ruby">elasticsearch-ruby</a> 및 <code>esql.query</code> API와 함께 직접 사용할 수 있습니다:</p>require 'elasticsearch'
require 'elastic/esql'

client = Elasticsearch::Client.new
index = 'sample_data'

query = Elastic::ESQL.from(index)
                     .sort('@timestamp')
                     .desc
                     .where('event_duration &gt; 5000000')
                     .limit(3)
                     .eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })
client.esql.query(body: { query: query })<p>Elasticsearch Ruby 클라이언트의 ES|QL 도우미와 함께 사용할 수도 있으며, <a href="https://www.elastic.co/search-labs/blog/esql-ruby-helper-elasticsearch">자세히 알아보세요</a>:</p>require 'elasticsearch/helpers/esql_helper'

Elasticsearch::Helpers::ESQLHelper.query(client, query)<h2>독립형 도구로서</h2><p>이 보석은 관용적인 방식으로 ES|QL 쿼리를 작성하는 독립형 도구로 설계되었습니다. 런타임 종속성이 없으므로 공식 Elasticsearch Ruby 클라이언트와 함께 사용하거나 단독으로 사용할 수 있습니다.</p><p>생성된 쿼리는 애플리케이션이 Elasticsearch API와 상호 작용하는 모든 방식(Ruby 여부에 관계없이)으로 <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-query"><code>esql.query</code></a> API와 함께 사용할 수 있습니다. <code>elastic-esql</code> 으로 쿼리를 작성하면 생성된 문자열을 요청 본문에 <code>query</code> 매개변수로 API에 전송할 수 있습니다. </p><p>이전에 <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ruby-tools">인기 있는 Ruby 도구와 함께 Elasticsearch를 사용하는</a> 방법에 대한 글을 쓴 적이 있습니다. 이 보석은 널리 사용되는 모든 Ruby 도구와 함께 ES|QL로 Elasticsearch를 쿼리하는 데 사용할 수 있습니다.</p><h2>결론</h2><p>이 라이브러리는 현재 개발 중이며 최종 API는 아직 완성되지 않았습니다. 현재 기술 프리뷰 버전으로 출시되었습니다. 현재 API 또는 일반적인 사용법에 대한 피드백이 있으시면 주저하지 마시고 <a href="https://github.com/elastic/esql-ruby/issues">새 이슈를 개설해</a> 주세요. 루비 ES|QL 쿼리 빌더에 대해 자세히 알아보려면 <a href="https://github.com/elastic/esql-ruby/?tab=readme-ov-file#ruby-esql-query-builder">README를</a> 참조하세요.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-query-builder-elasticsearch-ruby-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Ruby]]></category>
    <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85d112ccca541b9e/6a17dccb4b055d6bfd4320cc/f8e1263ab53d356824a4fc539084151be80899db-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES|QL에서 사용하기 위해 Kibana를 사용하여 위치 기반 정보 데이터를 Elasticsearch로 수집하기]]></title>
    <description><![CDATA[Kibana와 csv 수집 프로세서를 사용하여 Elasticsearch 쿼리 언어(ES|QL)에서 검색에 사용할 수 있도록 위치 기반 정보 데이터를 Elasticsearch로 수집하는 방법을 알아보세요. Elasticsearch는 강력한 위치 기반 정보 검색 기능을 갖추고 있으며, 이제 사용 편의성과 OGC 친숙도를 획기적으로 개선하기 위해 ES|QL에 제공됩니다. 하지만 이러한 기능을 사용하려면 지리공간 데이터가 필요합니다.]]></description>
    <content:encoded><![CDATA[<p>최근에 Elasticsearch의 새롭고 강력한 <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">파이핑 쿼리 언어인</a> <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">ES|QL에서 새로운</a> <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">위치 기반</a> <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">정보</a> 검색 기능을 사용하는 방법을 설명하는 블로그가 게시되었습니다. 이러한 기능을 사용하려면 Elasticsearch에 위치 기반 정보 데이터가 있어야 합니다. 따라서 이 블로그에서는 지리공간 데이터를 수집하는 방법과 ES|QL 쿼리에서 이를 사용하는 방법을 보여드리겠습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc041ebca11476c6/6a16f70560084b31b93c4344/01fde3b1d714f12bf8673140c9f2f940d443de31-1440x823.jpg" alt="ESQL 지리공간 검색" /><h2>Kibana를 사용하여 위치 기반 정보 데이터 가져오기</h2><p>이전 블로그의 예제에 사용된 데이터는 내부적으로 통합 테스트에 사용하는 데이터를 기반으로 했습니다. 사용자의 편의를 위해 Kibana를 사용하여 쉽게 가져올 수 있는 몇 가지 CSV 파일 형태로 여기에 포함시켰습니다. 데이터는 공항, 도시, 도시 경계가 혼합되어 있습니다. 다음에서 데이터를 다운로드할 수 있습니다:</p><ul><li><p><a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airports.csv">airports.csv</a></p><ul><li><p>여기에는 세 개의 데이터 세트가 병합되어 있습니다:</p><ul><li><p><a href="https://www.naturalearthdata.com/downloads/10m-cultural-vectors/airports/">Natural Earth의</a>공항(이름, 위치 및 관련 데이터)</p></li><li><p><a href="https://simplemaps.com/data/world-cities">SimpleMaps의</a>도시 위치</p></li><li><p><a href="https://www.partow.net/miscellaneous/airportdatabase/">전 세계 공항 데이터베이스의</a>공항 고도</p></li></ul></li></ul></li><li><p><a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airport_city_boundaries.csv">airport_city_boundaries.csv</a></p><ul><li><p>여기에는 위의 공항 및 도시 이름이 하나의 새로운 소스로 병합되어 있습니다:</p><ul><li><p><a href="https://www.openstreetmap.org/">오픈스트리트맵의</a>도시 경계</p></li></ul></li></ul></li></ul><p>짐작할 수 있듯이, ES|QL의 지리적 공간 기능을 테스트하기 위해 이러한 데이터 소스를 위의 두 파일로 결합하는 데 시간을 보냈습니다. 구체적인 데이터 요구 사항과 완전히 같지는 않을 수 있지만, 이를 통해 어떤 것이 가능한지에 대한 아이디어를 얻을 수 있기를 바랍니다. 특히 몇 가지 흥미로운 점을 보여드리고자 합니다:</p><ul><li><p>다른 색인 가능한 데이터와 함께 지리공간 필드가 있는 데이터 가져오기</p></li><li><p><code>geo_point</code> 및 <code>geo_shape</code> 데이터를 모두 가져와 쿼리에서 함께 사용</p></li><li><p>공간 관계를 사용하여 조인할 수 있는 두 인덱스로 데이터 가져오기</p></li><li><p>향후 가져오기를 용이하게 하기 위한 수집 파이프라인 생성(Kibana 이후)</p></li><li><p>수집 프로세서의 몇 가지 예는 <code>csv</code>, <code>convert</code> 및 <code>split</code></p></li></ul><p>이 블로그에서는 CSV 데이터 작업에 대해 설명하지만,<a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">Kibana를</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">사용해 위치 기반 데이터를 추가하는</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">방법에는</a> <a href="https://www.elastic.co/guide/en/kibana/current/import-geospatial-data.html">여러 가지가 있다는 것을</a> 이해하는 것이 중요합니다. 맵 애플리케이션 내에서 CSV, GeoJSON 및 ESRI 셰이프파일과 같은 구분된 데이터를 업로드할 수 있으며 맵에서 직접 도형을 그릴 수도 있습니다. 이 블로그에서는 Kibana 홈 페이지에서 CSV 파일을 가져오는 데 중점을 두겠습니다.</p><h3>공항 가져오기</h3><p>첫 번째 파일인 <a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airports.csv">airports.csv</a>, 에는 우리가 처리해야 할 몇 가지 흥미로운 문제가 있습니다. 첫째, 열에는 CSV 파일에서는 일반적으로 볼 수 없는 추가 공백이 열을 구분합니다. 둘째, <code>type</code> 필드는 다중 값 필드이므로 별도의 필드로 분할해야 합니다. 마지막으로 일부 필드는 문자열이 아니므로 올바른 유형으로 변환해야 합니다. 이 모든 작업은 Kibana의 CSV 가져오기 기능을 사용하여 수행할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt393bc416bf425301/6a16f707839dfa1e86dcfca9/b1afd8c95973bec32f229a9adfaef14680b09a8e-1944x478.png" alt="Kibana 업로드 - 미리보기" /><p>Kibana 홈페이지에서 시작하세요. "통합을 추가하여 시작하기" 라는 섹션이 있으며, 여기에는 "파일 업로드" 라는 링크가 있습니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte12fc741edab20d3/6a16f709acf088600fbe98bf/996372bb3a52859cc840bd1d8f984a33a0e7ada3-1180x410.png" alt="Kibana 홈 - 파일 업로드" /><p>이 링크를 클릭하면 "파일 업로드" 페이지로 이동합니다. 여기에서 <code>airports.csv</code> 파일을 끌어다 놓으면 Kibana가 파일을 분석하여 데이터 미리 보기를 표시합니다. 구분 기호를 쉼표로, 첫 번째 행을 헤더 행으로 자동으로 감지해야 합니다. 그러나 모든 필드가 <code>text</code> 또는 <code>keyword</code> 이라고 가정하여 열 사이의 여분의 공백을 잘라내거나 필드 유형을 결정하지 않았을 수 있습니다. 이 문제를 해결해야 합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf65a09ec0c7a61ad/6a16f70ad7c0227aedde626f/1d9ffa6cdbc0d67a228be1a747ec1ebda6a63099-1800x538.png" alt="Kibana 업로드 - 미리보기" /><p><code>Override settings</code> 을 클릭하고 <code>Should trim fields</code>, <code>Apply</code> 확인란을 선택하여 설정을 닫습니다. 이제 필드 유형을 수정해야 합니다. 다음 페이지에서 <code>Import</code> 을 클릭하세요.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda9a5f85d8e693dc/6a16f70c60084b72f53c4348/a97aceffb1858eae26a213336913505ae9923b03-1800x526.png" alt="Kibana 업로드 - 가져오기" /><p>먼저 인덱스 이름을 선택한 다음 <code>Advanced</code> 을 선택하여 필드 매핑 및 수집 프로세서 페이지로 이동합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt56705083cdd9eb18/6a16f70d66c4f93350f8bdbb/058b0fb09da0b8d7f9c9b0a9c83b8b485ce43925-1440x629.png" alt="Kibana 업로드 - 필드 매핑" /><p>여기서는 인덱스에 대한 필드 매핑과 데이터를 가져오기 위한 수집 파이프라인을 모두 변경해야 합니다. 첫째, Kibana는 <code>scalerank</code> 필드를 <code>long</code> 으로 자동 감지했지만 <code>location</code> 및 <code>city_location</code> 필드를 <code>keyword</code> 로 잘못 인식했을 가능성이 높습니다. <code>geo_point</code> 으로 편집하면 다음과 같은 매핑이 완성됩니다:</p>{
  "properties": {
    "abbrev":        { "type": "keyword" },
    "city":          { "type": "keyword" },
    "city_location": { "type": "geo_point" },
    "country":       { "type": "keyword" },
    "elevation":     { "type": "double" },
    "location":      { "type": "geo_point" },
    "name":          { "type": "text" },
    "scalerank":     { "type": "long" },
    "type":          { "type": "keyword" }
  }
}<p>여기에는 약간의 유연성이 있지만, 어떤 유형을 선택하느냐에 따라 필드가 색인되는 방식과 가능한 쿼리 종류에 영향을 미칩니다. 예를 들어 <code>location</code> 을 <code>keyword</code> 으로 남겨두면 지리공간 검색 쿼리를 수행할 수 없습니다. 마찬가지로 <code>elevation</code> 을 <code>text</code> 으로 남겨두면 숫자 범위 쿼리를 수행할 수 없습니다.</p><p>이제 수집 파이프라인을 수정할 차례입니다. Kibana가 <code>scalerank</code> 를 위의 <code>long</code> 로 자동 감지했다면, 필드를 <code>long</code> 로 변환하는 프로세서도 추가했을 것입니다. <code>elevation</code> 필드에 비슷한 프로세서를 추가해야 하는데, 이번에는 <code>double</code> 으로 변환합니다. 파이프라인을 편집하여 이 변환이 제대로 이루어지도록 합니다. 이를 저장하기 전에 <code>type</code> 필드를 여러 필드로 분할하는 변환을 한 번 더 수행하려고 합니다. 다음 구성을 사용하여 파이프라인에 <code>split</code> 프로세서를 추가합니다:</p>{
  "split": {
    "field": "type",
    "separator": ":",
    "ignore_missing": true
  }
}<p>최종 수집 파이프라인은 다음과 같아야 합니다:</p>{
  "description": "Ingest pipeline created by text structure finder",
  "processors": [
    {
      "csv": {
        "field": "message",
        "target_fields": [
          "abbrev",
          "name",
          "scalerank",
          "type",
          "location",
          "country",
          "city",
          "city_location",
          "elevation"
        ],
        "ignore_missing": false,
        "trim": true
      }
    },
    {
      "convert": {
        "field": "scalerank",
        "type": "long",
        "ignore_missing": true
      }
    },
    {
      "convert": {
        "field": "elevation",
        "type": "double",
        "ignore_missing": true
      }
    },
    {
      "split": {
        "field": "type",
        "separator": ":",
        "ignore_missing": true
      }
    },
    {
      "remove": {
        "field": "message"
      }
    }
  ]
}<p><code>location</code> 및 <code>city_location</code> 필드에 변환 프로세서를 추가하지 않았습니다. 이는 필드 매핑의 <code>geo_point</code> 유형이 이미 이러한 필드에 있는 데이터의 WKT 형식을 이해하고 있기 때문입니다. <code>geo_point</code> 유형은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html">WKT, GeoJSON 등을</a> 포함한 다양한 형식을 이해할 수 있습니다. 예를 들어 CSV 파일에 <code>latitude</code> 와 <code>longitude</code> 에 대한 두 개의 열이 있는 경우, 이를 단일 <code>geo_point</code> 필드로 결합하려면 <code>script</code> 또는 <code>set</code> 프로세서를 추가해야 합니다(예. <code>"set": {"field": "location", "value": "{{lat}},{{lon}}"}</code>).</p><p>이제 파일을 가져올 준비가 되었습니다. <code>Import</code> 을 클릭하면 방금 정의한 매핑과 수집 파이프라인을 사용하여 데이터를 인덱스로 가져옵니다. 데이터를 수집하는 데 오류가 있는 경우, Kibana가 여기에 보고하므로 소스 데이터 또는 수집 파이프라인을 편집하고 다시 시도할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte81621425c7289c5/6a16f70f839dfaf608dcfcad/55dde2940c7aba66ce8257d15007ef797b8a5107-1440x415.png" alt="Kibana 업로드 - 가져오기" /><p>새 수집 파이프라인이 생성된 것을 확인할 수 있습니다. 이는 Kibana의 <code>Stack Management</code> 섹션으로 이동하여 <code>Ingest pipelines</code> 을 선택하면 볼 수 있습니다. 여기에서 방금 만든 파이프라인을 확인하고 필요한 경우 편집할 수 있습니다. 실제로 <code>Ingest pipelines</code> 섹션은 수집 파이프라인을 만들고 테스트하는 데 사용할 수 있으며, 훨씬 더 복잡한 수집을 계획하는 경우 매우 유용한 기능입니다.</p><p>이 데이터를 바로 살펴보고 싶다면 뒷부분으로 건너뛰고, 도시 경계도 가져오려면 계속 읽으세요.</p><h3>도시 경계 가져오기</h3><p><a href="https://raw.githubusercontent.com/elastic/elasticsearch-labs/refs/heads/main/supporting-blog-content/geospatial-data-ingest/airports/airport_city_boundaries.csv">airport_city_boundaries.csv에서</a> 제공되는 도시 경계 파일은 이전 예제보다 가져오기가 조금 더 간단합니다. 여기에는 도시 경계를 <code>POLYGON</code> 으로 표현하는 WKT 필드인 <code>city_boundary</code> 필드와 도시 위치를 <code>geo_point</code> 으로 표현하는 <code>city_location</code> 필드가 포함되어 있습니다. 이 데이터는 공항 데이터와 비슷한 방식으로 가져올 수 있지만 몇 가지 차이점이 있습니다:</p><ul><li><p>자동 감지되지 않았기 때문에 재정의 설정 <code>Has header row</code> 을 선택해야 했습니다.</p></li><li><p>데이터에 여분의 공백이 이미 정리되어 있었기 때문에 필드를 다듬을 필요가 없었습니다.</p></li><li><p>모든 유형이 문자열 또는 공간 유형이었기 때문에 수집 파이프라인을 편집할 필요가 없었습니다.</p></li><li><p>하지만 필드 매핑을 편집하여 <code>city_boundary</code> 필드를 <code>geo_shape</code>, <code>city_location</code> 필드를 다음과 같이 설정해야 했습니다. <code>geo_point</code></p></li></ul><p>최종 필드 매핑은 다음과 같습니다:</p>{
  "properties": {
    "abbrev":        { "type": "keyword" },
    "airport":       { "type": "keyword" },
    "city":          { "type": "keyword" },
    "city_boundary": { "type": "geo_shape" },
    "city_location": { "type": "geo_point" },
    "region":        { "type": "text" }
  }
}<p>앞서 <code>airports.csv</code> 가져오기와 마찬가지로 <code>Import</code> 을 클릭하여 데이터를 인덱스로 가져오기만 하면 됩니다. 데이터는 우리가 편집한 매핑과 Kibana가 정의한 수집 파이프라인으로 가져옵니다.</p><h3>개발 도구로 지리공간 데이터 탐색하기</h3><p>Kibana에서는 일반적으로 "검색" 을 사용하여 색인된 데이터를 탐색합니다. 그러나 ES|QL 쿼리를 사용하여 직접 앱을 작성하려는 경우, 원시 Elasticsearch API에 액세스하는 것이 더 흥미로울 수 있습니다. Kibana에는 쿼리 작성을 실험해 볼 수 있는 편리한 콘솔이 있습니다. 이를 <code>Dev Tools</code> 콘솔이라고 하며, Kibana 사이드바에서 찾을 수 있습니다. 이 콘솔은 Elasticsearch 클러스터와 직접 통신하며 쿼리 실행, 인덱스 생성 등에 사용할 수 있습니다.</p><p>다음을 시도해 보세요:</p>POST /_query?error_trace=true&amp;format=txt
{
  "query": """
FROM airports
| EVAL distance = ST_DISTANCE(city_location, TO_GEOPOINT("POINT(12.565 55.673)"))
| WHERE distance &lt; 1000000 AND scalerank &lt; 6 AND distance &gt; 10000
| SORT distance ASC
| KEEP distance, abbrev, name, location, country, city, elevation
| LIMIT 10
  """
}<p>이렇게 하면 다음과 같은 결과가 표시됩니다:</p><p>거리</p><p>abbrev</p><p>이름</p><p>장소</p><p>국가</p><p>도시</p><p>고도</p><p>273418.05776847183</p><p>HAM</p><p>함부르크</p><p>포인트 (10.005647830925 53.6320011640866)</p><p>독일</p><p>노르트슈테트</p><p>17.0</p><p>337534.653466062</p><p>TXL</p><p>베를린-테겔 국제 공항</p><p>포인트 (13.2903090925074 52.5544287044101)</p><p>독일</p><p>호헨 노이엔도르프</p><p>38.0</p><p>483713.15032266214</p><p>OSL</p><p>오슬로 가르데르멘</p><p>포인트 (11.0991032762581 60.1935783171386)</p><p>노르웨이</p><p>오슬로</p><p>208.0</p><p>522538.03148094116</p><p>BMA</p><p>브롬마</p><p>포인트 (17.9456175406145 59.3555902065112)</p><p>스웨덴</p><p>스톡홀름</p><p>15.0</p><p>522538.03148094116</p><p>ARN</p><p>Arlanda</p><p>포인트 (17.9307299016916 59.6511203397372)</p><p>스웨덴</p><p>스톡홀름</p><p>38.0</p><p>624274.8274399083</p><p>DUS</p><p>뒤셀도르프 국제 공항</p><p>POINT (6.76494446612174 51.2781820420774)</p><p>독일</p><p>뒤셀도르프</p><p>45.0</p><p>633388.6966435644</p><p>PRG</p><p>루진</p><p>포인트 (14.2674849854076 50.1076511703671)</p><p>체코</p><p>프라하</p><p>381.0</p><p>635911.1873311149</p><p>AMS</p><p>스키폴</p><p>POINT (4.76437693232812 52.3089323889822)</p><p>네덜란드</p><p>Hoofddorp</p><p>-3.0</p><p>670864.137958866</p><p>FRA</p><p>프랑크푸르트 국제</p><p>포인트 (8.57182286907608 50.0506770895207)</p><p>독일</p><p>프랑크푸르트</p><p>111.0</p><p>683239.2529970079</p><p>WAW</p><p>오케시에 국제</p><p>포인트 (20.9727263383587 52.171026749259)</p><p>폴란드</p><p>Piaseczno</p><p>111.0</p><h2>Kibana Maps로 위치 기반 정보 데이터 시각화하기</h2><p>Kibana Maps는 위치 기반 정보 데이터를 시각화하기 위한 강력한 도구입니다. 여러 레이어가 있는 맵을 만드는 데 사용할 수 있으며, 각 레이어는 서로 다른 데이터 집합을 나타냅니다. 데이터는 다양한 방식으로 필터링, 집계, 스타일 지정이 가능합니다. 이 섹션에서는 이전 섹션에서 가져온 데이터를 사용하여 Kibana Maps에서 지도를 만드는 방법을 보여드리겠습니다.</p><p>Kibana 메뉴에서 <code>Analytics</code>-&gt;<code>Maps</code> 으로 이동하여 새 지도 보기를 엽니다. <code>Add Layer</code> 을 클릭하고 <code>Documents</code> 을 선택하여 데이터 보기 <code>airports</code> 를 선택한 다음 레이어 스타일을 편집하여 <code>elevation</code> 필드를 사용하여 마커에 색상을 지정하면 각 공항의 높이를 쉽게 확인할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdcc4736d88c1abc9/6a16f7102b835f7353f4afca/9e63726d7c059331e6e20e474f8abee53dc2cbb4-840x388.png" alt="Kibana 지도 - 공항 레이어 스타일" /><p>'변경 사항 유지'를 클릭하여 지도를 저장합니다:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ec3cc535b5c21d8/6a16f71375879ec091fe15dc/32ad1dadb5d341a2b66662c58638b781122fc22c-2852x1528.png" alt="Kibana 지도 - 공항" /><p>이제 두 번째 레이어를 추가하고 이번에는 <code>airport_city_boundaries</code> 데이터 보기를 선택합니다. 이번에는 <code>city_boundary</code> 필드를 사용하여 레이어 스타일을 지정하고 채우기 색상을 하늘색으로 설정하겠습니다. 그러면 지도에 도시 경계가 표시됩니다. 공항 마커가 맨 위에 오도록 레이어를 다시 정렬해야 합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2932d7ff4a7206c9/6a16f7156f7f0409f09145c7/53dd65026f9a98c13f2cc4f9328a79242f0b70de-2854x1510.png" alt="Kibana 지도 - 도시 경계 레이어 스타일" /><h2>공간 조인</h2><p>ES|QL은 <code>JOIN</code> 명령을 지원하지 않지만 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"><code>ENRICH</code></a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich">명령을</a> 사용하여 특별한 경우의 조인을 수행할 수 있습니다. 이 명령은 SQL의 'Left 조인'과 유사하게 작동하며, 두 데이터 집합 간의 공간적 관계를 기반으로 한 인덱스의 결과를 다른 인덱스의 데이터로 보강할 수 있습니다.</p><p>예를 들어 공항 위치가 포함된 도시 경계를 찾아서 공항이 취항하는 도시에 대한 추가 정보로 공항 테이블의 결과를 보강한 다음 결과에 대한 몇 가지 통계를 수행해 보겠습니다:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| STATS
    centroid = ST_CENTROID_AGG(location),
    count = COUNT(city_location)
    BY region
| SORT count DESC
| LIMIT 10<p>색인 강화 인덱스를 먼저 준비하지 않고 이 쿼리를 실행하면 다음과 같은 오류 메시지가 표시됩니다:</p>cannot find enrich policy [city_boundaries]<p>앞서 언급했듯이 ES|QL은 진정한 <code>JOIN</code> 명령을 지원하지 않기 때문입니다. 그 중요한 이유 중 하나는 Elasticsearch가 분산 시스템이고 조인이 확장하기 어려울 수 있는 고비용 작업이라는 점입니다. 그러나 <code>ENRICH</code> 명령은 클러스터 전체에 복제된 특별히 준비된 강화 인덱스를 사용하여 각 노드에서 로컬 조인을 수행할 수 있기 때문에 매우 효율적일 수 있습니다.</p><p>이를 더 잘 이해하기 위해 위의 쿼리에서 <code>ENRICH</code> 명령에 초점을 맞춰 보겠습니다:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary<p>이 명령은 <code>airports</code> 인덱스에서 검색된 결과를 보강하고 원본 인덱스의 <code>city_location</code> 필드와 앞서 몇 가지 예제에서 사용한 <code>airport_city_boundaries</code> 인덱스의 <code>city_boundary</code> 필드 간에 <code>intersects</code> 조인을 수행하도록 Elasticsearch에 지시합니다. 그러나 이 정보 중 일부는 이 쿼리에서 명확하게 표시되지 않습니다. 우리가 볼 수 있는 것은 인라이크 정책의 이름 <code>city_boundaries</code> 이며, 누락된 정보는 해당 정책 정의에 캡슐화되어 있습니다.</p>{
  "geo_match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}<p>여기에서 <code>geo_match</code> 쿼리를 수행하고(<code>intersects</code> 이 기본값), 일치시킬 필드는 <code>city_boundary</code>, <code>enrich_fields</code> 은 원본 문서에 추가하려는 필드임을 알 수 있습니다. 이러한 필드 중 하나인 <code>region</code> 은 실제로 <code>STATS</code> 명령의 그룹화 키로 사용되었는데, 이 '왼쪽 조인' 기능이 없었다면 불가능했을 것입니다. 인라이크 정책에 대한 자세한 내용은 인라이크 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">문서를</a> 참조하세요.</p><p>Elasticsearch의 강화 인덱스와 정책은 원래 준비된 다른 강화 인덱스의 데이터를 사용하여 인덱스 시점에 데이터를 강화하도록 설계되었습니다. 그러나 ES|QL에서는 <code>ENRICH</code> 명령이 쿼리 시점에 작동하며 수집 파이프라인을 사용할 필요가 없습니다. 이렇게 하면 사실상 SQL <code>LEFT JOIN</code> 과 매우 유사하지만, 두 인덱스를 조인할 수 없고 왼쪽에는 일반 인덱스만 있고 오른쪽에는 특별히 준비된 강화 인덱스가 있다는 점이 다릅니다.</p><p>수집 파이프라인이든 ES|QL에서 사용하든 어느 경우든, 수집 인덱스와 정책을 설정하기 위해 몇 가지 준비 단계를 수행해야 합니다. 위에서 이미 <code>airport_city_boundaries</code> 인덱스를 가져왔지만 <code>ENRICH</code> 명령에서 이 인덱스를 직접 색인 강화 인덱스로 사용할 수 없습니다. 먼저 두 단계를 수행해야 합니다:</p><ul><li><p>위에서 설명한 보강 정책을 생성하여 소스 인덱스, 일치시킬 소스 인덱스의 필드, 일치하면 반환할 필드를 정의합니다.</p></li><li><p>이 정책을 실행하여 보강 인덱스를 생성합니다. 이렇게 하면 원본 소스 인덱스를 보다 효율적인 데이터 구조로 읽고 클러스터 전체에 복사하여 특별한 내부 인덱스를 구축합니다.</p></li></ul><p>인리치 정책은 다음 명령을 사용하여 만들 수 있습니다:</p>PUT /_enrich/policy/city_boundaries
{
  "match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}<p>그리고 다음 명령을 사용하여 정책을 실행할 수 있습니다:</p>POST /_enrich/policy/city_boundaries/_execute<p><code>airport_city_boundaries</code> 인덱스의 콘텐츠를 변경하는 경우 이 정책을 다시 실행해야 변경 사항이 인리치 인덱스에 반영되는지 확인할 수 있습니다. 이제 원래 ES|QL 쿼리를 다시 실행해 보겠습니다:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| STATS
    centroid = ST_CENTROID_AGG(location),
    count = COUNT(city_location)
    BY region
| SORT count DESC
| LIMIT 10<p>그러면 공항이 가장 많은 상위 5개 지역과 해당 지역과 일치하는 모든 공항의 중심점, 해당 지역 내 도시 경계를 나타내는 WKT의 길이 범위가 반환됩니다:</p><p>중심</p><p>개수</p><p>지역</p><p>포인트 (-12.139086859300733 31.024386116624648)</p><p>126</p><p>null</p><p>포인트 (-83.10398317873478 42.300230911932886)</p><p>3</p><p>디트로이트</p><p>포인트 (39.74537850357592 47.21613017376512)</p><p>3</p><p>городской округ Батайск</p><p>POINT (-156.80986787192523 20.476673701778054)</p><p>3</p><p>하와이</p><p>포인트 (-73.94515332765877 40.70366442203522)</p><p>3</p><p>뉴욕시</p><p>포인트 (-83.10398317873478 42.300230911932886)</p><p>3</p><p>디트로이트</p><p>포인트 (-76.66873019188643 24.306286952923983)</p><p>2</p><p>새로운 프로비던스</p><p>포인트 (-3.0252167768776417 51.39245774131268)</p><p>2</p><p>카디프</p><p>포인트(-115.40993484668434 32.73126147687435)</p><p>2</p><p>멕시칼리 시</p><p>포인트 (41.790108773857355 50.302146775648)</p><p>2</p><p>Центральный район</p><p>포인트 (-73.88902732171118 45.57078813901171)</p><p>2</p><p>몬트리올</p><p>또한 가장 많이 발견된 지역은 <code>null</code> 입니다. 이것은 무엇을 의미할까요? 이 명령을 SQL의 '왼쪽 조인'에 비유했는데, 이는 공항에 대해 일치하는 도시 경계를 찾을 수 없는 경우 공항이 여전히 반환되지만 <code>airport_city_boundaries</code> 인덱스의 필드에 대한 <code>null</code> 값이 포함된다는 의미입니다. 일치하는 항목이 없는 공항이 125개( <code>city_boundary</code>), 일치하는 항목이 있는 공항이 1개( <code>region</code> 필드 <code>null</code>)인 것으로 나타났습니다. 그 결과 결과에서 <code>region</code> 이 없는 공항이 126개로 집계되었습니다. 사용 사례에서 모든 공항을 도시 경계와 일치시켜야 하는 경우, 빈틈을 메우기 위해 추가 데이터를 소싱해야 합니다. 두 가지를 결정해야 합니다:</p><ul><li><p><code>airport_city_boundaries</code> 인덱스의 레코드에 <code>city_boundary</code> 필드가 없는 경우</p></li><li><p><code>ENRICH</code> 명령을 사용하여 <code>airports</code> 인덱스의 레코드가 일치하지 않는 레코드를 확인합니다(즉. 교차하지 않음)</p></li></ul><h2>Kibana Maps의 위치 기반 정보 데이터에 ES|QL 사용</h2><p>Kibana는 지도 애플리케이션에서 공간 ES|QL에 대한 지원을 추가했습니다. 즉, 이제 ES|QL을 사용해 Elasticsearch에서 위치 기반 정보 데이터를 검색하고 그 결과를 지도에서 시각화할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91922eb4ef43d290/6a16f7161949f737bfe7a7b5/bd78470bd8a4bc60f0db7006bd804b8fe87e2fea-1440x683.png" alt="Kibana 레이어 ES|QL" /><p>레이어 추가 메뉴에 "ES|QL" 이라는 새로운 레이어 옵션이 있습니다. 지금까지 설명한 모든 지리공간 기능과 마찬가지로 "기술 미리보기" 에서 확인할 수 있습니다. 이 옵션을 선택하면 ES|QL 쿼리 결과를 기반으로 맵에 레이어를 추가할 수 있습니다. 예를 들어 전 세계의 모든 공항을 표시하는 레이어를 지도에 추가할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5185ef7461d7e81d/6a16f718839dfa1559dcfcb1/1dd28d3d0509f92d26b0bb5320a2925f7a54c5d9-1440x736.png" alt="Kibana ES|QL - 공항" /><p>또는 <code>airport_city_boundaries</code> 인덱스의 다각형을 표시하는 레이어를 추가하거나, 각 지역에 몇 개의 공항이 있는지 통계를 생성하는 위의 복잡한 <code>ENRICH</code> 쿼리를 추가하는 것이 더 좋을 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51ee3543bb811d98/6a16f71a8b73cbbe63189df1/679a0a401faa613c7cedddd07c64f614ac2b7144-1440x727.png" alt="Kibana ES|QL - 지역 통계" /><h2>그 다음은 무엇일까요?</h2><p>이전 <a href="https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one">위치 기반 정보 검색</a> 블로그에서는 8.14부터 Elasticsearch에서 사용할 수 있는 <code>ST_INTERSECTS</code> 같은 기능을 사용하여 검색을 수행하는 데 중점을 두었습니다. 이 블로그에서는 이러한 검색에 사용한 데이터를 가져오는 방법을 설명합니다. 하지만 Elasticsearch 8.15에는 특히 흥미로운 기능이 추가되었습니다. <code>ST_DISTANCE</code> 효율적인 공간 거리 검색을 수행하는 데 사용할 수 있으며, 다음 블로그의 주제는 이 기능에 관한 것입니다!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/geospatial-data-ingest-for-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/geospatial-data-ingest-for-esql</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Craig Taverner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc041ebca11476c6/6a16f70560084b31b93c4344/01fde3b1d714f12bf8673140c9f2f940d443de31-1440x823.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 25 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES|QL을 사용한 Elasticsearch 위치 기반 정보 검색]]></title>
    <description><![CDATA[Elasticsearch 쿼리 언어(ES|QL)로 위치 기반 정보 검색. Elasticsearch는 강력한 위치 기반 정보 검색 기능을 갖추고 있으며, 이제 사용 편의성과 OGC 친숙도를 획기적으로 개선하기 위해 ES|QL에 제공됩니다.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch는 수년 동안 강력한 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geospatial-analysis.html">위치 기반 정보 검색 및 분석 기능을</a> 제공해 왔지만, API는 일반적인 GIS 사용자들에게 익숙한 것과는 상당히 달랐습니다. 작년에 저희는 SQL만큼 쉽거나 그보다 더 쉬운 파이프라인 쿼리 언어인 <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">ES|QL 쿼리 언어를 추가했습니다</a>. 특히 Elastic이 탁월한 검색, 보안 및 통합 가시성 사용 사례에 적합합니다. 또한 ES|QL 내에서 지리공간 검색 및 분석에 대한 지원을 추가하여 특히 SQL 또는 <a href="https://en.wikipedia.org/wiki/Geographic_information_system">GIS</a> 커뮤니티에서 온 사용자들이 훨씬 쉽게 사용할 수 있도록 하고 있습니다.</p><p>Elasticsearch 8.12와 8.13은 ES|QL에 위치 기반 정보 유형에 대한 기본 지원을 도입했습니다. 이 기능은 8.14에서 지리공간 검색 기능이 추가되면서 크게 향상되었습니다. 무엇보다도 이 지원은 PostGIS와 같은 다른 공간 데이터베이스에서 사용하는 <a href="https://en.wikipedia.org/wiki/Simple_Features"></a> <a href="https://en.wikipedia.org/wiki/Open_Geospatial_Consortium">오픈 지리공간 컨소시엄(OGC) 의 간단한 기능 액세스 표준을 밀접하게 준수하도록 설계되어 이러한</a> 표준에 익숙한 GIS 전문가가 훨씬 쉽게 사용할 수 있습니다.</p><p>이 블로그에서는 ES|QL을 사용하여 위치 기반 정보 검색을 수행하는 방법과 SQL 및 Query DSL과 비교하는 방법을 보여드리겠습니다. 또한 ES|QL을 사용하여 공간 조인을 수행하는 방법과 Kibana Maps에서 결과를 시각화하는 방법도 보여드리겠습니다. 여기에 설명된 모든 기능은 "기술 미리보기" 에서 확인할 수 있으며, 개선 방법에 대한 피드백을 보내주시면 감사하겠습니다.</p><h2>지리공간 데이터 검색</h2><p>쿼리 예제부터 시작하겠습니다:</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>이것은 싼야 피닉스 국제공항(SYX) 주변의 직사각형 검색 다각형과 교차하는 모든 도시 경계 다각형을 검색합니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3897df6bed5d6061/6a17d7c2abe0f29eccdfe861/e48bac8f246c8842f2ea97ddd54910045262aeb1-1440x808.png" alt="ESQL 지리공간 검색" /><p>공항, 도시 및 도시 경계로 구성된 샘플 데이터 세트에서 이 검색은 교차하는 다각형을 찾아 일치하는 문서에서 원하는 필드를 반환합니다:</p><p>abbrev</p><p>공항</p><p>지역</p><p>도시</p><p>도시_위치</p><p>SYX</p><p>산야 피닉스 인터내셔널</p><p>天涯区</p><p>Sanya</p><p>point(109.5036 18.2533)</p><p>쉬웠습니다! 이제 동일한 쿼리에 대한 기존 Elasticsearch 쿼리 DSL과 비교해 보세요:</p>GET /airport_city_boundaries/_search
{
  "_source": ["abbrev", "airport", "region", "city", "city_location"],
  "query": {
    "geo_shape": {
      "city_boundary": {
        "shape": {
          "type": "polygon",
          "coordinates" : [[
            [109.4, 18.1],
            [109.6, 18.1],
            [109.6, 18.3],
            [109.4, 18.3],
            [109.4, 18.1]
          ]]
        }
      }
    }
  }
}
<p>두 쿼리 모두 의도가 상당히 명확하지만 ES|QL 쿼리는 SQL과 매우 유사합니다. PostGIS에서 동일한 쿼리는 다음과 같습니다:</p>SELECT abbrev, airport, region, city, city_location
FROM airport_city_boundaries
WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
);
<p>ES|QL 예제를 다시 살펴보세요. 비슷하지 않나요?</p>FROM airport_city_boundaries
| WHERE ST_INTERSECTS(
      city_boundary,
      "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
  )
| KEEP abbrev, airport, region, city, city_location
<p>Elasticsearch API의 기존 사용자들은 ES|QL이 훨씬 더 사용하기 쉽다는 것을 알게 되었습니다. 이제 기존 SQL 사용자, 특히 공간 SQL 사용자는 ES|QL이 익숙한 것과 매우 유사하게 느껴질 것으로 예상합니다.</p><h4>SQL은 왜 안 될까요?</h4><p>Elasticsearch SQL은 어떻습니까? 오래 전부터 사용되어 왔으며 몇 가지 지리 공간적 특징을 가지고 있습니다. 그러나 Elasticsearch SQL은 원래 쿼리 API 위에 래퍼로 작성되었기 때문에 원래 API로 변환할 수 있는 쿼리만 지원되었습니다. ES|QL에는 이러한 제한이 없습니다. 완전히 새로운 스택이기 때문에 SQL에서는 불가능했던 많은 최적화가 가능합니다. 벤치마크 결과 ES|QL은 특히 집계에서 <a href="https://elasticsearch-benchmarks.elastic.co/#tracks/esql/nightly/default/6M">쿼리 API보다 매우 빠른 경우가 많습니다</a>!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18bda964c8b24e36/6a17d7c3e3179155d22d568a/b8b6c2b2e45850d832805ed1e71e522f4955f53c-1440x813.png" alt="다각형-교차-벤치마크" /><h2>SQL과의 차이점</h2><p>이전 예제에서 보듯이 ES|QL은 SQL과 다소 유사하지만 몇 가지 중요한 차이점이 있습니다. 예를 들어 ES|QL은 파이프 쿼리 언어로, FROM과 같은 소스 명령으로 시작한 다음 모든 후속 명령을 파이프 | 문자로 연결합니다. 이렇게 하면 각 명령이 데이터 테이블을 수신하고 해당 테이블에서 필터링( <code>WHERE</code>), 열 추가( <code>EVAL</code>), 집계 수행( <code>STATS</code>) 등의 작업을 수행하는 방식을 매우 쉽게 이해할 수 있습니다. <code>SELECT</code> 로 시작하여 최종 출력 열을 정의하는 대신 하나 이상의 <code>KEEP</code> 명령이 있을 수 있으며, 마지막 명령은 최종 출력 결과를 지정합니다. 이 구조는 쿼리에 대한 추론을 단순화합니다.</p><p>위의 예제에서 <code>WHERE</code> 명령에 초점을 맞추면 PostGIS 예제와 매우 유사하다는 것을 알 수 있습니다:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    "POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))"::geo_shape
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    'SRID=4326;POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'::geometry
)
<p>문자열 따옴표 문자의 차이 외에도 가장 큰 차이점은 문자열을 공간 유형으로 타입 변환하는 방식에 있습니다. PostGIS에서는 <code>::geometry</code> 접미사를 사용하고, ES|QL에서는 <code>::geo_shape</code> 접미사를 사용합니다. 이는 ES|QL이 Elasticsearch 내에서 실행되고 유형 캐스팅 연산자 <code>::</code> 를 사용하여 문자열을 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-limitations.html#_supported_types">지원되는 ES|QL 유형</a>(이 경우 <code>geo_shape</code>)으로 변환할 수 있기 때문입니다. 또한 Elasticsearch의 <code>geo_shape</code> 및 <code>geo_point</code> 유형은 WGS84로 알려진 공간 좌표계를 의미하며, 더 일반적으로 SRID 번호 4326을 사용하여 참조합니다. PostGIS에서는 명시적이어야 하므로 WKT 문자열에 <code>SRID=4326;</code> 접두사를 사용해야 합니다. 이 접두사를 제거하면 SRID는 0으로 설정되며, 이는 특정 좌표계에 연결되지 않는 Elasticsearch 유형 <code>cartesian_point</code> 및 <code>cartesian_shape</code> 과 유사합니다.</p><p>ES|QL과 PostGIS 모두 유형 변환 함수 구문도 제공합니다:</p><p><em>ES|QL</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    TO_GEOSHAPE("POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))")
)
<p><em>PostGIS</em></p>WHERE ST_INTERSECTS(
    city_boundary,
    ST_SetSRID(
      ST_GeomFromText('POLYGON((109.4 18.1, 109.6 18.1, 109.6 18.3, 109.4 18.3, 109.4 18.1))'),
      4326
    )
)
<h2>OGC 기능</h2><p>Elasticsearch 8.14에는 다음과 같은 네 가지 OGC 공간 검색 기능이 도입되었습니다:</p><p>ES|QL</p><p>PostGIS</p><p>설명</p><p>ST_INTERSECTS</p><p>ST_Intersects</p><p>두 지오메트리가 교차하면 참을 반환하고 그렇지 않으면 거짓을 반환합니다.</p><p>ST_DISJOINT</p><p>ST_Disjoint</p><p>두 지오메트리가 교차하지 않으면 참을 반환하고, 그렇지 않으면 거짓을 반환합니다. ST_INTERSECTS의 역수입니다.</p><p>ST_CONTAINS</p><p>ST_Contains</p><p>하나의 지오메트리에 다른 지오메트리가 포함되어 있으면 참을 반환하고, 그렇지 않으면 거짓을 반환합니다.</p><p>ST_WITHIN</p><p>ST_Within</p><p>하나의 지오메트리가 다른 지오메트리 안에 있으면 참을 반환하고, 그렇지 않으면 거짓을 반환합니다. ST_CONTAINS의 역수입니다.</p><p>이러한 함수는 PostGIS와 유사하게 작동하며 동일한 방식으로 사용됩니다. 예를 들어 <code>ST_INTERSECTS</code> 은 두 지오메트리가 교차하면 참을 반환하고 그렇지 않으면 거짓을 반환합니다. 위 표의 문서 링크를 따라가 보면 모든 ES|QL 예제는 <code>FROM</code> 절 뒤에 <code>WHERE</code> 절 안에 있는 반면, 모든 PostGIS 예제는 리터럴 지오메트리를 사용하고 있음을 알 수 있습니다. 실제로 두 플랫폼 모두 쿼리의 모든 부분에서 해당 함수를 사용할 수 있도록 지원합니다.</p><p><code>ST_INTERSECTS</code> 에 대한 PostGIS 설명서의 첫 번째 예는 다음과 같습니다:</p>SELECT ST_Intersects(
    'POINT(0 0)'::geometry,
    'LINESTRING ( 2 0, 0 2 )'::geometry
);
<p>이에 해당하는 ES|QL은 다음과 같습니다:</p>ROW ST_INTERSECTS(
    "POINT(0 0)"::geo_point,
    "LINESTRING ( 2 0, 0 2 )"::geo_shape
)
<p>PostGIS 예제에서 SRID를 지정하지 않은 점에 유의하세요. 이는 PostGIS에서 <code>geometry</code> 유형을 사용할 때 모든 계산이 평면 좌표계에서 수행되므로 두 도형의 SRID가 같으면 SRID가 무엇이든 상관없기 때문입니다. Elasticsearch에서는 대부분의 함수에서도 마찬가지이지만, 다음 블로그에서 공간 거리 검색에 대해 살펴보겠지만 <code>geo_shape</code> 및 <code>geo_point</code> 에서 구형 계산을 사용하는 예외가 있습니다.</p><h2>ES|QL 다용도성</h2><p>위에서 <code>WHERE</code> 절과 <code>ROW</code> 명령에서 공간 함수를 사용하는 예제를 살펴봤습니다. 그 외에는 어디에 의미가 있을까요? 매우 유용한 곳 중 하나는 <code>EVAL</code> 명령어입니다. 이 명령을 사용하면 표현식을 평가하고 결과를 반환할 수 있습니다. 예를 들어 국가 이름으로 그룹화된 모든 공항의 중심이 국가를 나타내는 경계선 내에 있는지 확인해 보겠습니다:</p>FROM airports
| EVAL in_uk = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL in_iceland = ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| EVAL within_uk = ST_WITHIN(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
| EVAL within_iceland = ST_WITHIN(location, TO_GEOSHAPE("POLYGON ((-25.4883 65.5312, -23.4668 66.7746, -18.4131 67.4749, -13.0957 66.2669, -12.3926 64.4159, -20.1270 62.7346, -24.7852 63.3718, -25.4883 65.5312))"))
| STATS centroid = ST_CENTROID_AGG(location), count=COUNT() BY in_uk, in_iceland, within_uk, within_iceland
| SORT count ASC
<p>결과는 예상대로 영국 공항의 중심이 영국 경계 내에 있고 아이슬란드 경계 내에 있지 않으며 그 반대의 경우도 마찬가지입니다:</p><p>중심</p><p>개수</p><p>in_uk</p><p>in_iceland</p><p>within_uk</p><p>within_iceland</p><p>포인트 (-21.946634463965893 64.13187285885215)</p><p>1</p><p>false</p><p>true</p><p>false</p><p>true</p><p>포인트 (-2.597342072712148 54.33551226578214)</p><p>17</p><p>true</p><p>false</p><p>true</p><p>false</p><p>POINT (0.04453958108176276 23.74658354606057)</p><p>873</p><p>false</p><p>false</p><p>false</p><p>false</p><p>실제로 이러한 함수는 서명이 의미가 있는 쿼리의 모든 부분에서 사용할 수 있습니다. 이 함수는 모두 리터럴 공간 객체 또는 공간 유형의 필드인 두 개의 인수를 받으며, 모두 부울 값을 반환합니다. 한 가지 중요한 고려 사항은 지오메트리의 좌표 참조 시스템(CRS)이 일치해야 하며, 그렇지 않으면 오류가 반환된다는 것입니다. 즉, 동일한 함수 호출에서 <code>geo_shape</code> 유형과 <code>cartesian_shape</code> 유형을 혼합할 수 없습니다. 그러나 <code>geo_point</code> 유형은 <code>geo_shape</code> 유형의 특수한 경우이며 둘 다 동일한 좌표 참조 시스템을 공유하므로 <code>geo_point</code> 유형과 <code>geo_shape</code> 유형을 혼합하여 사용할 수 있습니다. 위에 정의된 각 함수에 대한 문서에는 지원되는 유형 조합이 나열되어 있습니다.</p><p>또한 인수는 공간 리터럴 또는 필드 중 어느 것이든 순서와 상관없이 사용할 수 있습니다. 두 개의 필드, 두 개의 리터럴, 필드와 리터럴 또는 리터럴과 필드를 지정할 수도 있습니다. 유일한 요구 사항은 유형이 호환되어야 한다는 것입니다. 예를 들어, 이 쿼리는 동일한 인덱스에 있는 두 필드를 비교합니다:</p>FROM airport_city_boundaries
| EVAL in_city = ST_INTERSECTS(city_location, city_boundary)
| STATS count=COUNT(*) BY in_city
| SORT count ASC
| EVAL cardinality = CASE(count &lt; 10, "very few", count &lt; 100, "few", "many")
| KEEP cardinality, count, in_city
<p>이 쿼리는 기본적으로 도시 위치가 도시 경계 내에 있는지 묻는데, 일반적으로는 사실이어야 하지만 항상 예외가 있습니다:</p><p>카디널리티</p><p>개수</p><p>in_city</p><p>few</p><p>29</p><p>false</p><p>많은</p><p>740</p><p>true</p><p>훨씬 더 흥미로운 질문은 공항의 위치가 해당 공항이 서비스를 제공하는 도시의 경계 내에 있는지 여부입니다. 그러나 공항 위치는 도시 경계를 포함하는 인덱스와 다른 인덱스에 있습니다. 이를 위해서는 이 두 개의 개별 인덱스에서 데이터를 효과적으로 쿼리하고 상호 연관시킬 수 있는 방법이 필요합니다.</p><h2>공간 조인</h2><p>ES|QL은 <code>JOIN</code> 명령을 지원하지 않지만 SQL의 '왼쪽 조인'과 유사하게 작동하는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"><code>ENRICH</code></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-commands.html#esql-enrich"> 명령을</a> 사용하여 특수한 경우의 조인을 수행할 수 있습니다. 이 명령은 SQL의 'Left 조인'과 유사하게 작동하며, 두 데이터 집합 간의 공간적 관계를 기반으로 한 인덱스의 결과를 다른 인덱스의 데이터로 보강할 수 있습니다.</p><p>예를 들어 공항 위치가 포함된 도시 경계를 찾아서 공항이 취항하는 도시에 대한 추가 정보로 공항 테이블의 결과를 보강한 다음 결과에 대한 몇 가지 통계를 수행해 보겠습니다:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
| MV_EXPAND city_boundary
| EVAL boundary_wkt_length = LENGTH(TO_STRING(city_boundary))
| STATS centroid = ST_CENTROID_AGG(location), count = COUNT(city_location), min_wkt = MIN(boundary_wkt_length), max_wkt = MAX(boundary_wkt_length) BY region
| SORT count DESC
| LIMIT 5
<p>그러면 공항이 가장 많은 상위 5개 지역과 해당 지역과 일치하는 모든 공항의 중심점, 해당 지역 내 도시 경계를 나타내는 WKT의 길이 범위가 반환됩니다:</p><p>중심</p><p>개수</p><p>min_wkt</p><p>max_wkt</p><p>지역</p><p>POINT (-32.56093470960719 32.598117914802714)</p><p>90</p><p>207</p><p>207</p><p>null</p><p>포인트 (-73.94515332765877 40.70366442203522)</p><p>9</p><p>438</p><p>438</p><p>뉴욕시</p><p>포인트 (-83.10398317873478 42.300230911932886)</p><p>9</p><p>473</p><p>473</p><p>디트로이트</p><p>POINT (-156.3020245861262 20.176383580081165)</p><p>5</p><p>307</p><p>803</p><p>하와이</p><p>포인트 (-73.88902732171118 45.57078813901171)</p><p>4</p><p>837</p><p>837</p><p>몬트리올</p><p>그렇다면 여기서 실제로 무슨 일이 일어났을까요? <code>JOIN</code> 은 어디에서 발생했나요? 쿼리의 핵심은 <code>ENRICH</code> 명령어에 있습니다:</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
<p>이 명령은 <code>airports</code> 인덱스에서 검색된 결과를 보강하고 원본 인덱스의 <code>city_location</code> 필드와 앞서 몇 가지 예제에서 사용한 <code>airport_city_boundaries</code> 인덱스의 <code>city_boundary</code> 필드 간에 <code>intersects</code> 조인을 수행하도록 Elasticsearch에 지시합니다. 그러나 이 정보 중 일부는 이 쿼리에서 명확하게 표시되지 않습니다. 우리가 볼 수 있는 것은 인라이크 정책의 이름 <code>city_boundaries</code> 이며, 누락된 정보는 해당 정책 정의에 캡슐화되어 있습니다.</p>{
  "geo_match": {
    "indices": "airport_city_boundaries",
    "match_field": "city_boundary",
    "enrich_fields": ["city", "airport", "region", "city_boundary"]
  }
}
<p>여기에서 <code>geo_match</code> 쿼리를 수행하고(<code>intersects</code> 이 기본값), 일치시킬 필드는 <code>city_boundary</code>, <code>enrich_fields</code> 은 원본 문서에 추가하려는 필드임을 알 수 있습니다. 이러한 필드 중 하나인 <code>region</code> 은 실제로 <code>STATS</code> 명령의 그룹화 키로 사용되었는데, 이 '왼쪽 조인' 기능이 없었다면 불가능했을 것입니다. 인라이크 정책에 대한 자세한 내용은 인라이크 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">문서를</a> 참조하세요. 이러한 문서를 읽다 보면, 수집 파이프라인을 구성하여 인덱스 시점에 데이터를 보강하기 위해 인덱스 보강을 사용하는 방법을 설명하는 것을 볼 수 있습니다. <code>ENRICH</code> 명령은 쿼리 시점에 작동하므로 ES|QL에는 필요하지 않습니다. 필요한 데이터와 보강 정책으로 보강 인덱스를 준비한 다음 ES|QL 쿼리에서 <code>ENRICH</code> 명령을 사용하면 충분합니다.</p><p>또한 가장 많이 발견된 지역은 <code>null</code> 입니다. 이것은 무엇을 의미할까요? 이 명령을 SQL의 '왼쪽 조인'에 비유했는데, 이는 공항에 대해 일치하는 도시 경계를 찾을 수 없는 경우 공항이 여전히 반환되지만 <code>airport_city_boundaries</code> 인덱스의 필드에 대한 <code>null</code> 값이 포함된다는 의미입니다. 일치하는 항목이 없는 공항이 89개( <code>city_boundary</code>), 일치하는 항목이 있는 공항이 1개( <code>region</code> 필드 <code>null</code>)인 것으로 나타났습니다. 그 결과 결과에서 <code>region</code> 이 없는 공항이 90곳으로 집계되었습니다. 또 다른 흥미로운 세부 사항은 <code>MV_EXPAND</code> 명령이 필요하다는 것입니다. 이는 <code>ENRICH</code> 명령이 각 입력 행에 대해 여러 개의 결과를 반환할 수 있고 <code>MV_EXPAND</code> 을 사용하면 이러한 결과를 각 결과마다 하나씩 여러 행으로 분리할 수 있기 때문에 필요합니다. 이는 또한 "하와이" 과 <code>min_wkt</code> 및 <code>max_wkt</code> 결과가 다르게 표시되는 이유를 설명합니다. 이름은 같지만 경계가 다른 여러 지역이 존재하기 때문입니다.</p><h2>Kibana 지도</h2><p>Kibana는 지도 애플리케이션에서 공간 ES|QL에 대한 지원을 추가했습니다. 즉, 이제 ES|QL을 사용해 Elasticsearch에서 위치 기반 정보 데이터를 검색하고 그 결과를 지도에서 시각화할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91922eb4ef43d290/6a16f7161949f737bfe7a7b5/bd78470bd8a4bc60f0db7006bd804b8fe87e2fea-1440x683.png" alt="Kibana 레이어 ES|QL" /><p>레이어 추가 메뉴에 "ES|QL" 이라는 새로운 레이어 옵션이 있습니다. 지금까지 설명한 모든 지리공간 기능과 마찬가지로 "기술 미리보기" 에서 확인할 수 있습니다. 이 옵션을 선택하면 ES|QL 쿼리 결과를 기반으로 맵에 레이어를 추가할 수 있습니다. 예를 들어 전 세계의 모든 공항을 표시하는 레이어를 지도에 추가할 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5185ef7461d7e81d/6a16f718839dfa1559dcfcb1/1dd28d3d0509f92d26b0bb5320a2925f7a54c5d9-1440x736.png" alt="Kibana ES|QL - 공항" /><p>또는 <code>airport_city_boundaries</code> 인덱스의 다각형을 표시하는 레이어를 추가하거나, 각 지역에 몇 개의 공항이 있는지 통계를 생성하는 위의 복잡한 <code>ENRICH</code> 쿼리를 추가하는 것이 더 좋을 수 있습니다.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51ee3543bb811d98/6a16f71a8b73cbbe63189df1/679a0a401faa613c7cedddd07c64f614ac2b7144-1440x727.png" alt="Kibana ES|QL - 지역 통계" /><h2>그 다음은 무엇일까요?</h2><p>위의 두 가지 예제에서 또 다른 공간 함수 <code>ST_CENTROID_AGG</code> 를 압축한 것을 보셨을 것입니다. 이것은 <code>STATS</code> 명령에 사용되는 집계 함수이며, ES|QL에 추가할 예정인 많은 공간 분석 기능 중 첫 번째 기능입니다. 더 많은 것을 보여드릴 수 있게 되면 블로그에 포스팅하겠습니다!</p><p>그 전에 우리가 작업한 특히 흥미로운 기능에 대해 자세히 알려드리고자 합니다. 바로 Elasticsearch에서 가장 많이 사용되는 공간 검색 기능 중 하나인 공간 거리 검색을 수행하는 기능입니다. 거리 검색의 구문이 어떤 모습일지 상상할 수 있나요? OGC 기능과 비슷하지 않을까요? 이 시리즈의 다음 블로그에서 자세히 알아보세요!</p><p>스포일러 경고: Elasticsearch 8.15가 방금 출시되었으며, ES|QL을 사용한 공간 거리 검색이 포함되어 있습니다!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-geospatial-search-part-one</guid>
    <category><![CDATA[Python]]></category>
    <category><![CDATA[ES|QL]]></category>
    <dc:creator><![CDATA[Craig Taverner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd05627be20e89dfb/6a17d7c6414c640256944fdb/de886289dcb56494920875303b622b030b9b810f-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 12 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ES|QL에서 PHP 객체로]]></title>
    <description><![CDATA[PHP에서 ES|QL 쿼리를 실행하고 관리하는 방법을 알아보세요. 이 가이드에 따라 ES|QL 결과를 PHP 객체 또는 사용자 정의 클래스에 매핑하세요.]]></description>
    <content:encoded><![CDATA[<p>elasticsearch-php <a href="https://github.com/elastic/elasticsearch-php/releases/tag/v8.13.0">v8.13.0부터는</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> 쿼리를 실행하고 그 결과를 <a href="https://www.php.net/manual/en/class.stdclass.php">stdClass</a> 또는 사용자 정의 클래스의 PHP 객체에 매핑할 수 있습니다.</p><h2>ES|QL</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL은</a> Elasticsearch 8.11.0에 도입된 새로운 Elasticsearch 쿼리 언어입니다. 현재는 기술 미리보기 버전으로 제공됩니다. Elasticsearch에 저장된 데이터를 필터링, 변환, 분석할 수 있는 강력한 방법을 제공합니다.</p><p>"파이프" (<code>|</code>)를 사용하여 단계별 방식으로 데이터를 조작하고 변환합니다. 이 접근 방식을 통해 사용자는 한 작업의 출력이 다음 작업의 입력이 되는 일련의 작업을 구성하여 복잡한 데이터 변환 및 분석을 수행할 수 있습니다.</p><p>예를 들어 다음 쿼리는 <code>sample_data</code> 인덱스의 처음 3개 문서(행)를 반환합니다:</p>FROM sample_data
| LIMIT 3
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b0310cb335872b7/6a17d7eab1e113258679f0df/c23aee777bacdf90c63b717fd9458207dfc0511d-864x284.png" alt="ES|QL은 테이블을 생성합니다." /><h2>사용 사례: 공식 PHP 클라이언트의 ES|QL 기능</h2><p>공식 PHP 클라이언트에서 개발된 ES|QL 기능을 설명하기 위해 다음 정보를 포함한 81,828권의 책(54.4MB)으로 구성된 <a href="https://github.com/elastic/elasticsearch-php-examples/blob/main/examples/ESQL/data/books.csv">CSV 파일을</a> Elasticsearch에 저장했습니다:</p>Title;Descrition;Author;Year;Publisher;Ratings
<p>이 목록은 공개적으로 사용 가능한 <a href="https://www.kaggle.com/datasets/mohamedbakhet/amazon-books-reviews">Amazon 서평 데이터 세트에서</a> 추출했습니다.</p><p>다음 Elasticsearch 매핑을 사용하여 <code>books</code> 인덱스를 생성했습니다:</p>'mappings' : {
    'properties': {
        'title': {
            'type': 'text'
        },
        'description': {
            'type': 'text'
        },
        'author': {
            'type': 'text'
        },
        'year': {
            'type': 'short'
        },
        'publisher': {
            'type': 'keyword'
        },
        'rating': {
            'type': 'half_float'
        }
    }
}
<p><code>rating</code> 값은 2.9GB의 <a href="https://www.kaggle.com/datasets/mohamedbakhet/amazon-books-reviews?select=Books_rating.csv">Books_rating.csv</a> 파일에서 가져온 순위 리뷰의 평균입니다.</p><p><a href="https://github.com/elastic/elasticsearch-php-examples/blob/main/examples/ESQL/bulk.php">여기에서</a> Elasticsearch에서 모든 도서를 일괄 가져오기 위해 사용한 PHP 스크립트를 찾을 수 있습니다. 대량 작업은 PHP 8.2.17을 사용하여 7초와 28MB RAM이 소요되었습니다. 제안된 매핑을 사용하면 Elasticsearch의 인덱스 크기는 약 62MB입니다.</p><h2>ES|QL 결과를 PHP 객체 또는 사용자 정의 클래스에 매핑하기</h2><p><code>esql()-&gt;query()</code> 엔드포인트를 사용하여 PHP에서 ES|QL 쿼리를 실행할 수 있습니다. 이 쿼리의 결과는 테이블 데이터 구조입니다. 이는 <code>columns</code> 및 <code>values</code> 필드를 사용하여 JSON으로 표현됩니다. <code>columns</code> 필드에는 <code>name</code> 및 <code>type</code> 정의가 있습니다.</p><p>다음은 사용자 순위 리뷰에 따라 스티븐 킹이 쓴 상위 10권의 책을 검색하는 ES|QL 쿼리의 예입니다:</p>$query = &lt;&lt;&lt;EOD
    FROM books
    | WHERE author == "Stephen King"
    | SORT rating DESC
    | LIMIT 10
EOD;

$result = $client-&gt;esql()-&gt;query([
    'body' =&gt; ['query' =&gt; $query]
]);
<p>Elasticsearch의 JSON 결과는 다음과 같습니다:</p>{
    "columns": [
        { "name": "author", "type": "text" },
        { "name": "description", "type": "text" },
        { "name": "publisher", "type": "keyword" },
        { "name": "rating", "type": "double" },
        { "name": "title", "type": "text" },
        { "name": "year", "type": "integer" }
    ],
    "values": [
        [
            "Stephen King",
            "The author ...",
            "Turtleback",
            5.0,
            "How writers write",
            2002
        ],
        [
            "Stephen King",
            "In Blockade Billy, a retired coach...",
            "Simon and Schuster",
            5.0,
            "Blockade",
            2010
        ],
        [
            "Stephen King",
            "A chilling collection of twenty horror stories.",
            "Signet Book",
            4.55859375,
            "Night Shift (Signet)",
            1979
        ],
        ...
    ]
}
<p>이 예에서는 책과 관련된 6개의 속성(저자, 설명, 출판사, 등급, 제목, 연도)과 10개의 결과(모두 스티븐 킹의 책)가 있습니다.</p><p>ES|QL에서 지원되는 모든 유형 목록은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-limitations.html#esql-supported-types">여기에</a> 나와 있습니다.</p><p><code>$result</code> 응답 개체는 배열, 문자열 또는 객체로 액세스할 수 있습니다(자세한 내용은 <a href="https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/connecting.html#client-usage">여기를</a> 참조하세요).</p><p>객체 인터페이스를 사용하면 속성 및 인덱스를 사용하여 값에 액세스할 수 있습니다. 예를 들어 <code>$result-&gt;values[0][4]</code> 은 목록에서 첫 번째 책(0)의 제목(4)을 반환하고 <code>$result-&gt;values[1][3]</code> 은 두 번째 책(1)의 순위 점수(3) 등을 반환합니다. PHP에서 배열의 인덱스는 0부터 시작한다는 점을 기억하세요.</p><p>이 인터페이스는 일부 사용 사례에는 충분할 수 있지만 대부분의 경우 결과물로 여러 개의 객체를 원합니다.</p><p>결과를 객체 배열로 매핑하려면 elasticsearch-php의 새로운 <a href="https://github.com/elastic/elasticsearch-php/issues/1398">mapTo()</a> 기능을 사용할 수 있습니다.</p><p>이 기능은 <a href="https://github.com/elastic/elasticsearch-php/blob/main/src/Response/Elasticsearch.php">Elasticsearch 응답 개체에서</a> 직접 사용할 수 있습니다. 즉, 다음과 같이 액세스할 수 있습니다:</p>$books = $result-&gt;mapTo(); // Array of stdClass
foreach ($books as $book) {
    printf(
        "%s, %s, %d, Rating: %.2f\n",
        $book-&gt;author,
        $book-&gt;title,
        $book-&gt;year,
        $book-&gt;rating
    );
}
<p>사용자 지정 Book 클래스가 있는 경우 다음과 같이 이를 사용하여 결과를 매핑할 수 있습니다:</p>class Book
{
    public string $author;
    public string $title;
    public string $description;
    public int $year;
    public float $rating;
}

$books = $result-&gt;mapTo(Book::class); // Array of Book
<p>클래스에 ES|QL 결과에 포함된 속성 외에 다른 속성이 있는 경우 이 방법도 작동합니다. <code>mapTo()</code> 함수는 ES|QL 결과의 열로 반환된 속성만 사용합니다.</p><p>이 문서에 보고된 모든 예제는 <a href="https://github.com/elastic/elasticsearch-php-examples/tree/main/examples/ESQL">여기에서</a> 다운로드할 수 있습니다.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/esql-php-map-object-class</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/esql-php-map-object-class</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[PHP]]></category>
    <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99f5ab85c0713977/6a17d7ebfbc5f8072b491918/aea56270f48cb64130d1b515b983434e0960dc2f-500x500.png" length="0" type="image/png"/>
    <pubDate>Mon, 08 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>