<?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[Florian Bernd - 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[Florian Bernd - 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/author/florian-bernd</link>
    </image>
    <link>https://www.elastic.co/jp/search-labs/author/florian-bernd</link>
    <atom:link href="https://www.elastic.co/jp/search-labs/rss/author/florian-bernd.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[jp]]></language>
    <lastBuildDate>Wed, 23 Sep 2026 04:50:25 GMT</lastBuildDate>
  <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[AIエージェント開発におけるMicrosoftセマンティックカーネル向けElasticsearch Vector Store Connectorの使い方]]></title>
    <description><![CDATA[Microsoft Semantic Kernel は、AI エージェントを簡単に構築し、最新の AI モデルを C#、Python、または Java コードベースに統合できる軽量のオープンソース開発キットです。Semantic Kernel Elasticsearch Vector Store Connector のリリースにより、AI エージェントの構築に Semantic Kernel を使用する開発者は、Semantic Kernel の抽象化を引き続き使用しながら、Elasticsearch をスケーラブルなエンタープライズ グレードのベクター ストアとしてプラグインできるようになりました。]]></description>
    <content:encoded><![CDATA[<p><a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Microsoft Semantic Kernel</a> チームと連携して、<a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/"> Microsoft Semantic</a> Kernel (.NET) ユーザー向けに<a href="https://github.com/elastic/semantic-kernel-net/"> Semantic Kernel Elasticsearch Vector Store Connector が利用可能になったことを発表します。</a>セマンティック カーネルは、ベクター ストアからのより関連性の高いデータ駆動型の応答を使用して大規模言語モデル (LLM) を強化する機能など、エンタープライズ グレードの AI エージェントの構築を簡素化します。Semantic Kernel は、Elasticsearch などの Vector Stores と対話するためのシームレスな抽象化レイヤーを提供し、レコードのコレクションの作成、一覧表示、削除や、個々のレコードのアップロード、取得、削除などの重要な機能を提供します。</p><p><a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">すぐに使用できるセマンティック カーネル Elasticsearch ベクター ストア コネクタは、</a>セマンティック カーネル<a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">ベクター ストアの抽象化</a>をサポートしており、開発者は AI エージェントの構築時に Elasticsearch をベクター ストアとしてプラグインすることが非常に簡単になります。</p><p>Elasticsearch はオープンソース コミュニティに強固な基盤を持ち、最近<a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">AGPL ライセンスを</a>採用しました。これらのツールは、オープンソースの Microsoft Semantic Kernel と組み合わせることで、強力なエンタープライズ対応ソリューションを提供します。このコマンド<code>curl -fsSL https://elastic.co/start-local | sh </code> (詳細については<a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a>を参照) を実行して数分で Elasticsearch を起動し、ローカルで開始できます。その後、AI エージェントを本番稼働させながら、<a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">クラウドホスト バージョン</a>または<a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">セルフホスト</a>バージョンに移行できます。</p><p>このブログでは、Semantic Kernel を使用する際に<a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector を</a>使用する方法について説明します。コネクタの Python バージョンは将来提供される予定です。</p><h2>高レベルのシナリオ: Semantic Kernel と Elasticsearch を使用した RAG アプリの構築</h2><p>次のセクションでは例を見ていきます。大まかに言うと、ユーザーの質問を入力として受け取り、回答を返す RAG (Retrieval Augmented Generation) アプリケーションを構築しています。LLM として Azure OpenAI (<a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">ローカル LLM</a>も使用可能)、ベクター ストアとして Elasticsearch、すべてのコンポーネントを結び付けるフレームワークとして Semantic Kernel (.net) を使用します。</p><p>RAG アーキテクチャに精通していない場合は、次の記事で簡単に概要を把握できます: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a> 。</p><p>回答は、Elasticsearch vectorstore から取得され、質問に関連するコンテキストが入力する LLM によって生成されます。応答には、LLM によってコンテキストとして使用されたソースも含まれます。</p><h3>RAGの例</h3><p>この具体的な例では、社内のホテル データベースに保存されているホテルについてユーザーが質問できるアプリケーションを構築します。ユーザーは例えばさまざまな基準に基づいて特定のホテルを検索したり、ホテルのリストを要求したりできます。</p><p>サンプル データベースでは、100 件のエントリを含む<a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">ホテルのリスト</a>を生成しました。コネクタのデモをできるだけ簡単に試せるように、サンプル サイズは意図的に小さくなっています。実際のアプリケーションでは、特に非常に大量のデータを扱う場合、Elasticsearch コネクタは `InMemory` ベクトル ストア実装などの他のオプションよりも優位性を発揮します。</p><p>完全なデモ アプリケーションは、Elasticsearch ベクター ストア コネクタ<a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">リポジトリ</a>にあります。</p><p>まず、必要な NuGet パッケージと using ディレクティブをプロジェクトに追加することから始めましょう。</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Elastic.Clients.Elasticsearch;
using Elastic.Transport;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.VectorData;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Data;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;<p>これで、データ モデルを作成し、セマンティック カーネル固有の属性を指定して、ストレージ モデル スキーマとテキスト検索のヒントを定義できるようになりました。</p>/// &lt;summary&gt;
/// Data model for storing a "hotel" with a name, a description, a  description embedding and an optional reference link.
/// &lt;/summary&gt;
public sealed record Hotel
{
	[VectorStoreRecordKey]
	public required string HotelId { get; set; }

	[TextSearchResultName]
	[VectorStoreRecordData(IsFilterable = true)]
	public required string HotelName { get; set; }

	[TextSearchResultValue]
	[VectorStoreRecordData(IsFullTextSearchable = true)]
	public required string Description { get; set; }

	[VectorStoreRecordVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, IndexKind.Hnsw)]
	public ReadOnlyMemory&lt;float&gt;? DescriptionEmbedding { get; set; }

	[TextSearchResultLink]
	[VectorStoreRecordData]
	public string? ReferenceLink { get; set; }
}<p>ストレージ モデル スキーマ属性 (`VectorStore*`) は、Elasticsearch Vector Store Connector の実際の使用に最も関連しています。具体的には次のようになります。</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> レコード クラスのプロパティを、ベクトル ストアにレコードが格納されるキーとしてマークします。</p></li><li><p><code>VectorStoreRecordData</code> レコード クラスのプロパティを 'data' としてマークします。</p></li><li><p><code>VectorStoreRecordVector</code> レコード クラスのプロパティをベクトルとしてマークします。</p></li></ul><p>これらの属性はすべて、ストレージ モデルをさらにカスタマイズするために使用できるさまざまなオプション パラメーターを受け入れます。たとえば、 <code>VectorStoreRecordKey </code>の場合、異なる距離関数や異なるインデックス タイプを指定することが可能です。</p><p>テキスト検索属性 ( <code>TextSearch*</code> ) は、この例の最後のステップで重要になります。これらについては後ほど説明します。</p><p>次のステップでは、セマンティック カーネル エンジンを初期化し、コア サービスへの参照を取得します。実際のアプリケーションでは、サービス コレクションに直接アクセスするのではなく、<a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">依存性注入を</a>使用する必要があります。同じことがハードコードされた構成とシークレットにも当てはまります。これらは、代わりに<a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">構成プロバイダー</a>を使用して読み取る必要があります。</p>var builder = Host.CreateApplicationBuilder(args);

// Register AI services.
var kernelBuilder = builder.Services.AddKernel();

kernelBuilder.AddAzureOpenAIChatCompletion("gpt-4o", "https://my-service.openai.azure.com", "my_token");

kernelBuilder.AddAzureOpenAITextEmbeddingGeneration("ada-002", "https://my-service.openai.azure.com", "my_token");

// Register text search service.
kernelBuilder.AddVectorStoreTextSearch&lt;Hotel&gt;();

// Register Elasticsearch vector store.
var elasticsearchClientSettings = new ElasticsearchClientSettings(new Uri("https://my-elasticsearch-instance.cloud"))
    .Authentication(new BasicAuthentication("elastic", "my_password"));

kernelBuilder.AddElasticsearchVectorStoreRecordCollection&lt;string, Hotel&gt;("skhotels", elasticsearchClientSettings);

// Build the host.
using var host = builder.Build();

// For demo purposes, we access the services directly without using a DI context.

var kernel = host.Services.GetService&lt;Kernel&gt;()!;
var embeddings = host.Services.GetService&lt;ITextEmbeddingGenerationService&gt;()!;
var vectorStoreCollection = host.Services.GetService&lt;IVectorStoreRecordCollection&lt;string, Hotel&gt;&gt;()!;

// Register search plugin.
var textSearch = host.Services.GetService&lt;VectorStoreTextSearch&lt;Hotel&gt;&gt;()!;
kernel.Plugins.Add(textSearch.CreateWithGetTextSearchResults("SearchPlugin"));<p><code>vectorStoreCollection</code>サービスを使用してコレクションを作成し、いくつかの<a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">デモ レコード</a>を取り込むことができるようになりました。</p>await vectorStoreCollection.CreateCollectionIfNotExistsAsync();

// CSV format: ID;Hotel Name;Description;Reference Link
var hotels = (await File.ReadAllLinesAsync("hotels.csv"))
    .Select(x =&gt; x.Split(';'));

foreach (var chunk in hotels.Chunk(25))
{
    var descriptionEmbeddings = await embeddings.GenerateEmbeddingsAsync(chunk.Select(x =&gt; x[2]).ToArray());
    
    for (var i = 0; i &lt; chunk.Length; ++i)
    {
        var hotel = chunk[i];
        await vectorStoreCollection.UpsertAsync(new Hotel
        {
            HotelId = hotel[0],
            HotelName = hotel[1],
            Description = hotel[2],
            DescriptionEmbedding = descriptionEmbeddings[i],
            ReferenceLink = hotel[3]
        });
    }
}<p>これは、セマンティック カーネルが、複雑なベクトル ストアの使用を、いくつかの単純なメソッド呼び出しにまで削減する方法を示しています。</p><p>内部的には、Elasticsearch に新しいインデックスが作成され、必要なすべてのプロパティ マッピングが作成されます。その後、データ セットは完全に透過的にストレージ モデルにマッピングされ、最終的にインデックスに保存されます。以下は Elasticsearch でのマッピングの様子です。</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p><code>embeddings.GenerateEmbeddingsAsync()</code>は、構成された Azure AI Embeddings Generation サービスを透過的に呼び出しました。</p><p>このデモの最後のステップでは、さらに多くの魔法が観察できます。</p><p><code>InvokePromptAsync</code>を 1 回呼び出すだけで、ユーザーがデータについて質問したときに、次のすべての操作が実行されます。</p><p>1.ユーザーの質問の埋め込みが生成される</p><p>2. ベクトルストアで関連するエントリを検索する</p><p>3. クエリの結果はプロンプトテンプレートに挿入されます</p><p>4. 最終プロンプトの形式で実際のクエリがAIチャット補完サービスに送信されます。</p>// Invoke the LLM with a template that uses the search plugin to
// 1. get related information to the user query from the vector store
// 2. add the information to the LLM prompt.
var response = await kernel.InvokePromptAsync(
    promptTemplate: """
                    Please use this information to answer the question:
                    {{#with (SearchPlugin-GetTextSearchResults question)}}
                      {{#each this}}
                        Name: {{Name}}
                        Value: {{Value}}
                        Source: {{Link}}
                        -----------------
                      {{/each}}
                    {{/with}}
                    
                    Include the source of relevant information in the response.

                    Question: {{question}}
                    """,
    arguments: new KernelArguments
    {
        { "question", "Please show me all hotels that have a rooftop bar." },
    },
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory());<p>以前データ モデルで定義した<code>TextSearch*</code>属性を覚えていますか?これらの属性により、プロンプト テンプレート内の対応するプレースホルダーを使用できるようになります。これらのプレースホルダーには、ベクター ストア内のエントリからの情報が自動的に入力されます。</p><p>「屋上バーがあるホテルをすべて教えてください。」という質問に対する最終的な回答は次のとおりです。</p>Console.WriteLine(response.ToString());

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>答えは、hotels.csvの次のエントリを正しく参照しています。</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>この例は、Microsoft Semantic Kernel を使用すると、よく考えられた抽象化によって複雑さが大幅に軽減され、非常に高いレベルの柔軟性が実現されることを示しています。たとえば、コードの 1 行を変更するだけで、コードの他の部分をリファクタリングすることなく、使用されているベクトル ストアまたは AI サービスを置き換えることができます。</p><p>同時に、このフレームワークは、`InvokePrompt` 関数やテンプレート、検索プラグイン システムなどの膨大な高レベル機能を提供します。</p><p>完全なデモ アプリケーションは、Elasticsearch ベクター ストア コネクタ リポジトリにあります。</p><h2>Elasticsearchで他に何ができるのか</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearchの新しいsemantic_textマッピング：セマンティック検索の簡素化</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Elasticsearch におけるセマンティックリランキング（リトリーバー使用）</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">高度なRAGテクニックパート1：データ処理</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">高度なRAGテクニックパート2：クエリとテスト</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Llama 3オープンソースとElasticでRAGを構築する</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">LangGraph、LLaMA3、Elasticsearchベクターストアを使用してローカルエージェントをゼロから構築するチュートリアル</a></p></li></ul><h2>Elasticsearch とセマンティックカーネル: 次は何?</h2><ul><li><p>.NET で GenAI アプリケーションを構築する際に、Elasticsearch ベクター ストアを Semantic Kernel に簡単にプラグインする方法を示しました。次回の Python 統合にご期待ください。</p></li><li><p>Semantic Kernel は<a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">ハイブリッド検索</a>などの高度な検索機能の抽象化を構築するため、Elasticsearch Connect を使用すると、.NET 開発者は Semantic Kernel を使用しながらそれらを簡単に実装できるようになります。</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Vector Database]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elasticsearch .NET クライアントの進化: NEST から Elastic.Clients.Elasticsearch へ]]></title>
    <description><![CDATA[Elasticsearch .NET クライアントの進化と、NEST から Elastic.Clients.Elasticsearch への移行について学習します。]]></description>
    <content:encoded><![CDATA[<h2>.NET クライアントと NEST の紹介</h2><p>.NET の世界では、Elasticsearch との統合は長い間<code>NEST</code>ライブラリによって促進されてきました。このライブラリは、開発者が Elasticsearch の強力な検索機能や分析機能と対話するための堅牢なインターフェースとして機能します。<code>NEST</code> 、Elasticsearch 用のネイティブ .NET クライアントの必要性から生まれ、豊富な機能セットとシームレスな統合機能により、開発者の間で急速に人気を博しました。</p><p><a href="https://github.com/elastic/elasticsearch/commit/ec72ca8b7a115f9b2eea3c76c518062b99a1d015">Elasticsearch の最初のコミット</a> から約<a href="https://github.com/elastic/elasticsearch-net/commit/724f932ba598915c8c3c35a19827fdfa4f782c1d"> 14 年</a> とわずか 8 か月間、NEST は Elasticsearch のリリースを忠実に追跡してきました。</p><h2>NEST から Elastic.Clients.Elasticsearch への移行</h2><p>Elasticsearch が進化するにつれて、 <code>NEST</code>の複雑なコードベースの維持がますます困難になりました。私たちは、クライアント開発に対してより持続可能なアプローチが必要であることを認識し、.NET クライアントを根本から再設計する取り組みを始めました。最初のベータ版をリリースするのにほぼ 1 年かかり、すべてのサーバー エンドポイントのサポートに近づくまでにさらに 1 年かかりました。最も難しい決断の 1 つは、保守性を優先するためにライブラリの範囲を縮小することでした。</p><p>現在の Elasticsearch API サーフェスのサイズを考えると、450 を超えるエンドポイントと 3000 近くのタイプ (リクエスト、レスポンス、クエリ、集計など) を手動で管理することはもはや現実的ではありません。言語クライアントと Elasticsearch 間の一貫性、正確性、およびタイムリーな調整を保証するために、8.x クライアントと多くの関連タイプは、<a href="https://github.com/elastic/elasticsearch-specification">共有仕様</a>から自動的にコード生成されるようになりました。これは、Azure、AWS、Google Cloud Platform などの SDK とライブラリ間でクライアントとサーバーの整合性を維持するための一般的なソリューションです。</p><p>Elasticsearch 仕様は、 <code>NEST</code>から型マッピングをエクスポートすることによって 8 年以上前に作成されましたが、クライアント チームの懸命な努力により、同じ仕様を使用して新しい .NET クライアント (および Java、Go などの複数の他の言語のクライアント) を作成できるようになりました。</p><p>バージョン 8.13 のリリースにより、 <code>NEST</code>の廃止が正式に発表されました。Elasticsearch が<code>Elastic.Clients.Elasticsearch</code>に移行するにつれて、 <code>NEST</code>段階的に廃止され、年末にサポートが終了します。スムーズな移行を実現し、潜在的な中断を最小限に抑えるために、開発者は移行作業を早期に開始することを強くお勧めします。<code>Elastic.Clients.Elasticsearch</code>を採用すると、最新のサーバー機能との互換性が確保されるだけでなく、廃止される機能に対してもアプリケーションの将来性が保証されます。</p><h2>Elastic.Clients.Elasticsearch: 機能と変更点の概要</h2><p>v8 クライアント<code>Elastic.Clients.Elasticsearch</code>に切り替えると、Elasticsearch 8 のすべての新機能にアクセスできるようになり、ライブラリ自体も大幅に最新化されますが、以前のバージョンに比べて便利な機能が少なくなります。新しいコア機能には、クエリ言語<code>ES|QL</code> 、最新の機械学習 (ML) 機能、OpenTelemetry 互換アクティビティの形式での改善された診断機能などがあります。バージョン 8.13 以降、 <code>Elastic.Clients.Elasticsearch</code> Elasticsearch 8 のほぼすべてのサーバー機能をサポートします。</p><p>たとえば、重要な重大な変更は集計に関連しています。<code>NEST</code>では、Fluent API の使用は次のようになります。</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
    .Children&lt;CommitActivity&gt;("name_of_child_agg", child =&gt; child
        .Aggregations(childAggs =&gt; childAggs
            .Average("average_per_child", avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor))
            .Max("max_per_child", max =&gt; max.Field(p =&gt; p.ConfidenceFactor))
            .Min("min_per_child", min =&gt; min.Field(p =&gt; p.ConfidenceFactor))
        )
    )
)
<p>一方、v8 クライアントでは次の構文が必要です。</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
	.Add("name_of_child_agg", agg =&gt; agg
		.Children(_ =&gt; {})
		.Aggregations(childAggs =&gt; childAggs
			.Add("average_per_child", agg =&gt; agg.Avg(avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor)))
			.Add("max_per_child", agg =&gt; agg.Max(max =&gt; max.Field(p =&gt; p.ConfidenceFactor)))
			.Add("min_per_child", agg =&gt; agg.Min(min =&gt; min.Field(p =&gt; p.ConfidenceFactor)))
		)
	)
)
<h2>NEST v7 から .NET クライアント v8 への移行</h2><p>包括的な移行ガイドは、こちらでご覧いただけます:<a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.18/migration-guide.html">移行ガイド: NEST v7 から .NET Client v8 へ</a>。</p><h2>参考資料</h2><ul><li><p><a href="https://github.com/elastic/elasticsearch-net">GitHub の Elastic.Clients.Elasticsearch v8 クライアント</a></p></li><li><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch">NuGet 上の Elastic.Clients.Elasticsearch v8 クライアント</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/net-client-evolution</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/net-client-evolution</guid>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Florian Bernd]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltac0adcdaa85d703f/6a17f5816df731db170a1079/d09f7f5cb468d5e84f7f4636d92b3476e6604e11-1024x1024.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>