<?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[.NET - 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[.NET - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/de/search-labs/blog/category/dot-net-programming</link>
    </image>
    <link>https://www.elastic.co/de/search-labs/blog/category/dot-net-programming</link>
    <atom:link href="https://www.elastic.co/de/search-labs/rss/category/dot-net-programming.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[de]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 16:26:14 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Wie man den Elasticsearch Vector Store Connector für Microsoft Semantic Kernel zur Entwicklung von KI-Agenten verwendet]]></title>
    <description><![CDATA[Microsoft Semantic Kernel ist ein leichtgewichtiges Open-Source-Entwicklungskit, mit dem Sie auf einfache Weise KI-Agenten erstellen und die neuesten KI-Modelle in Ihre C#-, Python- oder Java-Codebasis integrieren können. Mit der Veröffentlichung des Semantic Kernel Elasticsearch Vector Store Connectors können Entwickler, die Semantic Kernel zum Erstellen von KI-Agenten verwenden, Elasticsearch nun als skalierbaren Vektorspeicher der Enterprise-Klasse einbinden und gleichzeitig die Abstraktionen von Semantic Kernel weiterhin nutzen.]]></description>
    <content:encoded><![CDATA[<p>In Zusammenarbeit mit dem <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Microsoft Semantic Kernel-</a> Team geben wir die Verfügbarkeit des <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a> für <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Microsoft Semantic Kernel</a> (.NET)-Benutzer bekannt. Der semantische Kernel vereinfacht die Entwicklung von KI-Agenten auf Unternehmensebene und bietet unter anderem die Möglichkeit, große Sprachmodelle (LLMs) mit relevanteren, datengesteuerten Antworten aus einem Vektorspeicher zu erweitern. Semantic Kernel bietet eine nahtlose Abstraktionsschicht für die Interaktion mit Vektorspeichern wie Elasticsearch und stellt wichtige Funktionen wie das Erstellen, Auflisten und Löschen von Datensatzsammlungen sowie das Hochladen, Abrufen und Löschen einzelner Datensätze bereit.</p><p>Der <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">sofort einsatzbereite Semantic Kernel Elasticsearch Vector Store Connector</a> unterstützt die <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">Abstraktionen des Semantic Kernel Vector Stores</a> , was es Entwicklern sehr einfach macht, Elasticsearch als Vector Store beim Erstellen von KI-Agenten einzubinden.</p><p>Elasticsearch hat eine starke Verankerung in der Open-Source-Community und hat vor Kurzem die <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">AGPL-Lizenz</a> übernommen. In Kombination mit dem Open-Source-Microsoft Semantic Kernel bieten diese Tools eine leistungsstarke, unternehmensgerechte Lösung. Sie können lokal beginnen, indem Sie Elasticsearch in wenigen Minuten mit diesem Befehl <code>curl -fsSL https://elastic.co/start-local | sh </code>starten (siehe <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> für Details) und dann auf <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">Cloud-gehostete</a> oder <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">selbstgehostete</a> Versionen umsteigen, während Sie Ihre KI-Agenten produktiv einsetzen.</p><p>In diesem Blogbeitrag zeigen wir Ihnen, wie Sie <a href="https://github.com/elastic/semantic-kernel-net/">den Semantic Kernel Elasticsearch Vector Store Connector</a> in Verbindung mit Semantic Kernel verwenden. Eine Python-Version des Konnektors wird zu einem späteren Zeitpunkt verfügbar sein.</p><h2>Szenario auf hoher Ebene: Entwicklung einer RAG-Anwendung mit Semantic Kernel und Elasticsearch</h2><p>Im folgenden Abschnitt gehen wir ein Beispiel durch. Im Wesentlichen entwickeln wir eine RAG-Anwendung (Retrieval Augmented Generation), die eine Frage des Benutzers als Eingabe entgegennimmt und eine Antwort zurückgibt. Wir verwenden Azure OpenAI (<a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">ein lokales LLM</a> kann ebenfalls verwendet werden) als LLM, Elasticsearch als Vektorspeicher und Semantic Kernel (.net) als Framework, um alle Komponenten miteinander zu verbinden.</p><p>Falls Sie mit RAG-Architekturen nicht vertraut sind, können Sie sich mit diesem Artikel eine kurze Einführung verschaffen: <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>Die Antwort wird vom LLM generiert, der mit Kontextinformationen gespeist wird, die für die Frage relevant sind und aus dem Elasticsearch-Vektorspeicher abgerufen werden. Die Antwort enthält auch die Quelle, die vom LLM als Kontext verwendet wurde.</p><h3>RAG-Beispiel</h3><p>In diesem konkreten Beispiel entwickeln wir eine Anwendung, die es Benutzern ermöglicht, Fragen zu Hotels zu stellen, die in einer internen Hoteldatenbank gespeichert sind. Der Benutzer könnte z.B. Suchen Sie nach einem bestimmten Hotel anhand verschiedener Kriterien oder fordern Sie eine Liste von Hotels an.</p><p>Für die Beispieldatenbank haben wir eine <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">Liste von Hotels</a> mit 100 Einträgen generiert. Die Stichprobengröße ist bewusst klein gehalten, damit Sie die Konnektordemo so einfach wie möglich ausprobieren können. In einer realen Anwendung würde der Elasticsearch-Connector seine Vorteile gegenüber anderen Optionen, wie der `InMemory`-Vektorspeicherimplementierung, insbesondere bei der Arbeit mit extrem großen Datenmengen zeigen.</p><p>Die vollständige Demo-Anwendung finden Sie im Elasticsearch Vector Store Connector- <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">Repository</a>.</p><p>Beginnen wir damit, die erforderlichen NuGet-Pakete und die verwendeten Direktiven zu unserem Projekt hinzuzufügen:</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>Wir können nun unser Datenmodell erstellen und es mit semantischen Kernel-spezifischen Attributen ausstatten, um das Speichermodellschema und einige Hinweise für die Textsuche zu definieren:</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>Die Attribute des Speichermodellschemas (`VectorStore*`) sind für die tatsächliche Verwendung des Elasticsearch Vector Store Connectors am relevantesten, nämlich:</p><p></p><ul><li><p><code>VectorStoreRecordKey</code> Eine Eigenschaft einer Datensatzklasse als Schlüssel zu kennzeichnen, unter dem der Datensatz in einem Vektorspeicher abgelegt wird.</p></li><li><p><code>VectorStoreRecordData</code> Eine Eigenschaft einer Datensatzklasse als 'Daten' kennzeichnen.</p></li><li><p><code>VectorStoreRecordVector</code> Eine Eigenschaft einer Datensatzklasse als Vektor kennzeichnen.</p></li></ul><p>Alle diese Attribute akzeptieren verschiedene optionale Parameter, mit denen das Speichermodell weiter angepasst werden kann. Im Fall von <code>VectorStoreRecordKey </code> ist es beispielsweise möglich, eine andere Distanzfunktion oder einen anderen Indextyp anzugeben.</p><p>Die Textsuchattribute (<code>TextSearch*</code>) werden im letzten Schritt dieses Beispiels wichtig sein. Wir werden später auf sie zurückkommen.</p><p>Im nächsten Schritt initialisieren wir die Semantic Kernel Engine und erhalten Referenzen zu den Kerndiensten. In einer realen Anwendung sollte <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection">Dependency Injection</a> anstelle des direkten Zugriffs auf die Service-Collection verwendet werden. Dasselbe gilt für die fest codierte Konfiguration und die Geheimnisse, die stattdessen mithilfe eines <a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration">Konfigurationsanbieters</a> gelesen werden sollten:</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>Der Dienst <code>vectorStoreCollection</code> kann nun verwendet werden, um die Sammlung zu erstellen und einige <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">Demodatensätze</a> einzulesen:</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>Dies zeigt, wie Semantic Kernel die Verwendung eines Vektorspeichers mit all seiner Komplexität auf wenige einfache Methodenaufrufe reduziert.</p><p>Im Hintergrund wird in Elasticsearch ein neuer Index erstellt und alle notwendigen Eigenschaftszuordnungen werden angelegt. Unser Datensatz wird dann vollständig transparent in das Speichermodell abgebildet und schließlich im Index gespeichert. Nachfolgend sehen Sie, wie die Zuordnungen in Elasticsearch aussehen.</p>{
  "mappings": {
    "properties": {
      "descriptionEmbedding": {
        "dims": 1536,
        "index": true,
        "index_options": {
          "type": "hnsw"
        },
        "similarity": "cosine",
        "type": "dense_vector"
      },
      "hotelName": {
        "type": "keyword"
      },
      "description": {
        "type": "text"
      }
    }
  }
}<p>Die <code>embeddings.GenerateEmbeddingsAsync()</code> -Aufrufe riefen transparent den konfigurierten Azure AI Embeddings Generation-Dienst auf.</p><p>Noch mehr Magie lässt sich im letzten Schritt dieser Demonstration beobachten.</p><p>Mit nur einem einzigen Aufruf von <code>InvokePromptAsync</code> werden alle folgenden Operationen ausgeführt, wenn der Benutzer eine Frage zu den Daten stellt:</p><p>1. Es wird eine Einbettung für die Frage des Benutzers generiert.</p><p>2. Der Vektorspeicher wird nach relevanten Einträgen durchsucht.</p><p>3. Die Ergebnisse der Abfrage werden in eine Eingabeaufforderungsvorlage eingefügt.</p><p>4. Die eigentliche Anfrage in Form der finalen Eingabeaufforderung wird an den KI-Chatvervollständigungsdienst gesendet.</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>Erinnern Sie sich an die <code>TextSearch*</code> -Attribute, die wir zuvor in unserem Datenmodell definiert haben? Mithilfe dieser Attribute können wir in unserer Eingabeaufforderungsvorlage entsprechende Platzhalter verwenden, die automatisch mit den Informationen aus unseren Einträgen im Vektorspeicher befüllt werden.</p><p>Die endgültige Antwort auf unsere Frage „Bitte zeigen Sie mir alle Hotels mit einer Dachterrassenbar“ lautet wie folgt:</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>Die Antwort bezieht sich korrekt auf den folgenden Eintrag in unserer hotels.csv-Datei.</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>Dieses Beispiel zeigt sehr gut, wie die Verwendung des Microsoft Semantic Kernel durch seine durchdachten Abstraktionen eine signifikante Reduzierung der Komplexität ermöglicht und gleichzeitig ein sehr hohes Maß an Flexibilität gewährleistet. Durch die Änderung einer einzigen Codezeile können beispielsweise der Vektorspeicher oder die verwendeten KI-Dienste ersetzt werden, ohne dass ein anderer Teil des Codes umstrukturiert werden muss.</p><p>Gleichzeitig bietet das Framework eine enorme Menge an High-Level-Funktionalität, wie zum Beispiel die Funktion `InvokePrompt` oder das Template- oder Such-Plugin-System.</p><p>Die vollständige Demo-Anwendung finden Sie im Elasticsearch Vector Store Connector-Repository.</p><h2>Was ist sonst noch mit Elasticsearch möglich?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Elasticsearch: Neues semantisches Textmapping: Vereinfachung der semantischen Suche</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Semantisches Reranking in Elasticsearch mit Retrievern</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Erweiterte RAG-Techniken Teil 1: Datenverarbeitung</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Fortgeschrittene RAG-Techniken Teil 2: Abfragen und Testen</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">RAG mit Llama 3 Open-Source und Elastic erstellen</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Eine Anleitung zum Erstellen eines lokalen Agenten mit LangGraph, LLaMA3 und dem Elasticsearch-Vektorspeicher von Grund auf</a></p></li></ul><h2>Elasticsearch &amp; Semantic Kernel: Was kommt als Nächstes?</h2><ul><li><p>Wir haben gezeigt, wie der Elasticsearch-Vektorspeicher beim Erstellen von GenAI-Anwendungen in .NET einfach in den Semantic Kernel eingebunden werden kann. Seien Sie gespannt auf die nächste Python-Integration.</p></li><li><p>Da Semantic Kernel Abstraktionen für fortgeschrittene Suchfunktionen wie <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">die Hybridsuche</a> erstellt, ermöglicht Elasticsearch Connect .NET-Entwicklern die einfache Implementierung dieser Funktionen bei der Verwendung von 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[KI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Vektordatenbank]]></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[Erstellen einer Such-App mit Blazor und Elasticsearch]]></title>
    <description><![CDATA[Lernen Sie, wie Sie mit Blazor und Elasticsearch eine Suchanwendung erstellen und wie Sie den Elasticsearch .NET-Client für die hybride Suche verwenden.]]></description>
    <content:encoded><![CDATA[<p>In diesem Artikel erfahren Sie, wie Sie Ihre C#-Kenntnisse nutzen können, um eine Suchanwendung mit Blazor und Elasticsearch zu erstellen. Wir werden den <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/introduction.html">Elasticsearch .NET-</a> Client verwenden, um <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html">Volltext-</a>, <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">semantische</a> und <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">hybride</a> Suchabfragen durchzuführen.</p><p><strong>HINWEIS:</strong> Wenn Sie mit der älteren Version des Elasticsearch C#-Clients <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/nest.html">NEST</a> vertraut sind, lesen Sie diesen <a href="https://www.elastic.co/search-labs/blog/net-client-evolution">Blogbeitrag</a> über die Einstellung des NEST-Clients und neue Funktionen. <em>NEST war die vorherige Generation des .NET-Clients, der durch den .NET-Client ersetzt wurde.</em><code>Elastic.Clients.Elasticsearch package</code></p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca8d1da68641fac0/6a17f65763173044d4585bfb/18f890286ca122cd97286c09ebf740208b802d0b-650x395.png" alt="Erstellen Sie eine Blazor-App: Esre mit Blazor-Diagramm" /><ul><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-blazor?">Was ist Blazor?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#what-is-esre?">Was ist ESRE?</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#configuring-elser">ELSER konfigurieren</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#indexing-data">Indexierungsdaten</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor#building-the-app-with-blazor-&amp;-elasticsearch">Gebäudeanwendung</a></p></li></ul><h2>Was ist Blazor?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91278ae422883e28/6a17f6592f4a5c3213fa8a95/622741915d016b68bf94f742332d10d736d60052-707x461.png" alt="Blazor-Server" /><p><a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">Blazor</a> ist ein Open-Source-Webframework auf Basis von HTML, CSS und C#, das von Microsoft entwickelt wurde, um Entwicklern die Erstellung von Webanwendungen zu ermöglichen, die auf dem Client oder dem Server ausgeführt werden können. Blazor ermöglicht es Ihnen außerdem, wiederverwendbare Komponenten zu erstellen, um Anwendungen schneller zu entwickeln; es ermöglicht Entwicklern, die HTML-Ansicht und Aktionen in C# innerhalb derselben Datei zu erstellen, was dazu beiträgt, lesbaren und sauberen Code zu erhalten. Mit Blazor Hybrid können Sie außerdem native mobile Apps erstellen, die über .NET-Code auf die Funktionen der nativen Plattform zugreifen.</p><p>Einige der Merkmale, die Blazor zu einem großartigen Framework machen:</p><ul><li><p>Serverseitige und clientseitige Rendering-Optionen</p></li><li><p>Wiederverwendbare UI-Komponenten</p></li><li><p>Echtzeit-Updates mit SignalR</p></li><li><p>Integrierte Zustandsverwaltung</p></li><li><p>Eingebautes Routingsystem</p></li><li><p>Strenge Typisierung und Kompilierzeitprüfungen</p></li></ul><h3>Warum Blazer?</h3><p>Blazor bietet gegenüber anderen Frameworks und Bibliotheken mehrere Vorteile: Es ermöglicht Entwicklern, C# sowohl für Client- als auch für Servercode zu verwenden, bietet eine starke Typisierung und Kompilierzeitprüfung, was die Zuverlässigkeit erhöht. Es integriert sich nahtlos in das .NET-Ökosystem, ermöglicht die Wiederverwendung von .NET-Bibliotheken und -Tools und bietet robuste Unterstützung beim Debuggen.</p><h2>Was ist ESRE?</h2><p><a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">Elasticsearch Relevance Engine ™ (ESRE)</a> ist eine Sammlung von <a href="https://www.elastic.co/guide/en/esre/current/learn.html">Tools zum Erstellen von Suchanwendungen</a> mithilfe von maschinellem Lernen und künstlicher Intelligenz auf Basis der leistungsstarken Elasticsearch-Suchmaschine.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf313ba986de01b93/6a17d851505ac306fcad8975/4c0f2645ed1c27fe3ef61a1a9126adadfd8d5368-721x421.png" alt="esre" /><p>Um mehr über ESRE zu erfahren, können Sie unseren aufschlussreichen Blogbeitrag <a href="https://www.elastic.co/search-labs/blog/introducing-elasticsearch-relevance-engine-esre">hier</a>lesen.</p><h2>ELSER konfigurieren</h2><p>Um <a href="https://www.elastic.co/elasticsearch/elasticsearch-relevance-engine">die ESRE-</a> Funktionen von Elastic optimal zu nutzen, werden wir <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html">ELSER</a> als unseren Modellanbieter verwenden.</p><p><em>Hinweis: Für die Nutzung der ELSER-Modelle von Elasticsearch benötigen Sie eine Platinum- oder Enterprise-Lizenz sowie einen dedizierten Machine-Learning-Knoten (ML-Knoten) mit mindestens 4 GB Speicherplatz. Weitere Informationen finden Sie</em> <a href="https://www.elastic.co/guide/en/machine-learning/8.15/ml-nlp-elser.html#elser-req"><em>hier.</em></a></p><p>Beginnen Sie mit der Erstellung des Inferenzendpunkts:</p>PUT _inference/sparse_embedding/my-elser-model
{
  "service": "elser",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1
  }
}<p>Wenn Sie ELSER zum ersten Mal verwenden, kann beim Laden des Modells im Hintergrund ein 502 Bad Gateway-Fehler auftreten. Den Status des Modells können Sie in <code>Machine Learning &gt; Trained Models</code> in Kibana überprüfen. Sobald die Bereitstellung abgeschlossen ist, können Sie mit dem nächsten Schritt fortfahren.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee295a3516338302/6a17f65b414c640deb94531d/a7ff94b72d337cde892165d744b9f42fba702a87-1440x649.png" alt="Überprüfung trainierter Modelle" /><h2>Indexierungsdaten</h2><p>Sie können den Datensatz <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor/books.zip">hier</a> herunterladen und die Daten anschließend mit Kibana importieren. Gehen Sie dazu auf die Startseite und klicken Sie auf „Daten hochladen“. Laden Sie anschließend die Datei hoch und klicken Sie auf <code>Import</code>. Wechseln Sie abschließend zum Reiter <code>Advanced</code> und fügen Sie die folgenden Zuordnungen ein:</p>{
   "properties":{
      "authors":{
         "type":"keyword"
      },
      "categories":{
         "type":"keyword"
      },
      "longDescription":{
         "type":"semantic_text",
         "inference_id":"my-elser-model",
         "model_settings":{
            "task_type":"sparse_embedding"
         }
      },
      "pageCount":{
         "type":"integer"
      },
      "publishedDate":{
         "type":"date"
      },
      "shortDescription":{
         "type":"text"
      },
      "status":{
         "type":"keyword"
      },
      "thumbnailUrl":{
         "type":"keyword"
      },
      "title":{
         "type":"text"
      }
   }
}<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f07300647edd2ca/6a17f65dfaa913172a93ca0c/1aea0b9c51e275f89339f5d463fcaef799fc3943-1235x1083.png" alt="Importdaten" /><p>Wir werden einen Index erstellen, der semantische und Volltextabfragen ausführen kann. Der Feldtyp <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">semantic_text</a> kümmert sich um die Datensegmentierung und -einbettung. <em>Beachten Sie, dass wir als indizieren</em> <em><code>longDescription</code></em>  <em><code>semantic_text</code></em><em>. Sie können copy_to verwenden, wenn Sie ein Feld sowohl als als</em> <em><code>semantic_text</code></em> <em>auch als text` indizieren</em> möchten .</p><h2>Entwicklung der App mit Blazor &amp; Elasticsearch</h2><h3>API-Schlüssel</h3><p>Als Erstes müssen wir einen API-Schlüssel erstellen, um unsere Anfragen an Elasticsearch zu authentifizieren. Der API-Schlüssel sollte schreibgeschützt sein und nur Abfragen des Index <code>books-blazor</code> erlauben.</p>POST /_security/api_key
{
  "name": "books-blazor-key",
  "role_descriptors": {
    "books-blazor-reader": {
      "indices": [
        {
          "names": ["books-blazor"],
          "privileges": ["read"]
        }
      ]
    }
  }
}<p>Sie werden etwa Folgendes sehen:
</p>{
  "id": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "name": "books-blazor-key",
  "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "encoded": "XXXXXXXXXXXXXXXXXXXXXXXX=="
}<p>Speichern Sie den Wert des Antwortfelds <code>encoded</code> , da er später benötigt wird. Wenn Sie <a href="https://www.elastic.co/cloud/">Elastic Cloud</a> nutzen, benötigen Sie außerdem Ihre Cloud-ID. (Sie finden sie <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id">hier</a>).</p><h4>Das Blazor-Projekt wird erstellt</h4><p>Beginnen Sie mit der Installation von Blazor und der Erstellung eines Beispielprojekts gemäß den <a href="https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/install">offiziellen Anweisungen</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb5c20b896fe1e98e/6a17f65f6317301566585bff/f5720867d960c91fd1b3c4ae9be174e062e6b2fc-975x830.png" alt="Blazor-Tutorial – Erstellen des Blazor-Projekts" /><p>Sobald das Projekt erstellt ist, sollte die Ordnerstruktur und die Dateistruktur wie folgt aussehen:</p>BlazorApp/
|-- BlazorApp.csproj
|-- BlazorApp.sln
|-- Program.cs
|-- appsettings.Development.json
|-- appsettings.json
|-- Properties/
|   `-- launchSettings.json
|-- Components/
|   |-- App.razor
|   |-- Routes.razor
|   |-- _Imports.razor
|   |-- Layout/
|   |   |-- MainLayout.razor
|   |   |-- MainLayout.razor.css
|   |   |-- NavMenu.razor
|   |   `-- NavMenu.razor.css
|   `-- Pages/
|       |-- Counter.razor
|       |-- Error.razor
|       |-- Home.razor
|       `-- Weather.razor
|-- wwwroot/
|-- bin/
`-- obj/ <p>Die Template-Anwendung enthält <a href="https://blog.getbootstrap.com/2021/08/04/bootstrap-5-1-0/">Bootstrap v5.1.0.</a> zum Styling.</p><p>Schließen Sie die Projekteinrichtung ab, indem Sie <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.0/installation.html">den Elasticsearch .NET</a> -Client installieren:</p>dotnet add package Elastic.Clients.Elasticsearch<p>Sobald Sie diesen Schritt abgeschlossen haben, sollte Ihre Seite wie folgt aussehen:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b90e394ded7ff56/6a17f660faa913d49f93ca10/ef9d2186c2cf49e78a307c4aa69c51632e84578d-940x529.png" alt="Blazor Hallo Welt" /><h3>Ordnerstruktur</h3><p>Nun werden wir unsere Ordner wie folgt organisieren:</p>BlazorApp/
|-- Components/
|   |-- Pages/
|   |   |-- Search.razor
|   |   `-- Search.razor.css
|   `-- Elasticsearch/
|       |-- SearchBar.razor
|       |-- Results.razor
|       `-- Facet.razor
|-- Models/
|   |-- Book.cs
|   `-- Response.cs
`-- Services/
    `-- ElasticsearchService.cs<p>Dateitypen erklärt:</p><ul><li><p>Components/Pages/Search.razor: Hauptseite mit Suchleiste, Suchergebnissen und Filtern.</p></li><li><p>Components/Pages/Search.razor.css: Seitenstile.</p></li><li><p>Components/Elasticsearch/SearchBar.razor: Suchleistenkomponente.</p></li><li><p>Components/Elasticsearch/Results.razor: Ergebniskomponente.</p></li><li><p>Components/Elasticsearch/Facet.razor: Filterkomponente.</p></li><li><p>Components/Svg/GlassIcon.razor: Suchsymbol.</p></li><li><p>Components/_Imports.razor: Dadurch werden alle Komponenten importiert.</p></li><li><p>Models/Book.cs: Hier wird das Schema der Buchfelder gespeichert.</p></li><li><p>Models/Response.cs: Hier wird das Antwortschema gespeichert, einschließlich der Suchergebnisse, Facetten und der Gesamtzahl der Treffer.</p></li><li><p>Services/ElasticsearchService.cs: Elasticsearch-Dienst. Es übernimmt die Verbindung und die Abfragen an Elasticsearch.</p></li></ul><h4>Erstkonfiguration</h4><p>Fangen wir mit Aufräumarbeiten an.</p><p>Löschen Sie die Dateien:</p><ul><li><p>Komponenten/Seiten/Counter.razor</p></li><li><p>Komponenten/Seiten/Wetter.razor</p></li><li><p>Komponenten/Seiten/Startseite.razor</p></li><li><p>Komponenten/Layout/NavMenu.razor</p></li><li><p>Components/Layout/NavMenu.razor.css</p></li></ul><p>Überprüfen Sie die Datei <code>/Components/_Imports.razor</code> . Sie sollten folgende Importe haben:</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components<h4>Integration von Elastic in das Projekt</h4><p>Importieren wir nun die Elasticsearch-Komponenten:</p>@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorApp
@using BlazorApp.Components
@using BlazorApp.Components.Elasticsearch @* &lt;--- Add this line *@<p>Wir werden die Standard-Seitenleiste entfernen, um mehr Platz für unsere Anwendung zu schaffen, indem wir sie aus der Datei <code>/Components/Layout/MainLayout.razor</code> löschen:</p>@inherits LayoutComponentBase

&lt;div class="page"&gt;
    &lt;main&gt;
        &lt;article class="content"&gt;
            @Body
        &lt;/article&gt;
    &lt;/main&gt;
&lt;/div&gt;

&lt;div id="blazor-error-ui"&gt;
    An unhandled error has occurred.
    &lt;a href="" class="reload"&gt;Reload&lt;/a&gt;
    &lt;a class="dismiss"&gt;🗙&lt;/a&gt;
&lt;/div&gt;<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt441012c5aeac85f8/6a17f661ec0f8949f15a67ac/55bfff607f9167a532f83ad074b479364a346c6e-816x472.png" alt="Navigationsleiste aus dem Layout entfernt" /><p>Geben wir nun die Elasticsearch-Zugangsdaten für die <a href="https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-8.0&amp;tabs=linux#secret-manager">Benutzergeheimnisse</a> ein:</p>dotnet user-secrets init
dotnet user-secrets set ElasticsearchCloudId "your Cloud ID"
dotnet user-secrets set ElasticsearchApiKey "your API Key"<p>Bei diesem Ansatz speichert .NET 8 sensible Daten an einem separaten Ort außerhalb des Projektordners und ermöglicht den Zugriff über die <code>IConfiguration</code> -Schnittstelle. Diese Variablen stehen jedem .NET-Projekt zur Verfügung, das dieselben Benutzergeheimnisse verwendet.</p><p>Als Nächstes modifizieren wir die Datei <code>Program.cs</code> , um die Geheimnisse zu lesen und den Elasticsearch-Client einzubinden:</p><p>Zuerst müssen die benötigten Bibliotheken importiert werden:</p>using BlazorApp.Services;
using Elastic.Clients.Elasticsearch;
using Elastic.Transport;<ul><li><p>BlazorApp.Services: enthält den Elasticsearch-Dienst.</p></li><li><p>Elastic.Clients.Elasticsearch: importiert die Elasticsearch-Client-Bibliothek für .NET 8.</p></li><li><p>Elastic.Transport: importiert die Elasticsearch-Transportbibliothek, die es uns ermöglicht, die ApiKey-Klasse zur Authentifizierung unserer Anfragen zu verwenden.</p></li></ul><p>Zweitens fügen Sie den folgenden Code vor der Zeile <code>var app = builder.Build()</code> ein:</p>// Initialize the Elasticsearch client.
builder.Services.AddScoped(sp =&gt;
{
    // Getting access to the configuration service to read the Elasticsearch credentials.
    var configuration = sp.GetRequiredService&lt;IConfiguration&gt;();
    var cloudId = configuration["ElasticsearchCloudId"];
    var apiKey = configuration["ElasticsearchApiKey"];

    if (string.IsNullOrEmpty(cloudId) || string.IsNullOrEmpty(apiKey))
    {
        throw new InvalidOperationException(
            "Elasticsearch credentials are missing in configuration."
        );
    }

    var settings = new ElasticsearchClientSettings(cloudId, new ApiKey(apiKey)).EnableDebugMode();
    return new ElasticsearchClient(settings);
});<p>Dieser Code liest die Elasticsearch-Zugangsdaten aus den Benutzergeheimnissen und erstellt eine Elasticsearch-Clientinstanz.</p><p>Nach der Initialisierung des Elasticsearch-Clients fügen Sie die folgende Zeile hinzu, um den Elasticsearch-Dienst zu registrieren:</p>builder.Services.AddScoped&lt;ElasticsearchService&gt;();<p>Der nächste Schritt besteht darin, die Suchlogik in der Datei <code>/Services/ElasticsearchService.cs</code> zu implementieren:</p><p>Zuerst müssen die benötigten Bibliotheken und Modelle importiert werden:</p>using BlazorApp.Models;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;<p>Zweitens fügen Sie die Klasse <code>ElasticsearchService</code>, den Konstruktor und die Variablen hinzu:</p>namespace BlazorApp.Services
{
    public class ElasticsearchService
    {
        private readonly ElasticsearchClient _client;

        // The logger is used to log information, warnings and errors about the Elasticsearch service and requests.
        private readonly ILogger&lt;ElasticsearchService&gt; _logger;

        public ElasticsearchService(
            ElasticsearchClient client,
            ILogger&lt;ElasticsearchService&gt; logger
        )
        {
            _client = client ?? throw new ArgumentNullException(nameof(client));
            _logger = logger;
        }
    }
}<h4>Suche konfigurieren</h4><p>Nun erstellen wir unsere Suchlogik:</p>private static Action&lt;RetrieverDescriptor&lt;BookDoc&gt;&gt; BuildHybridQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return retrievers =&gt;
        retrievers.Rrf(rrf =&gt;
            rrf.RankWindowSize(50)
                .RankConstant(20)
                .Retrievers(
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.MultiMatch(mm =&gt;
                                                mm.Query(searchTerm)
                                                    .Fields(
                                                        new[]
                                                        {
                                                            "title",
                                                            "shortDescription",
                                                        }
                                                    )
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        ),
                    retrievers =&gt;
                        retrievers.Standard(std =&gt;
                            std.Query(q =&gt;
                                q.Bool(b =&gt;
                                    b.Must(m =&gt;
                                            m.Semantic(sem =&gt;
                                                sem.Field("longDescription")
                                                    .Query(searchTerm)
                                            )
                                        )
                                        .Filter(filters.ToArray())
                                )
                            )
                        )
                )
        );
}

public static List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt; BuildFilters(
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = new List&lt;Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt;&gt;();

    if (selectedFacets != null)
    {
        foreach (var facet in selectedFacets)
        {
            foreach (var value in facet.Value)
            {
                var field = facet.Key.ToLower();
                if (!string.IsNullOrEmpty(field))
                {
                    filters.Add(m =&gt; m.Term(t =&gt; t.Field(new Field(field)).Value(value)));
                }
            }
        }
    }

    return filters;
}<ul><li><p><code>BuildFilters</code> wird die Filter für die Suchanfrage anhand der vom Benutzer ausgewählten Facetten erstellen.</p></li><li><p><code>BuildHybridQuery</code> wird eine <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">hybride Suchanfrage</a> erstellen, die Volltext- und semantische Suche kombiniert.</p></li></ul><p>Als Nächstes fügen Sie die Suchmethode hinzu:</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");

        // Retrieve the hybrid query with filters applied.
        var retrieverQuery = BuildHybridQuery(searchTerm, selectedFacets);

        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Retriever(retrieverQuery)
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}

public static Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; FormatFacets(
    Elastic.Clients.Elasticsearch.Aggregations.AggregateDictionary aggregations
)
{
    var facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

    foreach (var aggregation in aggregations)
    {
        if (
            aggregation.Value
            is Elastic.Clients.Elasticsearch.Aggregations.StringTermsAggregate termsAggregate
        )
        {
            var facetName = aggregation.Key;
            var facetDictionary = ConvertFacetDictionary(
                termsAggregate.Buckets.ToDictionary(b =&gt; b.Key, b =&gt; b.DocCount)
            );
            facets[facetName] = facetDictionary;
        }
    }

    return facets;
}

private static Dictionary&lt;string, long&gt; ConvertFacetDictionary(
    Dictionary&lt;Elastic.Clients.Elasticsearch.FieldValue, long&gt; original
)
{
    var result = new Dictionary&lt;string, long&gt;();
    foreach (var kvp in original)
    {
        result[kvp.Key.ToString()] = kvp.Value;
    }
    return result;
}<ul><li><p><code>SearchBooksAsync</code>: führt die Suche mit Hilfe der Hybridabfrage durch und gibt die Ergebnisse einschließlich der Aggregationen zum Aufbau der Facetten zurück.</p></li><li><p><code>FormatFacets</code>: formatiert die Aggregationsantwort in ein Wörterbuch.</p></li><li><p><code>ConvertFacetDictionary</code>: wird das Facettenwörterbuch in ein besser lesbares Format umwandeln.</p></li></ul><p>Im nächsten Schritt werden die Modelle erstellt, die die Daten repräsentieren, die in der <code>hits</code> der Elasticsearch-Abfrage zurückgegeben werden und als Ergebnisse auf unserer Suchseite angezeigt werden.</p><p>Wir beginnen damit, die Datei <code>/Models/Book.cs</code> zu erstellen und Folgendes hinzuzufügen:</p>namespace BlazorApp.Models
{
    public class BookDoc
    {
        public string? Title { get; set; }
        public int? PageCount { get; set; }
        public string? PublishedDate { get; set; }
        public string? ThumbnailUrl { get; set; }
        public string? ShortDescription { get; set; }
        public LongDescription? LongDescription { get; set; }
        public string? Status { get; set; }
        public List&lt;string&gt;? Authors { get; set; }
        public List&lt;string&gt;? Categories { get; set; }
    }

    public class LongDescription
    {
        public string? Text { get; set; }
    }
}<p>Anschließend wird die Elastic-Antwort in der Datei <code>/Models/Response.cs</code> eingerichtet und Folgendes hinzugefügt:</p>namespace BlazorApp.Models
{
    public class ElasticResponse
    {
        public ElasticResponse()
        {
            Documents = new List&lt;BookDoc&gt;();
            Facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
        }

        public long TotalHits { get; set; }
        public List&lt;BookDoc&gt; Documents { get; set; }
        public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; Facets { get; set; }
    }
}<h4>Konfigurieren einer grundlegenden Benutzeroberfläche</h4><p>Als Nächstes fügen Sie die Komponente SearchBar hinzu. Fügen Sie in der Datei <code>/Components/Elasticsearch/SearchBar.razor</code> Folgendes hinzu:</p>@using System.Threading.Tasks

&lt;form @onsubmit="SubmitSearch"&gt;
  &lt;div class="input-group mb-3"&gt;
    &lt;input type="text" @bind-value="searchTerm" class="form-control" placeholder="Enter search term..." /&gt;
    &lt;button type="submit" class="btn btn-primary input-btn"&gt;
      &lt;span class="input-group-svg"&gt;
        Search
      &lt;/span&gt;
    &lt;/button&gt;
  &lt;/div&gt;
&lt;/form&gt;

@code {
  [Parameter]
  public EventCallback&lt;string&gt; OnSearch { get; set; }

  private string searchTerm = "";

  private async Task SubmitSearch()
  {
    await OnSearch.InvokeAsync(searchTerm);
  }
}<p>Diese Komponente enthält eine Suchleiste und eine Schaltfläche zur Durchführung der Suche.</p><p>Blazor bietet große Flexibilität, da es die dynamische Generierung von HTML mithilfe von C#-Code innerhalb derselben Datei ermöglicht.</p><p>Anschließend erstellen wir in der Datei <code>/Components/Elasticsearch/Results.razor</code> die Ergebniskomponente, die die Suchergebnisse anzeigt:</p>@using BlazorApp.Models

@if (SearchResults != null &amp;&amp; SearchResults.Any())
{
  &lt;div class="row"&gt;
  @foreach (var result in SearchResults)
    {
      &lt;div class="col-12 mb-3"&gt;
        &lt;div class="card"&gt;
          &lt;div class="row g-0"&gt;
            &lt;div class="col-md-3 image-container"&gt;
              @if (!string.IsNullOrEmpty(result?.ThumbnailUrl))
              {
                &lt;img src="@result?.ThumbnailUrl" class="img-fluid rounded-start" alt="Thumbnail"&gt;
              }
              else
              {
                &lt;div class="placeholder"&gt;
                  @result?.Title
                &lt;/div&gt;
              }
            &lt;/div&gt;

            &lt;div class="col-md-9"&gt; &lt;!-- Adjusted to use the remaining 75% --&gt;
              &lt;div class="card-body"&gt;
                &lt;h4 class="card-title"&gt;
                  @result?.Title
                &lt;/h4&gt;

                &lt;div class="details-container"&gt;
                  &lt;div class=""&gt;

                    @if (result?.Authors?.Any() == true)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Authors: &lt;small class="text-muted"&gt;@string.Join(", ", result.Authors)&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Categories?.Any() == true)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Categories: &lt;small class="text-muted"&gt;@string.Join(", ", result.Categories)&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                  &lt;div class="numPages-status"&gt;
                    @if (result?.PageCount != null)
                    {
                      &lt;p class="card-text p-first"&gt;
                        Pages: &lt;small class="text-muted"&gt;@result.PageCount&lt;/small&gt;
                      &lt;/p&gt;
                    }

                    @if (result?.Status != null)
                    {
                      &lt;p class="card-text p-second"&gt;
                        Status: &lt;small class="text-muted"&gt;@result.Status&lt;/small&gt;
                      &lt;/p&gt;
                    }
                  &lt;/div&gt;
                &lt;/div&gt;

                &lt;div class="long-text-container"&gt;
                  &lt;p class="card-text"&gt;&lt;small class="text-muted"&gt;@result?.LongDescription?.Text&lt;/small&gt;&lt;/p&gt;
                &lt;/div&gt;
                @if (!string.IsNullOrEmpty(result?.PublishedDate))
                {
                  &lt;div class="date-container"&gt;
                    &lt;p class="card-text"&gt;
                      Published Date: &lt;small class="text-muted small-date"&gt;@FormatDate(result.PublishedDate)&lt;/small&gt;
                    &lt;/p&gt;
                  &lt;/div&gt;
                }
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    }
  &lt;/div&gt;
}
else if (SearchResults != null)
{
  &lt;p&gt;No results found.&lt;/p&gt;
}

