<?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/jp/search-labs/blog/category/esql</link>
    </image>
    <link>https://www.elastic.co/jp/search-labs/blog/category/esql</link>
    <atom:link href="https://www.elastic.co/jp/search-labs/rss/category/esql.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[jp]]></language>
    <lastBuildDate>Tue, 29 Sep 2026 05:54: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>この例では、要約にはマッピングやアナライザーの設定がありません。また、逆インデックスとも関連付けられていません。このクエリの有効期間中のみ存在しますが、それでも検索可能です。これを機能させるには、2つの追加要素が必要です。</p><p>まず、<a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/search-functions/match">MATCH</a>は、マップされたフィールドだけでなく、すべての式を最初の引数として受け入れるようになりました。これにはEVALで生成されたカラムや、インラインで使われる関数結果も含まれます。また、元のドキュメントから直接読み込む未マッピングフィールドも含まれます。さらに、この新しいユースケースでは、MATCHで通常受け入れられるすべてのデータタイプがサポートされます。</p><p>2つ目の要素は新しい<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は2つを異なる方法で扱うため、区別が重要です。テキスト値は分析され、キーワード値は正確に比較されます。これは、インデックス付きキーワードフィールドの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（正規表現）という2つの方法がすでにありました。どちらもどんな文字列式でも動作するので、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>詳細なスタックトレースや生のリクエストペイロードなど、意図的にマッピングからフィールドを除外する場合もあります。デバッグ用のデータブロックを省略することもあります。これらのうちの1つをインデキシングすると、ドキュメントごとにディスクとヒープ容量が消費されます。四半期に1回しかクエリしないようなフィールドのためにインデックスを作成する価値はありません。</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>以前の解決策は、マッピングをテキストに変更する（または複数フィールドを追加する）ことと、すべてを再インデックスすることでした。これは時間と費用がかかる場合があり、多くの場合、ユーザーはわざわざその手間をかけたくないことが多いです。新しい答えは、1つの関数呼び出しです。</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>逆インデックス検索は取り込み時に作業を行い、クエリ時に一致しない文書には一切触れません。実行時マッチはクエリ時に、到達するすべての行に対してその分析を行います。一方は既に処理が完了しているため高速であり、もう一方はデータがインデックス化されている必要がないため柔軟性があります。</p><h2>ES|QL全文検索の今後の展望は</h2><p>この記事の内容はすべて、ES|QLの検索機能を、事前にインデックスを作成したデータだけでなく、あらゆるデータに対して動作させるための、より大規模な取り組みの第一弾です。先に挙げた制約事項については積極的に改善に取り組んでおり、ロードマップはさらに次の段階へと進んでいます。</p><ul><li><p><strong>スコアリング。</strong>実行時の一致結果も_scoreに反映されるため、データがインデックス化されていない場合でも、関連性に基づいてソートできます。</p></li><li><p><strong>式に対する</strong><strong>MATCH_PHRASE。</strong>Elastic Cloud Serverlessですでに利用可能で、9.6でElastic Stackに登場します。</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/jp/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では、この機能が<a href="https://www.elastic.co/jp/search-labs/blog/ai-dashboard-generation-elastic-agent-kibana">9.4でのテクニカルプレビュー</a>を経て一般提供（GA）となり、失敗したクエリを再試行するエラー回復機能、階層型モデルルーティングによってES|QL生成コストを5分の1に抑える機能、インタラクティブなフィルターコントロールが利用可能になります。今回のリリースでは、散布図、箱ひげ図、条件付き書式、カスタムツールチップなど、通常はJSONで手動コーディングする必要がある<a href="https://www.elastic.co/docs/explore-analyze/visualize/custom-visualizations-with-vega">Vega-Lite</a>チャートを自然言語で作成する機能も追加されました。</p><h2>KibanaのAIダッシュボード作成の新機能</h2><p><strong>機能</strong></p><p><strong>テクニカルプレビュー（9.4）</strong></p><p><strong>一般提供（GA）（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>この機能を使用するには、Enterpriseライセンスが必要です。<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クエリ言語（ES|QL）クエリに変換する</a><a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Language Integrated</a> Query（LINQ）プロバイダーが含まれています。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からデータが到着すると、行は1つずつマテリアライズされます。</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>.Where(p =&gt; p.Price &gt; 100)</code> を <code>IEnumerable&lt;T&gt;</code> 上で呼び出すと、ラムダは <code>Func&lt;Product, bool&gt;</code> にコンパイルされます。これは、ランタイムがインプロセスで実行する通常のデリゲートです。これはLINQ-to-Objectsです。</p><p>同じメソッドを <code>IQueryable&lt;T&gt;</code> で呼び出すと、C#コンパイラはラムダを <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から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>BinaryExpression</code>ノードのサブツリーであり、<code>&amp;&amp;</code>、<code>&gt;=</code>、および<code>==</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 条件</p><p>SelectProjectionVisitor</p><p>.Select(selector)</p><p>EVAL + KEEP + RENAME</p><p>訪問者別にグループ化</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>の各々が、1つの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文字列を生成します。各コマンドは1行になり、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>ツリーは、1クエリにつき1回事前に計算され、フラットな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>によって1行ずつ生成されます。</p><h2>レイヤーアーキテクチャ</h2><p>LINQ to ES|QL機能は、以下の3つのパッケージに分かれています。</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実行を追加します。もしアプリケーションが他のElasticsearch APIではなく、ES|QLのみを必要とする場合、これが最小限の依存関係オプションです。</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>これら3つのパッケージはすべて、ソース生成の<code>JsonSerializerContext</code>と併用する場合、ネイティブ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>80以上のES|QL関数が <code>EsqlFunctions</code>クラスを通じて利用可能で、日付/時間、文字列、数学、IP、パターンマッチング、スコアリングをカバーしています。標準的な<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>サーバー側の非同期クエリは、通常のタイムアウトしきい値を超える可能性のある長時間実行される分析クエリや大規模データセットの処理、あるいはロードバランサー、APIゲートウェイ、プロキシなど、厳格なHTTPタイムアウトを強制するタイムアウトに敏感な環境で特に役立ちます。非同期クエリは、結果の取得から提出を切り離すことで接続切断を回避します。</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[Vector Database]]></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 Query Language (ES|QL) で一貫性のある測定可能なスピードアップを実現する方法。]]></description>
    <content:encoded><![CDATA[<p>最近、Elasticsearchのハッシュテーブル実装の重要な部分をスイススタイルの設計に置き換えたところ、均一でカーディナリティの高いワークロードでビルドと反復処理の時間が最大2～3倍高速化されることがわかりました。結果として、Elasticsearch Query Language (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>各制御バイトは単一のスロットを表し、この場合、スロットが空かどうかと、ハッシュから導出された短いフィンガープリントの2つの項目をエンコードします。これらの制御バイトはメモリ上に連続的に配置され、通常16個のグループで構成されており、<a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">単一命令多重データ</a>（SIMD）処理に理想的です。</p><p>スイステーブルは、一度に1つのスロットをプローブする代わりに、ベクトル命令を使用して制御バイトブロック全体をスキャンします。1回の操作で、CPUは入力キーのフィンガープリントを16個のスロットと比較し、空のエントリーを除外します。この高速パスを通過する少数の候補のみが、実際のキーのロードとの比較を必要とします。</p><p>この設計では、少量の追加メタデータと引き換えに、はるかに優れたキャッシュローカリティと大幅に少ないランダムロードを実現しています。テーブルが拡大し、プローブチェーンが長くなるにつれて、それらのプロパティはますます価値が高くなります。</p><h2>中央にSIMDがあります</h2><p>ここでの真の主役はSIMDです。</p><p>制御バイトは単にコンパクトであるだけでなく、ベクトル命令で処理されるように明示的に設計されています。1 回のSIMD比較で16個のフィンガープリントを一度にチェックできるため、通常はループとなる処理が複数の広範な操作に変わります。例：</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f710e87dd749ab3/6a170cc46234e052dadb1a49/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" alt="SIMDがElasticsearchの中心に" /><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ビットベクトルのPanama Vector 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>上記のヒートマップは、異なるキーサイズ（8、32、64、128バイト）に対する時間改善係数を、1,000から10,000,000グループまでの基数にわたってプロットしています。カーディナリティが増加するにつれて、改善係数は着実に増加し、均一分布の場合は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="キャッシュの挙動：オリジナルとES|QL統計（スイススタイルのハッシュテーブルを使用）の比較" /><p>これがSIMD対応の制御バイトの実用的なメリットです。散在したメモリ位置からキーと値を繰り返しロードする代わりに、ほとんどのプローブは、コンパクトなキャッシュ常駐構造をスキャンすることによって解決されます。アクセスされるメモリが少なければミスも減り、ミスが少なければクエリも速くなります。</p><h2>まとめ</h2><p>スイススタイルのハッシュテーブル設計を採用し、SIMDフレンドリーなプロービングを積極的に活用することで、高カーディナリティのES|QL統計ワークロードで2〜3倍の速度向上を達成し、より安定的で予測可能なパフォーマンスを実現しました。</p><p>この研究は、現代のCPUに対応したデータ構造が、ハッシュテーブルのような十分に確立された問題においても、大きな性能向上を実現できることを示しています。ここでは、追加のプリミティブ型の特殊化や、結合などの他の高カーディナリティパスでの使用など、さらに検討する余地がありますが、これらはすべて、Elasticsearchの内部を継続的に近代化するための広範かつ継続的な取り組みの一部に過ぎません。</p><p>詳細に興味がある方や作業をフォローしたい方は、GitHubの<a href="https://github.com/elastic/elasticsearch/pull/139343">プルリクエスト</a>と<a href="https://github.com/elastic/elasticsearch/issues/138799">メタイシュー</a>の進捗追跡をチェックしてみてください。</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>この記事では、Google MCP Toolboxと<a href="https://github.com/elastic/elasticsearch">Elasticsearch</a>を併用し、Elasticsearchインデックスから情報を抽出する簡単なツールを構築する方法を解説します。</p><p>当社は最近、<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>データをインストールできます。このデータベースには、eコマースWebサイトからの4,675件の注文に関する情報を含む<strong>kibana_sample_data_ecommerce</strong>という単一のインデックスが含まれています。各注文について、次の情報があります。</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>ページを開き（検索トップバーで「Integration」を検索）、「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はオープンソースのMCPサーバーで、アプリケーションやAIエージェントが安全かつ効率的にデータベースとやり取りできるように設計されています。以前は「GenAI Toolbox for Databases」と呼ばれていたこのプロジェクトは、<a href="https://www.anthropic.com/news/model-context-protocol">モデルコンテキストプロトコル</a>（MCP）との完全な互換性を採用した後に改名されました。その目的は、エージェントをデータベースに接続する際に従来必要とされていた接続プーリング、認証、オブザーバビリティ、その他の運用上の懸念をバックエンドで処理することで、重労働を排除することです。</p><p>Toolboxの本質は、開発者がデータベースのやり取りをカプセル化する再利用可能な高レベルのツールを定義できるようにすることです。これらのツールは、AIエージェントなどのMCP互換クライアントならどれでも起動できます。クライアントが低レベルのSQLクエリを実装したり、データベース接続を管理したりする必要はありません。このアプローチにより、データベース対応エージェントの構築に必要な定型コードの量が大幅に削減され、わずか数行のアプリケーションロジックに高度なデータ操作を統合できるようになります。ツールが定義されると、複数のエージェント、フレームワーク、言語間で共有できます（図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>MCP ToolboxサーバーをLinuxにインストールするには、次のコマンドを使用します。</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>Elasticsearch向けにToolboxを構成する</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をローカルで実行している場合は、start-localによって生成された.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はSQLと同様にElasticが開発したクエリ言語で、1つ以上のインデックスを検索するために使用できます。ES|QLの詳細については<a href="https://www.elastic.co/docs/reference/query-languages/esql">こちらの</a>公式ドキュメントをご覧ください。</p><p>上記のクエリは、<strong>kibana_sample_data_ecommerce</strong>インデックスに格納されている指定顧客名を含むすべての注文を<strong>?name</strong>パラメーター（疑問符はパラメーターを示します）を用いて検索します。</p><p>顧客名は、以前のYAML設定で文字列型と「顧客名」という記述で定義されています。</p><p>このツールを使用すると、顧客の注文に関する質問に答えることができます。たとえば、<em>「顧客Fooは2025年10月に何件の注文をしましたか？」</em></p><p>ツールとそのパラメーターの説明は、ユーザーの自然言語リクエストから関連情報を抽出するために不可欠です。この抽出は、大規模言語モデル（LLM）の<strong>関数呼び出し</strong>機能を使用して実行されます。実際には、LLMは、必要な情報を取得するためにどの機能（ツール）を実行する必要があるかを判断し、その機能に適したパラメーターも取得できます。</p><p>詳細については、<a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">Elasticsearchを使用したOpenAIの関数呼び出し</a>に関するAshish Tiwariの記事を読むことをお勧めします。</p><h2>Toolboxサーバーを実行する</h2><p>次のコマンドで、以前のtools.yamlファイルを使用してMCPツールボックスを実行できます。</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>[ツール]</strong> &gt; <strong>[customer-orders]</strong> を選択し、パラメータ<strong>名</strong>に顧客名（例：Gwen Sanders）を挿入して <strong>[ツールを実行]</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クライアントを使用して、MCP Toolbox for Databasesと通信できます。例えば、<a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>というコマンドラインツールを使ってGeminiを使うことができます。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つの製品を1回の注文で、合計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>または、Webインターフェース経由で提供することもできます。</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>eコマースWebサイトからの注文を含むkibana_sample_data_ecommerceデータセットとの対話方法を示しました。この設定ファイルを使用すると、MCP Toolboxサーバーを簡単に実行し、任意のMCPクライアントから接続できます。</p><p>最後に、Gemini CLIをクライアントとして使用してMCP Toolbox for Databasesに接続し、Elasticsearchに保存されているeコマースデータをクエリする方法を示しました。特定の顧客の名前で識別された注文情報を取得するために自然言語クエリを実行しました。</p><p>MCPエコシステムが成長し続けるにつれて、このパターン（安全で本番環境ですぐに使えるインフラストラクチャーに裏打ちされた軽量なツール定義）は、最小限の労力で、ますます有能でデータを認識するエージェントを構築する新しい機会を生み出します。MCP Toolboxは、Elasticのサンプルデータセットを使ってローカルで実験する場合でも、大規模なアプリケーションに検索機能を統合する場合でも、自然言語を使って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">「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コマンドの3つのアップデートを行いました。]]></description>
    <content:encoded><![CDATA[<p>10月にリリースされたElasticsearch 9.2には、データの分析をこれまで以上に高速化、柔軟化、アクセスしやすくするための大きな進化が詰まっています。このリリースの中心となるのは、パイプクエリ言語であるES|QLの重要な機能強化で、エンドユーザーに直接さらに多くの価値をもたらすように設計されています。</p><p>ES|QLを使用してデータ分析ワークフローを変革するElasticsearch 9.2の特徴を見てみましょう。</p><h2>データ相関の革命：よりスマート高速、柔軟になったLookup Join</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_name</code>、<code>environment</code> の <code>service_registry</code> と結合する場合 <code>version:</code></p></li></ul>FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment, version<ul><li><p><strong>式を使用して複雑なjoin述語を活用（テクニカルプレビュー）：</strong></p></li></ul><p>もはや、単純な等式に制限されることはありません。LOOKUP JOINでは、<strong>複数の条件</strong>を相関に指定し、==、!=、、&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：このクエリは、時間とともに変化する地域の価格ポリシーに基づいて支払われるべき金額を計算します。複雑な日付範囲と等しい条件に基づく3つのデータセットを統合し、最終的に <code>due_amount</code>を算出します。2番目のルックアップ結合では、 <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>1000倍高速</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>8.19および9.1でLookup Joinが一般公開となった際、クロスクラスター検索（CCS）のサポートはありませんでした。複数のクラスターにまたがって運用している組織のため、LOOKUP JOINは9.2でCCSとシームレスに統合されるようになりました。ルックアップインデックスを結合したいすべてのリモートクラスターに配置するだけで、ES|QLは自動的にこれらのリモートルックアップインデックスを活用して、リモートデータと結合します。これにより、分散データ分析が簡素化され、Elasticsearch展開全体で一貫したエンリッチメントが保証されます。</p><p>これらの改善により、多様なデータセットを前例のない精度、速度、そして容易さで関連付けることができ、複雑な回避策や前処理のステップなしに、より深く、実用的な洞察を明らかにすることができます。</p><h2>データを簡単に充実化：ルックアップインデックスのためのKibana Discoverユーザーエクスペリエンス</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>並べて見る必要があることもあります。<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by">INLINE STATSを</a><strong>テクニカルプレビュー</strong>機能としてご紹介できることを嬉しく思います。</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="avgDistを使用した平均距離以上のフライト結果のフィルタリング " /><p>このクエリでは、<code>avgDist</code>が各行に対応する<code>Dest</code>(ination)と共に追加され、さらに、フライト情報の列が残っているため、平均を超える距離のフライトに結果をフィルタリングできます。</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 basicでテクニカルプレビューとして利用できます。</p><p>時系列分析は主に、1つ以上のフィルタリングディメンションでスライスされた時間バケット全体のメトリック値を要約する集計クエリに基づいています。ほとんどの集計クエリは、(a) 時系列ごとに値を集計する内部集計関数と、(b) 時系列全体で (a) の結果を結合する外部集計関数という2段階の処理に依存しています。</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">counter</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ドキュメント値を利用した基盤となる列指向ストレージエンジンに保存されます。TSコマンドは、ES|QLコンピュートエンジンを介してベクトル化されたクエリの実行を追加します。クエリパフォーマンスは、同等の<a href="https://www.elastic.co/docs/reference/query-languages/querydsl">DSL</a>クエリと比較して、しばしば1桁以上の向上が見られ、確立されたメトリクス固有のシステムと同等になります。今後、詳細なアーキテクチャおよびパフォーマンス分析も提供する予定ですので、どうぞご期待ください。</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><a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-contains">INCLUDES</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>が追加され、より堅牢なテキストおよびURL処理を実現します。</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全体にわたる数多くのパフォーマンス最適化が含まれています。関数が複数の類似したRLIKEクエリを置き換える場合に、プッシュダウンを使用して<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/where#like-and-rlike">RLIKE (LIST</a>) を高速化しました。<code>RLIKE</code> (LIST) を使用すると、これらのクエリを1つのオートマトンに結合し、複数ではなく1つのオートマトンを適用できます。また、インデックスの並べ替えによるキーワードフィールドの読み込みが高速化され、クエリ全般が最適化されました。これらの改善により、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[OpenSearch の PPL イベント アナライザーの手動アプローチと直接対照的に、ES|QL エディターの高度な機能がどのようにワークフローを加速するかをご覧ください。 
]]></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) の PPL と比較しながら探っていきます。</p><p>違いはすぐに明らかになります。ES|QL エディターは、初心者ユーザーだけでなく、エキスパートレベルのユーザーにも力を与えるインテリジェントなオートコンプリート、コンテキスト ヘルプ、推奨クエリ、およびクラスター間クエリ サポートを提供します。ES|QL オーサリングの思慮深い設計は、たとえば最近のクエリを使用した Kibana ワークフローによる統合クエリ検査と総合的な統合にも反映されています。</p><p>対照的に、PPL にはオートコンプリート、コンテキスト ガイダンス、分散クエリに対する同等のサポートがないため、学習曲線が急峻になり、試行錯誤が増えます。</p><h2>ES|QL の学習と使用を容易にする</h2><p>新しいクエリ言語を使い始めると、圧倒されると感じることがよくあります。<strong>Kibana Discover</strong> に直接組み込まれた ES|QL エディターは<strong> 、</strong> クエリの作成とデバッグをサポートするだけでなく、言語に慣れて使いこなせるようになるまでの時間を短縮することで、そのプロセスを容易にするように設計されています。エディターは日常のタスクの摩擦を軽減するのに役立つため、構文や試行錯誤からソリューションの作成に焦点を移すことができます。これらの原則と、それをエディターにどのように統合したかの詳細については、<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="ユーザーが「clu:g」と入力してリモート インデックスを検索する例。" /><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 がローカル インデックスに対してのみ補完を提供し、提案がプレフィックスに制限されるもう 1 つの例です。" /><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>、Cross-Cluster Search</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>クエリ駆動型分析における最も一般的な摩擦の原因の 1 つは、コンテキストの切り替えです。すでに記述したクエリを思い出す必要がある場合がよくあります。中断されるたびに集中力が途切れ、調査が遅くなります。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 件のクエリを表示、再実行、スター付けすることができ、頻繁に使用するクエリや複雑なクエリを 1 回のクリックで実行できるようになります。保存されたクエリは Kibana 全体に引き継がれ、ダッシュボード、視覚化、アラート、マップと統合されるため、現在の画面を離れたり、コマンドを最初から再入力したりする必要はありません。これにより、反復的な作業が削減され、調査が高速化され、エラーのリスクが最小限に抑えられます。</p><p>たとえば、ユーザーは Discover の ES|QL エディターで最近のクエリを利用できます (そしてスターを付けることもできます)。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49033a46a0805ec8/6a17f321e9ea87a505a9c724/eb0f9fe37b92dec421c394d31ae7d90afebe062e-1421x793.png" alt="Discover の ES|QL エディターで最近のクエリを使用する例 (およびそれらにスターを付ける方法)。" /><p>最近のクエリはダッシュボードに統合されています。</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2f7c745cdc5e5f1/6a17f323a2929932a1d02da9/b84cd3a9bdec58812360d2aba4fc7713363ee3cc-1411x797.png" alt=" 最近のクエリがダッシュボードに統合されました。" /><p>PPL には同等の機能は用意されていないため、ユーザーはクエリを再利用するために手動でのコピー アンド ペーストや外部メモに頼ることになります。この違いは利便性だけではありません。これは、ES|QL を Kibana エコシステム内に真に統合された言語として構築するという 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> で、完全に管理された Elasticsearch<a href="https://www.elastic.co/cloud/serverless"> Serverless</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>1 つのコマンドでローカル環境で数分以内に開始できます。</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 クエリ ビルダーの使用方法を学習します。Ruby コードを使用して ES|QL クエリをより簡単に構築するためのツール。]]></description>
    <content:encoded><![CDATA[<p>最近、Apache 2 ライセンスに基づいて公開された Ruby gem、 <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a>をリリースしました。この gem を使用すると、Elastic の<a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL</a>クエリを慣用的な Ruby で構築し、ES|QL クエリ API で使用できるようになります。ES|QL を使用すると、開発者はクエリを介して Elasticsearch に保存されているデータをフィルタリング、変換、分析できます。「パイプ」（ <code>|</code> ）を使用して、データを段階的に処理します。代わりに gem は Ruby 関数を使用します。これを元のオブジェクトに連鎖して、より複雑なクエリを構築できます。</p><p><strong>ESQL:</strong></p><p><strong>ルビー：</strong></p>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending<h2>インストール</h2><p>この gem は、RubyGems から次のようにインストールできます。</p>gem install elastic-esql<p>または、プロジェクトの Gemfile に追加することもできます。</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>gem は<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>Ruby クライアントで 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 Helper と一緒に使用することもできます。<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>この gem は、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">Elasticsearch を一般的な Ruby ツールと併用する方法</a>について書きました。この gem は、一般的な Ruby ツールと組み合わせて使用し、ES|QL を使用して Elasticsearch をクエリできます。</p><h2>まとめ</h2><p>このライブラリは現在開発中であり、最終的な API はまだ完成していません。現在はテクニカルプレビューとしてリリースされています。現在の API または一般的な使用方法に関してフィードバックがある場合は、遠慮なく<a href="https://github.com/elastic/esql-ruby/issues">新しい問題を開いて</a>ください。Ruby 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[Kibana を使用して地理空間データを Elasticsearch に取り込み、ES|QL で使用できるようにする]]></title>
    <description><![CDATA[Kibana と csv 取り込みプロセッサを使用して、Elasticsearch に地理空間データを取り込んで、Elasticsearch クエリ言語 (ES|QL) で検索する方法。Elasticsearch には強力な地理空間検索機能があり、これが ES|QL にも導入され、使いやすさと OGC への親しみやすさが大幅に向上しました。ただし、これらの機能を使用するには、地理空間データが必要です。]]></description>
    <content:encoded><![CDATA[<p>最近、Elasticsearch の新しい強力な<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-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> の使用方法を説明するブログを公開しました。これらの機能を使用するには、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">空港.csv</a></p><ul><li><p>これには 3 つのデータセットの結合が含まれます。</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">空港都市境界.csv</a></p><ul><li><p>これには、上記の空港名と都市名が 1 つの新しいソースと結合されたものが含まれています。</p><ul><li><p><a href="https://www.openstreetmap.org/">OpenStreetMap</a>の都市境界</p></li></ul></li></ul></li></ul><p>ご想像のとおり、私たちは ES|QL の地理空間機能をテストできるようにすることを目的として、これらのデータ ソースを上記の 2 つのファイルに結合するのに時間を費やしました。これは、特定のデータ ニーズとまったく同じではないかもしれませんが、これによって、何が可能かについてのアイデアが得られると思います。特に、いくつかの興味深い点について説明したいと思います。</p><ul><li><p>地理空間フィールドを含むデータを他のインデックス可能なデータと一緒にインポートする</p></li><li><p><code>geo_point</code>と<code>geo_shape</code>両方のデータをインポートし、クエリで一緒に使用します</p></li><li><p>空間関係を使用して結合できる 2 つのインデックスにデータをインポートする</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 ShapeFiles などの区切りデータをアップロードでき、マップ内に直接図形を描画することもできます。このブログでは、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>フィールドを複数のフィールドに分割する変換をもう 1 つ実行する必要があります。次の構成で、パイプラインに<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>の 2 つの列がある場合、これらを 1 つの<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 では、インデックス化されたデータを「Discover」で探索するのが一般的です。ただし、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>略語</p><p>名前</p><p>場所</p><p>国</p><p>市</p><p>標高</p><p>273418.05776847183</p><p>ハム</p><p>ハンブルク</p><p>ポイント (10.005647830925 53.6320011640866)</p><p>ドイツ</p><p>ノルダーシュテット</p><p>17.0</p><p>337534.653466062</p><p>テキサス</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>アーン</p><p>アーランダ</p><p>ポイント (17.9307299016916 59.6511203397372)</p><p>スウェーデン</p><p>ストックホルム</p><p>38.0</p><p>624274.8274399083</p><p>ダス</p><p>デュッセルドルフ国際空港</p><p>ポイント (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>アムス</p><p>スキポール</p><p>ポイント (4.76437693232812 52.3089323889822)</p><p>オランダ</p><p>ホーフトドルプ</p><p>-3.0</p><p>670864.137958866</p><p>フランス</p><p>フランクフルト国際空港</p><p>ポイント (8.57182286907608 50.0506770895207)</p><p>ドイツ</p><p>フランクフルト</p><p>111.0</p><p>683239.2529970079</p><p>ワウ</p><p>オケシー国際空港</p><p>ポイント (20.9727263383587 52.171026749259)</p><p>ポーランド</p><p>ピアセチュノ</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>データ ビューを選択して、2 番目のレイヤーを追加します。今回は、 <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 の「左結合」に似た動作をし、2 つのデータセット間の空間関係に基づいて、1 つのインデックスの結果を別のインデックスのデータで強化することができます。</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>コマンドをサポートしていないためです。その重要な理由の 1 つは、Elasticsearch が分散システムであり、結合はスケーリングが難しい高コストの操作であることです。ただし、 <code>ENRICH</code>コマンドは、クラスター全体に複製された特別に準備されたエンリッチ インデックスを利用し、各ノードでローカル結合を実行できるため、非常に効率的です。</p><p>これをよりよく理解するために、上記のクエリの<code>ENRICH</code>コマンドに注目してみましょう。</p>FROM airports
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary<p>このコマンドは、Elasticsearch に、 <code>airports</code>インデックスから取得された結果を強化し、元のインデックスの<code>city_location</code>フィールドと、前のいくつかの例で使用した<code>airport_city_boundaries</code>インデックスの<code>city_boundary</code>フィールドの間で<code>intersects</code>結合を実行するように指示します。しかし、この情報の一部はこのクエリでは明確に表示されません。表示されるのはエンリッチポリシーの名前<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>が元のドキュメントに追加するフィールドであることがわかります。これらのフィールドの 1 つである<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>と非常に似ていますが、2 つのインデックスを結合することはできず、左側の通常のインデックスと、右側の特別に準備されたエンリッチ インデックスのみを結合できます。</p><p>どちらの場合でも、取り込みパイプライン用でも ES|QL で使用する場合でも、エンリッチ インデックスとポリシーを設定するためにいくつかの準備手順を実行する必要があります。上記ですでに<code>airport_city_boundaries</code>インデックスをインポートしましたが、これを<code>ENRICH</code>コマンドのエンリッチ インデックスとして直接使用することはできません。まず、次の 2 つの手順を実行する必要があります。</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.13908685930073331.024386116624648)</p><p>126</p><p>ヌル</p><p>ポイント (-83.1039831787347842.300230911932886)</p><p>3</p><p>デトロイト</p><p>ポイント (39.74537850357592 47.21613017376512)</p><p>3</p><p>городской округ Батайск</p><p>ポイント (-156.8098678719252320.476673701778054)</p><p>3</p><p>ハワイ</p><p>ポイント (-73.9451533276587740.70366442203522）</p><p>3</p><p>ニューヨーク市</p><p>ポイント (-83.1039831787347842.300230911932886)</p><p>3</p><p>デトロイト</p><p>ポイント (-76.6687301918864324.306286952923983)</p><p>2</p><p>ニュープロビデンス</p><p>ポイント (-3.0252167768776417 51.39245774131268)</p><p>2</p><p>カーディフ</p><p>ポイント (-115.4099348466843432.73126147687435）</p><p>2</p><p>メヒカリ市</p><p>ポイント (41.790108773857355 50.302146775648)</p><p>2</p><p>Центральный район</p><p>ポイント (-73.8890273217111845.57078813901171)</p><p>2</p><p>モントリオール</p><p>また、最も頻繁に見つかった地域は<code>null</code>であったことにも気付くでしょう。これは何を意味するのでしょうか?このコマンドを SQL の「左結合」に例えたことを思い出してください。つまり、空港に一致する都市境界が見つからない場合でも、その空港は返されますが、 <code>airport_city_boundaries</code>インデックスのフィールドには<code>null</code>値が含まれます。一致する<code>city_boundary</code>が見つからなかった空港が 125 か所あり、 <code>region</code>フィールドが<code>null</code>である一致する空港が 1 か所あることがわかりました。これにより、結果に<code>region</code>が含まれない空港が 126 件見つかりました。ユースケースですべての空港を都市の境界に一致させる必要がある場合は、ギャップを埋めるために追加のデータを入手する必要があります。次の 2 つの点を決定する必要があります。</p><ul><li><p><code>airport_city_boundaries</code>インデックス内のどのレコードに<code>city_boundary</code>フィールドがありませんか</p></li><li><p><code>airports</code>インデックス内のどのレコードが<code>ENRICH</code>コマンドを使用しても一致しないか (つまり、交差しない）</p></li></ul><h2>Kibana Maps の地理空間データに ES|QL を使用する</h2><p>Kibana は、マップ アプリケーションに Spatial 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="キバナ 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>ブログでは、Elasticsearch 8.14 以降で利用可能な<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 には強力な地理空間検索機能があり、これが ES|QL にも導入され、使いやすさと OGC への親しみやすさが大幅に向上しました。]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch は長年にわたって強力な<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/geospatial-analysis.html">地理空間検索および分析機能を</a>提供してきましたが、その API は一般的な GIS ユーザーが使い慣れたものとはまったく異なっていました。過去 1 年間で、SQL と同じくらい、あるいは 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 で地理空間検索機能が追加されたことにより大幅に強化されました。<a href="https://en.wikipedia.org/wiki/Open_Geospatial_Consortium">さらに重要なことは、このサポートは、PostGIS などの他の空間データベースで使用されている Open</a> Geospatial Consortium (OGC) の<a href="https://en.wikipedia.org/wiki/Simple_Features"> Simple Feature Access</a> 標準に厳密に準拠するように設計されているため、これらの標準に精通している GIS 専門家にとって非常に使いやすくなっていることです。</p><p>このブログでは、ES|QL を使用して地理空間検索を実行する方法と、それを SQL およびクエリ 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>略語</p><p>空港</p><p>地域</p><p>市</p><p>都市の場所</p><p>シックス</p><p>三亜フェニックス国際空港</p><p>天外区</p><p>三亜</p><p>点(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 ユーザー、特に Spatial 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>から始めるのではなく、1 つ以上の<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>ポストGIS</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>ポストGIS</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 では、次の 4 つの OGC 空間検索関数が導入されています。</p><p>ES|QL</p><p>ポストGIS</p><p>説明</p><p>ST_INTERSECTS</p><p>ST_交差</p><p>2 つのジオメトリが交差する場合は true を返し、そうでない場合は false を返します。</p><p>ST_DISJOINT</p><p>ST_分離</p><p>2 つのジオメトリが交差しない場合は true を返し、そうでない場合は false を返します。ST_INTERSECTS の逆。</p><p>ST_CONTAINS</p><p>ST_Contains</p><p>1 つのジオメトリに別のジオメトリが含まれている場合は true を返し、含まれていない場合は false を返します。</p><p>ST_WITHIN</p><p>ST_以内</p><p>1 つのジオメトリが別のジオメトリ内にある場合は true を返し、そうでない場合は false を返します。ST_CONTAINS の逆。</p><p>これらの関数は PostGIS の対応する関数と同様に動作し、同じように使用されます。たとえば、 <code>ST_INTERSECTS</code> 2 つのジオメトリが交差する場合は true を返し、そうでない場合は false を返します。上記の表のドキュメント リンクに従うと、すべての 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>コマンドで空間関数を使用する例を見てきました。他にどこで意味を成すのでしょうか?非常に便利な場所の 1 つは、 <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>英国</p><p>アイスランド</p><p>英国内</p><p>アイスランド内</p><p>ポイント (-21.94663446396589364.13187285885215)</p><p>1</p><p>間違い</p><p>true</p><p>間違い</p><p>true</p><p>ポイント (-2.597342072712148 54.33551226578214)</p><p>17</p><p>true</p><p>間違い</p><p>true</p><p>間違い</p><p>ポイント (0.04453958108176276 23.74658354606057)</p><p>873</p><p>間違い</p><p>間違い</p><p>間違い</p><p>間違い</p><p>実際、これらの関数は、そのシグネチャが意味を成すクエリのどの部分でも使用できます。これらはすべて、リテラル空間オブジェクトまたは空間型のフィールドのいずれかである 2 つの引数を取り、ブール値を返します。重要な考慮事項の 1 つは、ジオメトリの座標参照システム (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>さらに、どちらの引数も、空間リテラルまたはフィールドを任意の順序で指定できます。2 つのフィールド、2 つのリテラル、フィールドとリテラル、またはリテラルとフィールドを指定することもできます。唯一の要件は、タイプに互換性があることです。たとえば、次のクエリは同じインデックス内の 2 つのフィールドを比較します。</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>市内</p><p>少し</p><p>29</p><p>間違い</p><p>多くの</p><p>740</p><p>true</p><p>さらに興味深い質問は、空港の場所が、その空港がサービスを提供する都市の境界内にあるかどうかです。ただし、空港の位置は、都市の境界を含むインデックスとは異なるインデックスに存在します。これには、これら 2 つの個別のインデックスからデータを効果的にクエリして相関させる方法が必要です。</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 の「左結合」に似た動作をし、2 つのデータセット間の空間関係に基づいて、1 つのインデックスの結果を別のインデックスのデータで強化することができます。</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>最小wkt</p><p>最大wkt</p><p>地域</p><p>ポイント (-32.5609347096071932.598117914802714)</p><p>90</p><p>207</p><p>207</p><p>ヌル</p><p>ポイント (-73.9451533276587740.70366442203522）</p><p>9</p><p>438</p><p>438</p><p>ニューヨーク市</p><p>ポイント (-83.1039831787347842.300230911932886)</p><p>9</p><p>473</p><p>473</p><p>デトロイト</p><p>ポイント (-156.302024586126220.176383580081165)</p><p>5</p><p>307</p><p>803</p><p>ハワイ</p><p>ポイント (-73.8890273217111845.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>このコマンドは、Elasticsearch に、 <code>airports</code>インデックスから取得された結果を強化し、元のインデックスの<code>city_location</code>フィールドと、前のいくつかの例で使用した<code>airport_city_boundaries</code>インデックスの<code>city_boundary</code>フィールドの間で<code>intersects</code>結合を実行するように指示します。しかし、この情報の一部はこのクエリでは明確に表示されません。表示されるのはエンリッチポリシーの名前<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>が元のドキュメントに追加するフィールドであることがわかります。これらのフィールドの 1 つである<code>region</code>は、実際には<code>STATS</code>コマンドのグループ化キーとして使用されていましたが、これはこの「左結合」機能がなければ実行できませんでした。エンリッチ ポリシーの詳細については、<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/enrich-setup.html">エンリッチのドキュメント</a>を参照してください。これらのドキュメントを読んでいくと、取り込みパイプラインを構成することによって、インデックス作成時にデータを拡充するための拡充インデックスの使用について説明されていることに気付くでしょう。ES|QL では、 <code>ENRICH</code>コマンドがクエリ時に機能するため、これは必要ありません。必要なデータとエンリッチポリシーを使用してエンリッチインデックスを準備し、ES|QL クエリで<code>ENRICH</code>コマンドを使用するだけで十分です。</p><p>また、最も頻繁に見つかった地域は<code>null</code>であったことにも気付くでしょう。これは何を意味するのでしょうか?このコマンドを SQL の「左結合」に例えたことを思い出してください。つまり、空港に一致する都市境界が見つからない場合でも、その空港は返されますが、 <code>airport_city_boundaries</code>インデックスのフィールドには<code>null</code>値が含まれます。一致する<code>city_boundary</code>が見つからなかった空港が 89 か所あり、 <code>region</code>フィールドが<code>null</code>である一致する空港が 1 か所あることがわかりました。これにより、結果に<code>region</code>が含まれない空港が 90 件見つかりました。もう 1 つの興味深い点は、 <code>MV_EXPAND</code>コマンドの必要性です。これが必要なのは、 <code>ENRICH</code>コマンドが入力行ごとに複数の結果を返す場合があり、 <code>MV_EXPAND</code>これらの結果を結果ごとに 1 つずつ複数の行に分割するのに役立つためです。これにより、「ハワイ」が異なる<code>min_wkt</code>と<code>max_wkt</code>結果を表示する理由も明らかになります。同じ名前でありながら境界が異なる複数の地域が存在したためです。</p><h2>Kibanaマップ</h2><p>Kibana は、マップ アプリケーションに Spatial 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="キバナ 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>上記の 2 つの例では、さらに別の空間関数<code>ST_CENTROID_AGG</code>が組み込まれていることに気付いたかもしれません。これは、 <code>STATS</code>コマンドで使用される集計関数であり、ES|QL に追加する予定の多くの空間分析機能の最初のものです。もっと詳しく紹介できるようになったら、ブログで紹介します!</p><p>その前に、私たちが取り組んできた特に興味深い機能、つまり Elasticsearch で最もよく使用される空間検索機能の 1 つである空間距離検索を実行する機能について詳しく説明したいと思います。距離検索の構文がどのようになるか想像できますか?おそらく 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> ）を使用して、データを段階的に操作および変換します。このアプローチにより、ユーザーは一連の操作を構成でき、1 つの操作の出力が次の操作の入力となり、複雑なデータ変換と分析が可能になります。</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.4 MB) の<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 Books Reviews データセット</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.9 GB の<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 秒と 28 MB の RAM が必要でした。提案されたマッピングでは、Elasticsearch のインデックス サイズは約 62 MB になります。</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>以下は、ユーザーのランキングレビュー順に並べられた、Stephen King が書いたトップ 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 件の結果 (すべて Stephen King の書籍) があります。</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> 2 番目の本 (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>