@code {
  [Parameter]
  public List&lt;BookDoc&gt; SearchResults { get; set; } = new List&lt;BookDoc&gt;();

  private string FormatDate(string? date)
  {
    if (DateTime.TryParse(date, out DateTime parsedDate))
    {
      return parsedDate.ToString("MMMM dd, yyyy");
    }
    return "";
  }
}<p>Abschließend müssen wir noch Facetten erstellen, um die Suchergebnisse zu filtern.</p><p><em>Hinweis: Facetten sind Filter, mit denen Nutzer Suchergebnisse anhand bestimmter Attribute oder Kategorien wie Produkttyp, Preisspanne oder Marke eingrenzen können. Diese Filter werden üblicherweise als anklickbare Optionen, oft in Form von Kontrollkästchen, dargestellt, um Nutzern die Suche zu erleichtern und relevante Ergebnisse schneller zu finden. In Elasticsearch werden Facetten mithilfe von</em> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html"><em>Aggregationen</em></a>erstellt<em>.</em></p><p>Wir richten Facetten ein, indem wir den folgenden Code in die Datei <code>/Components/Elasticsearch/Facet.razor</code> einfügen:</p>@if (Facets != null)
{
  &lt;div class="facets-container"&gt;
  @foreach (var facet in Facets)
    {
      &lt;h3&gt;@facet.Key&lt;/h3&gt;
      @foreach (var option in facet.Value)
      {
        &lt;div&gt;
          &lt;input type="checkbox" checked="@IsFacetSelected(facet.Key, option.Key)"
            @onclick="() =&gt; ToggleFacet(facet.Key, option.Key)" /&gt;
          @option.Key (@option.Value)
        &lt;/div&gt;
      }
    }
  &lt;/div&gt;
}


@code {
  [Parameter]
  public Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;? Facets { get; set; }

  [Parameter]
  public EventCallback&lt;Dictionary&lt;string, List&lt;string&gt;&gt;&gt; OnFacetChanged { get; set; }

  private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new();

  private void ToggleFacet(string facetName, string facetValue)
  {
    if (!selectedFacets.TryGetValue(facetName, out var facetValues))
    {
      facetValues = selectedFacets[facetName] = new List&lt;string&gt;();
    }

    if (!facetValues.Remove(facetValue))
    {
      facetValues.Add(facetValue);
    }

    OnFacetChanged.InvokeAsync(selectedFacets);
  }

  private bool IsFacetSelected(string facetName, string facetValue)
  {
    return selectedFacets.ContainsKey(facetName) &amp;&amp; selectedFacets[facetName].Contains(facetValue);
  }
}<p>Diese Komponente liest Daten aus einer <code>terms</code> -Aggregation der Felder <code>author</code>, <code>categories</code> und <code>status</code> und erzeugt anschließend eine Liste von Filtern, die an Elasticsearch zurückgesendet werden.</p><p>Nun fügen wir alles zusammen.</p><p>In der Datei <code>/Components/Pages/Search.razor</code> :</p>@page "/"
@rendermode InteractiveServer
@using BlazorApp.Models
@using BlazorApp.Services
@inject ElasticsearchService ElasticsearchService
@inject ILogger&lt;Search&gt; Logger

&lt;PageTitle&gt;Search&lt;/PageTitle&gt;

&lt;div class="top-row px-4 "&gt;

    &lt;div class="searchbar-container"&gt;
        &lt;h4&gt;Semantic Search with Elasticsearch and Blazor&lt;/h4&gt;

        &lt;SearchBar OnSearch="PerformSearch" /&gt;
    &lt;/div&gt;

    &lt;a href="https://www.elastic.co/search-labs/esre-with-blazor" target="_blank"&gt;About&lt;/a&gt;
&lt;/div&gt;

&lt;div class="px-4"&gt;

    &lt;div class="search-details-container"&gt;
        &lt;p role="status"&gt;Current search term: @currentSearchTerm&lt;/p&gt;
        &lt;p role="status"&gt;Total results: @totalResults&lt;/p&gt;
    &lt;/div&gt;

    &lt;div class="results-facet-container"&gt;
        &lt;div class="facets-container"&gt;
            &lt;Facet Facets="facets" OnFacetChanged="OnFacetChanged" /&gt;
        &lt;/div&gt;
        &lt;div class="results-container"&gt;
            &lt;Results SearchResults="searchResults" /&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

@code {
    private string currentSearchTerm = "";
    private long totalResults = 0;
    private List&lt;BookDoc&gt; searchResults = new List&lt;BookDoc&gt;();
    private Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt; facets = new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();
    private Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets = new Dictionary&lt;string, List&lt;string&gt;&gt;();

    protected override async Task OnInitializedAsync()
    {
        await PerformSearch();
    }

    private async Task PerformSearch(string searchTerm = "")
    {
        try
        {
            currentSearchTerm = searchTerm;

            var response = await ElasticsearchService.SearchBooksAsync(currentSearchTerm, selectedFacets);
            if (response != null)
            {
                searchResults = response.Documents;
                facets = response.Facets;
                totalResults = response.TotalHits;
            }
            else
            {
                Logger.LogWarning("Search response is null.");
            }

            StateHasChanged();
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error performing search.");
        }
    }

    private async Task OnFacetChanged(Dictionary&lt;string, List&lt;string&gt;&gt; newSelectedFacets)
    {
        selectedFacets = newSelectedFacets;
        await PerformSearch(currentSearchTerm);
    }
}<p>Unsere Seite funktioniert!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc750e55fff43de4b/6a17f6636864a4ab08b68931/5f0f25cb029a34df7f577a9f307278226ed07c3f-816x473.png" alt="Blazor-Seitenbeispiel" /><p>Wie Sie sehen können, ist die Seite funktional, aber es fehlen ihr die Stile. Fügen wir etwas CSS hinzu, damit es übersichtlicher und responsiver aussieht.</p><p>Fangen wir an, die Layoutstile zu ersetzen. In der Datei <code>Components/Layout/MainLayout.razor.css</code> :</p>.page {
  position: relative;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
}

#blazor-error-ui {
  background: lightyellow;
  bottom: 0;
  box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
  display: none;
  left: 0;
  padding: 0.6rem 1.25rem 0.7rem 1.25rem;
  position: fixed;
  width: 100%;
  z-index: 1000;
}

#blazor-error-ui .dismiss {
  cursor: pointer;
  position: absolute;
  right: 0.75rem;
  top: 0.5rem;
}<p>Fügen Sie die Stile für die Suchseite in der Datei <code>Components/Pages/Search.razor.css</code> hinzu:</p>.input-group .input-group-svg {
  background: transparent;
  border: transparent;
  pointer-events: none;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.search-details-container {
  display: flex;
  justify-content: space-between;
  margin-top: 1rem;
}

.searchbar-container {
  padding-top: 2rem;
  display: flex;
  flex-direction: column; 
  flex-grow: 1;
  height: 100%;
  max-width: 100%; 
}

.searchbar-container h4 {
  margin: 0;
}

.top-row {
  margin-top: -1.1rem;
  position: relative; 
  background-color: hsl(216, 29%, 67%);
  border-bottom: 1px solid #d6d5d5;
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 1rem;
}

.top-row a {
  margin-left: auto;
  margin-top: -4rem; 
  color: #000000;
  text-decoration: none;
}

.top-row a:hover {
  text-decoration: underline;
}

@media (max-width: 640.98px) {
  .top-row {
    justify-content: space-between;
  }

  .top-row ::deep a,
  .top-row ::deep .btn-link {
    margin-left: 0;
  }
}

@media (min-width: 641px) {
  .top-row.auth ::deep a:first-child {
    flex: 1;
    text-align: right;
    width: 0;
  }

  .top-row,
  article {
    padding-left: 2rem !important;
    padding-right: 1.5rem !important;
  }
}<p>Unsere Seite sieht jetzt besser aus:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ecc49f404f18591/6a17f6654b055d959b432384/3dadd7ade2dd9070300a4e0514a4da2ae9cc9fb9-817x473.png" alt="Blazor-Seite nach dem Hinzufügen von Stilen für die Suchseite" /><p>Geben wir dem Ganzen noch den letzten Schliff:</p><p>Erstellen Sie die folgenden Dateien:</p><ul><li><p>Komponenten/Elasticsearch/Facet.razor.css</p></li><li><p>Components/Elasticsearch/Results.razor.css</p></li></ul><p>Und fügen Sie die Stile für <code>Facet.razor.css</code> hinzu:</p>.facets-container {
  font-size: 15px;
  margin-right: 4rem;
  overflow-x: auto;
  white-space: nowrap;
  max-width: 300px;
}

.results-facet-container {
  display: flex;
  margin-top: 1rem;
  overflow-x: auto;
}

.results-facet-container &gt; * {
  flex-shrink: 0;
}<p>Für <code>Results.razor.css</code>:</p>.image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 1rem;
  box-sizing: border-box;
}

.image-container img {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

.placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
  font-size: 0.9rem;
  color: #888;
  text-align: center;
  padding: 1rem;
  border-radius: 0.5rem;
}

.card-body {
  padding: 1rem;
}

.details-container {
  display: flex;
  justify-content: space-between;
  padding: 1.5rem 0;
}

.date-container {
  margin-top: 1rem;
  display: flex;
  justify-content: flex-end;
}

.date-container .small-date {
  font-weight: bold;
}<p>Endergebnis:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt78bbd8e6f9e23988/6a17f6664b055d2ed8432388/3e68ec4a38775fdfbeaa1a990e6a1e11dfb081d8-816x472.png" alt="Endergebnis der Erstellung der Blazor-App-Seite" /><p>Um die Anwendung auszuführen, können Sie folgenden Befehl verwenden:</p><p><code>dotnet watch</code></p><p>Du hast es geschafft! Sie können nun mithilfe der Suchleiste in Ihrem Elasticsearch-Index nach Büchern suchen und die Ergebnisse nach Autor, Kategorie und Status filtern.</p><h3>Durchführung der Volltext- und semantischen Suche</h3><p>Standardmäßig führt unsere App eine <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">hybride Suche</a> durch, die sowohl <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html">Volltext-</a> als auch <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">semantische Suche</a> nutzt. Sie können die Suchlogik ändern, indem Sie zwei separate Methoden erstellen, eine für die Volltextsuche und eine weitere für die semantische Suche, und dann eine Methode auswählen, um die Abfrage basierend auf der Eingabe des Benutzers zu erstellen.</p><p>Fügen Sie der Klasse <code>ElasticsearchService</code> in der Datei <code>/Services/ElasticsearchService.cs</code> die folgenden Methoden hinzu:</p>private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildSemanticQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    return query =&gt;
        query.Bool(b =&gt;
            b.Must(m =&gt; m.Semantic(sem =&gt; sem.Field("longDescription").Query(searchTerm)))
                .Filter(filters.ToArray())
        );
}

private static Action&lt;QueryDescriptor&lt;BookDoc&gt;&gt; BuildMultiMatchQuery(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    var filters = BuildFilters(selectedFacets);

    if (string.IsNullOrEmpty(searchTerm))
    {
        return query =&gt; query.Bool(b =&gt; b.Filter(filters.ToArray()));
    }

    return query =&gt;
        query.Bool(b =&gt;
            b.Should(m =&gt;
                    m.MultiMatch(mm =&gt;
                        mm.Query(searchTerm).Fields(new[] { "title", "shortDescription" })
                    )
                )
                .Filter(filters.ToArray())
        );
}<p>Beide Methoden funktionieren ähnlich wie die <code>BuildHybridQuery</code> -Methode, führen aber nur eine Volltext- oder semantische Suche durch.</p><p>Sie können die <code>SearchBooksAsync</code> -Methode so anpassen, dass sie die ausgewählte Suchmethode verwendet:</p>public async Task&lt;ElasticResponse&gt; SearchBooksAsync(
    string searchTerm,
    Dictionary&lt;string, List&lt;string&gt;&gt; selectedFacets
)
{
    try
    {
        _logger.LogInformation($"Performing search for: {searchTerm}");
        
        // Modify the query builder to use the selected search method.
        var multiMatchQuery = BuildMultiMatchQuery(searchTerm, selectedFacets); // For full text search
        var semanticQuery = BuildSemanticQuery(searchTerm, selectedFacets); // For semantic search

        // In this case we will not use retrievers, but you can add them if you want to use them.
        var response = await _client.SearchAsync&lt;BookDoc&gt;(s =&gt;
            s.Index("elastic-blazor-books")
                .Query(multiMatchQuery) // Change this line to use different search methods, for example: .Query(semanticQuery) for semantic search
                .Aggregations(aggs =&gt;
                    aggs.Add("Authors", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Authors)))
                        .Add(
                            "Categories",
                            agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Categories))
                        )
                        .Add("Status", agg =&gt; agg.Terms(t =&gt; t.Field(p =&gt; p.Status)))
                )
        );

        if (response.IsValidResponse)
        {
            _logger.LogInformation($"Found {response.Documents.Count} documents");

            var hits = response.Total;
            var facets =
                response.Aggregations != null
                    ? FormatFacets(response.Aggregations)
                    : new Dictionary&lt;string, Dictionary&lt;string, long&gt;&gt;();

            var elasticResponse = new ElasticResponse
            {
                TotalHits = hits,
                Documents = response.Documents.ToList(),
                Facets = facets,
            };

            return elasticResponse;
        }
        else
        {
            _logger.LogWarning($"Invalid response: {response.DebugInformation}");
            return new ElasticResponse();
        }
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error performing search");
        return new ElasticResponse();
    }
}<p>Die vollständige Bewerbung finden Sie <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/esre-with-blazor">hier.</a></p><h2>Fazit</h2><p>Blazor ist ein effektives Framework, mit dem Sie Webanwendungen in C# erstellen können. Elasticsearch ist eine leistungsstarke Suchmaschine, mit der Sie Suchanwendungen erstellen können. Durch die Kombination beider können Sie auf einfache Weise robuste Suchanwendungen erstellen und die Leistungsfähigkeit von ESRE nutzen, um in kurzer Zeit ein <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">semantisches Sucherlebnis</a> zu schaffen.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/search-app-with-esre-blazor</guid>
    <category><![CDATA[Vektordatenbank]]></category>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7424ac5f0b223b4/6a17f668414c641971945323/7ba0d6bec908bfcae966b7f38626fabd682c6f3d-1200x628.png" length="0" type="image/png"/>
    <pubDate>Wed, 09 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Die Entwicklung des Elasticsearch .NET-Clients: Von NEST zu Elastic.Clients.Elasticsearch]]></title>
    <description><![CDATA[Erfahren Sie mehr über die Entwicklung des Elasticsearch .NET-Clients und den Übergang von NEST zu Elastic.Clients.Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>Einführung in den .NET-Client und NEST</h2><p>In der .NET-Welt wird die Integration mit Elasticsearch seit langem durch die <code>NEST</code> -Bibliothek erleichtert, die als robuste Schnittstelle für Entwickler dient, um mit den leistungsstarken Such- und Analysefunktionen von Elasticsearch zu interagieren. <code>NEST</code>, entstanden aus dem Bedürfnis nach einem nativen .NET-Client für Elasticsearch, erfreute sich aufgrund seines umfangreichen Funktionsumfangs und seiner nahtlosen Integrationsmöglichkeiten schnell großer Beliebtheit bei Entwicklern.</p><p>Seit fast <a href="https://github.com/elastic/elasticsearch-net/commit/724f932ba598915c8c3c35a19827fdfa4f782c1d">14 Jahren</a> und nur 8 Monaten nach <a href="https://github.com/elastic/elasticsearch/commit/ec72ca8b7a115f9b2eea3c76c518062b99a1d015">dem ersten Commit von Elasticsearch</a> verfolgt NEST die Elasticsearch-Releases zuverlässig.</p><h2>Umstellung von NEST auf Elastic.Clients.Elasticsearch</h2><p>Mit der Weiterentwicklung von Elasticsearch wurde die Wartung der komplexen Codebasis von <code>NEST</code> zunehmend schwieriger. Wir erkannten die Notwendigkeit eines nachhaltigeren Ansatzes für die Cliententwicklung und machten uns auf den Weg, den .NET-Client von Grund auf neu zu gestalten. Es dauerte fast ein Jahr, bis wir eine erste Beta-Version veröffentlichen konnten, und ein weiteres Jahr, bis wir nahezu jeden einzelnen Server-Endpunkt unterstützen konnten. Eine der schwierigsten Entscheidungen war, den Umfang der Bibliothek zu reduzieren, um stattdessen die Wartbarkeit zu priorisieren.</p><p>Angesichts des Umfangs der Elasticsearch-API-Oberfläche ist es heute nicht mehr praktikabel, über 450 Endpunkte und fast 3000 Typen (Anfragen, Antworten, Abfragen, Aggregationen usw.) manuell zu pflegen. Um eine konsistente, genaue und zeitnahe Abstimmung zwischen Sprachclients und Elasticsearch zu gewährleisten, werden die 8.x-Clients und viele der zugehörigen Typen jetzt automatisch aus einer <a href="https://github.com/elastic/elasticsearch-specification">gemeinsamen Spezifikation</a> generiert. Dies ist eine gängige Lösung, um die Abstimmung zwischen Client und Server bei SDKs und Bibliotheken wie denen für Azure, AWS und die Google Cloud Platform aufrechtzuerhalten.</p><p>Die Elasticsearch-Spezifikation wurde vor über 8 Jahren durch den Export der Typzuordnungen aus <code>NEST</code> erstellt, und dank der harten Arbeit des Client-Teams können wir nun dieselbe Spezifikation verwenden, um einen neuen .NET-Client (und Clients für zahlreiche andere Sprachen wie Java, Go usw.) zu erstellen.</p><p>Mit der Veröffentlichung von Version 8.13 wurde die Abschaffung von <code>NEST</code> offiziell bekannt gegeben. Mit dem Übergang von Elasticsearch zu <code>Elastic.Clients.Elasticsearch</code> wird <code>NEST</code> schrittweise auslaufen und zum Jahresende endgültig eingestellt. Entwickler werden dringend gebeten, frühzeitig mit den Migrationsbemühungen zu beginnen, um einen reibungslosen Übergang zu gewährleisten und mögliche Störungen zu minimieren. Die Verwendung <code>Elastic.Clients.Elasticsearch</code> gewährleistet nicht nur die Kompatibilität mit den neuesten Serverfunktionen, sondern schützt Anwendungen auch vor veralteten Funktionen für die Zukunft.</p><h2>Elastic.Clients.Elasticsearch: Funktionen und Änderungen im Überblick</h2><p>Durch den Wechsel zum v8-Client <code>Elastic.Clients.Elasticsearch</code> erhalten Sie Zugriff auf alle neuen Funktionen von Elasticsearch 8 und profitieren zudem von zahlreichen Modernisierungen der Bibliothek selbst. Allerdings bedeutet dies auch eine Reduzierung der Komfortfunktionen im Vergleich zum Vorgänger. Zu den neuen Kernfunktionen gehören die Abfragesprache <code>ES|QL</code>, moderne Machine-Learning-Funktionen (ML) und verbesserte Diagnosemöglichkeiten in Form von OpenTelemetry-kompatiblen Aktivitäten. Ab Version 8.13 unterstützt <code>Elastic.Clients.Elasticsearch</code> nahezu alle Serverfunktionen von Elasticsearch 8.</p><p>Eine wichtige grundlegende Änderung betrifft beispielsweise Aggregationen. In <code>NEST</code> sieht die Verwendung der Fluent API folgendermaßen aus:</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>Der V8-Client erfordert hingegen folgende Syntax:</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>Migration von NEST v7 zu .NET-Client v8</h2><p>Ein ausführlicher Migrationsleitfaden ist hier verfügbar: <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.18/migration-guide.html">Migrationsleitfaden: Von NEST v7 zu .NET Client v8</a>.</p><h2>Weitere Ressourcen</h2><ul><li><p><a href="https://github.com/elastic/elasticsearch-net">Elastic.Clients.Elasticsearch v8 Client auf GitHub</a></p></li><li><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch">Elastic.Clients.Elasticsearch v8 Client auf NuGet</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>