Verwenden von Azure.Search.Documents in einer in C# geschriebenen .NET-Anwendung

In diesem Artikel wird erläutert, wie Sie Suchobjekte mit C# und der Azure.Search.Documents (Version 11)-Clientbibliothek im Azure SDK für .NET erstellen und verwalten.

Informationen zu Version 11

Azure SDK für .NET enthält eine Azure.Search.Documents-Clientbibliothek des Azure SDK-Teams, die funktional der vorherigen Clientbibliothek Microsoft.Azure.Search entspricht. Version 11 ist in Bezug auf die Programmierbarkeit von Azure konsistenter. Beispiele sind die AzureKeyCredential-Schlüsselauthentifizierung und System.Text.Json.Serialization für die JSON-Serialisierung.

Wie die Vorgängerversionen können Sie diese Bibliothek zu Folgendem verwenden:

  • Erstellen und Verwalten von Suchindizes, Datenquellen, Indexern, Skillsets und Synonymzuordnungen
  • Laden und Verwalten von Suchdokumenten in einem Index
  • Ausführen von Abfragen, ohne sich um die Details von HTTP und JSON kümmern zu müssen
  • Aufrufen und Verwalten von KI-Anreicherung (Skillsets) und Ausgaben

Die Bibliothek wird als einzelnes Azure.Search.Documents-NuGet-Paket verteilt. Dieses enthält alle APIs, die für den programmgesteuerten Zugriff auf einen Suchdienst erforderlich sind.

Die Clientbibliothek definiert Klassen wie SearchIndex, SearchField und SearchDocument sowie Operationen wie SearchIndexClient.CreateIndex und SearchClient.Search für die Klassen SearchIndexClient und SearchClient. Diese Klassen sind in die folgenden Namespaces aufgeteilt:

Azure.Search.Documents (Version 11) zielt auf die Suchdienstspezifikation 2020-06-30.

Die Clientbibliothek bietet keine Dienstverwaltungsvorgänge wie das Erstellen und Skalieren von Suchdiensten und das Verwalten von API-Schlüsseln. Wenn Sie Ihre Ressourcen für die Suche in einer .NET-Anwendung verwalten müssen, können Sie die Microsoft.Azure.Management.Search-Bibliothek im Azure SDK für .NET verwenden.

Upgrade auf Version 11

Wenn Sie die vorherige Version des .NET SDK verwendet haben und auf die aktuelle, allgemein verfügbare Version aktualisieren möchten, lesen Sie bitte Upgrade auf Azure AI Search .NET SDK Version 11.

SDK-Anforderungen

  • Visual Studio 2019 oder höher

  • Ihr eigener Azure AI Search Service. Um das SDK verwenden zu können, benötigen Sie den Namen Ihres Dienstes und einen oder mehrere API-Schlüssel. Erstellen Sie einen Dienst im Portal, wenn Sie über keinen verfügen.

  • Laden Sie das Azure.Search.Documents-Paket über Extras>NuGet-Paket-Manager> NuGet-Pakete für Projektmappe verwalten… in Visual Studio herunter. Suchen Sie nach dem Paketname Azure.Search.Documents.

Azure SDK für .NET entspricht .NET Standard 2.0.

Beispielanwendung

Dieser Artikel „lehrt durch Beispiele“ und stützt sich auf das Codebeispiel DotNetHowTo auf GitHub, um grundlegende Konzepte in Azure AI Search zu veranschaulichen - insbesondere, wie man einen Suchindex erstellt, lädt und abfragt.

Nehmen Sie für den restlichen Artikel einen neuen Index namens „hotels“ an, der mit einigen Dokumenten aufgefüllt ist, sowie mehrere Abfragen, die mit den Ergebnissen übereinstimmen.

Unten sehen Sie das Hauptprogramm mit dem allgemeinen Flow:

// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
    IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
    IConfigurationRoot configuration = builder.Build();

    SearchIndexClient indexClient = CreateSearchIndexClient(configuration);

    string indexName = configuration["SearchIndexName"];

    Console.WriteLine("{0}", "Deleting index...\n");
    DeleteIndexIfExists(indexName, indexClient);

    Console.WriteLine("{0}", "Creating index...\n");
    CreateIndex(indexName, indexClient);

    SearchClient searchClient = indexClient.GetSearchClient(indexName);

    Console.WriteLine("{0}", "Uploading documents...\n");
    UploadDocuments(searchClient);

    SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);

    Console.WriteLine("{0}", "Run queries...\n");
    RunQueries(indexClientForQueries);

    Console.WriteLine("{0}", "Complete.  Press any key to end application...\n");
    Console.ReadKey();
}

Als Nächstes sehen Sie einen Screenshotausschnitt der Ausgabe unter der Annahme, dass Sie diese Anwendung mit einem gültigen Dienstnamen und API-Schlüsseln ausführen:

Screenshot of the Console.WriteLine output from the sample program.

Clienttypen

Die Clientbibliothek verwendet drei Clienttypen für verschiedene Vorgänge: SearchIndexClient zum Erstellen, Aktualisieren oder Löschen von Indizes, SearchClient zum Laden oder Abfragen eines Index und SearchIndexerClient zum Arbeiten mit Indexern und Skillsets. In diesem Artikel werden hauptsächlich die ersten beiden behandelt.

Für alle Clients ist mindestens der Dienstname oder der Endpunkt und ein API-Schlüssel erforderlich. In der Regel werden diese Informationen in einer Konfigurationsdatei angegeben, ähnlich zu den Angaben in der appsettings.json-Datei der DotNetHowTo-Beispielanwendung. Fügen Sie für Lesevorgänge in der Konfigurationsdatei Ihrem Programm using Microsoft.Extensions.Configuration; hinzu.

Die folgende Anweisung erstellt den Indexclient, der verwendet wird, um Indizes zu erstellen, zu aktualisieren oder zu löschen. Er benötigt einen Dienst-Endpunkt und einen API-Schlüssel für den Administrator.

private static SearchIndexClient CreateSearchIndexClient(IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string adminApiKey = configuration["SearchServiceAdminApiKey"];

    SearchIndexClient indexClient = new SearchIndexClient(new Uri(searchServiceEndPoint), new AzureKeyCredential(adminApiKey));
    return indexClient;
}

Die nächste Anweisung erstellt den Suchclient, der verwendet wird, um Dokumente zu laden oder Abfragen auszuführen. Für SearchClient ist ein Index erforderlich. Sie benötigen einen Admin-API-Schlüssel, um Dokumente zu laden, aber Sie können einen Abfrage-API-Schlüssel verwenden, um Abfragen auszuführen.

string indexName = configuration["SearchIndexName"];

private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string queryApiKey = configuration["SearchServiceQueryApiKey"];

    SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
    return searchClient;
}

Hinweis

Wenn Sie einen ungültigen Schlüssel für den Importvorgang angeben (z. B. einen Abfrageschlüssel, statt eines Administratorschlüssels), gibt SearchClient beim ersten Aufruf einer Vorgangsmethode eine CloudException-Ausnahme mit der Fehlermeldung „Unzulässig“ zurück. Überprüfen Sie in diesem Fall den API-Schlüssel.

Löschen des Index

In den frühen Entwicklungsphasen sollten Sie eine DeleteIndex-Anweisung einschließen, um einen laufenden Index zu löschen, damit Sie ihn mit aktualisierter Definition neu erstellen können. Der Beispielcode für Azure AI Search enthält oft einen Löschschritt, damit Sie das Beispiel erneut ausführen können.

Die folgende Zeile ruft DeleteIndexIfExists auf:

Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, indexClient);

Diese Methode verwendet die bereitgestellte Klasse SearchIndexClient, um zu überprüfen, ob der Index vorhanden ist, und löscht ihn gegebenenfalls:

private static void DeleteIndexIfExists(string indexName, SearchIndexClient indexClient)
{
    try
    {
        if (indexClient.GetIndex(indexName) != null)
        {
            indexClient.DeleteIndex(indexName);
        }
    }
    catch (RequestFailedException e) when (e.Status == 404)
    {
        // Throw an exception if the index name isn't found
        Console.WriteLine("The index doesn't exist. No deletion occurred.");

Hinweis

Im Beispielcode dieses Artikels werden der Einfachheit halber die synchronen Methoden verwenden. In Ihren Anwendungen sollten Sie jedoch die asynchronen Methoden verwenden, damit sie skaliert werden können und reaktionsfähig bleiben. In der oben gezeigten Methode könnten Sie DeleteIndexAsync zum Beispiel auch anstelle von DeleteIndex verwenden.

Erstellen eines Index

Sie können SearchIndexClient verwenden, um einen Index zu erstellen.

Die Methode unten erstellt ein neues SearchIndex-Objekt mit einer Liste von SearchField-Objekten, die das Schema des neuen Index definieren. Jedes Feld weist einen Namen, einen Datentyp und mehrere Attribute auf, die das Suchverhalten des Felds definieren.

Felder können in einer Modellklasse mithilfe von FieldBuilder definiert werden. Die Klasse FieldBuilder verwendet Reflexion, um durch Untersuchen der öffentlichen Eigenschaften und Attribute der entsprechenden Hotel-Modellklasse eine Liste von SearchField-Objekten für den Index zu erstellen. Wir werden uns die Klasse Hotel später genauer ansehen.

private static void CreateIndex(string indexName, SearchIndexClient indexClient)
{
    FieldBuilder fieldBuilder = new FieldBuilder();
    var searchFields = fieldBuilder.Build(typeof(Hotel));

    var definition = new SearchIndex(indexName, searchFields);

    indexClient.CreateOrUpdateIndex(definition);
}

Neben Feldern können Sie dem Index auch Bewertungsprofile, Vorschlagsfunktionen oder CORS-Optionen hinzufügen (diese Parameter fehlen im Beispiel, um es einfach zu halten). Weitere Informationen zum SearchIndex-Objekt und seinen Bestandteilen finden Sie in der SearchIndex-Eigenschaftenliste sowie in der Referenz zur REST-API.

Hinweis

Sie können die Liste der Field-Objekte immer direkt erstellen, anstatt FieldBuilder bei Bedarf zu verwenden. Sie möchten z.B. vielleicht keine Modellklasse verwenden oder Sie müssen möglicherweise eine vorhandene Modellklasse verwenden, die Sie nicht durch Hinzufügen von Attributen ändern möchten.

Aufrufen von CreateIndex in Main()

Als Nächstes ruft Main die oben stehende Methode auf, um einen neuen Index mit dem Namen „hotels“ zu erstellen:

Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, indexClient);

Verwenden einer Modellklasse für die Datendarstellung

Im DotNetHowTo-Beispiel werden Modellklassen für die Datenstrukturen Hotel, Address und Room verwendet. Hotel verweist auf Address, ein komplexer Typ mit einzelner Ebene (ein mehrteiliges Feld) und Room (eine Sammlung mit mehrteiligen Feldern).

Sie können diese Typen verwenden, um den Index zu erstellen und zu laden und um die Antwort einer Abfrage zu strukturieren:

// Use-case: <Hotel> in a field definition
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));

// Use-case: <Hotel> in a response
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.GetResults())
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

Eine alternative Methode besteht darin, einem Index die Felder direkt hinzuzufügen. Im folgenden Beispiel werden nur einige Felder gezeigt.

 SearchIndex index = new SearchIndex(indexName)
 {
     Fields =
         {
             new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true, IsSortable = true },
             new SearchableField("hotelName") { IsFilterable = true, IsSortable = true },
             new SearchableField("hotelCategory") { IsFilterable = true, IsSortable = true },
             new SimpleField("baseRate", SearchFieldDataType.Int32) { IsFilterable = true, IsSortable = true },
             new SimpleField("lastRenovationDate", SearchFieldDataType.DateTimeOffset) { IsFilterable = true, IsSortable = true }
         }
 };

Felddefinitionen

Ihr Datenmodell in .NET und das zugehörige Indexschema sollten so konzipiert sein, dass sie die Suchfunktion unterstützen, die Sie Ihrem Endbenutzer bieten möchten. Jedes Objekt der obersten Ebene in .NET, z. B. ein Suchdokument in einem Suchindex, entspricht einem Suchergebnis, das Sie auf Ihrer Benutzeroberfläche präsentieren würden. In einer Hotelsuchanwendung können Ihre Endbenutzer beispielsweise nach Hotelnamen, der Ausstattung des Hotels oder den Merkmalen eines bestimmten Zimmers suchen.

In jeder Klasse wird ein Feld mit einem Datentyp und Attributen definiert, die bestimmen, wie es verwendet wird. Der Name jeder öffentlichen Eigenschaft in jeder Klasse wird einem Feld mit dem gleichen Namen in der Indexdefinition zugeordnet.

Sehen Sie sich den folgenden Codeausschnitt an, der mehrere Felddefinitionen von der „Hotel“-Klasse pullt. Beachten Sie, dass es sich bei „Address“ und „Rooms“ um C#-Typen mit eigenen Klassendefinitionen handelt. Sehen Sie sich dazu den Beispielcode an, wenn Sie sie anzeigen möchten. Bei beiden handelt es sich um komplexe Typen. Weitere Informationen finden Sie unter Modellieren komplexer Datentypen in Azure Search.

public partial class Hotel
{
    [SimpleField(IsKey = true, IsFilterable = true)]
    public string HotelId { get; set; }

    [SearchableField(IsSortable = true)]
    public string HotelName { get; set; }

    [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
    public string Description { get; set; }

    [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
    public string Category { get; set; }

    [JsonIgnore]
    public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

    [SearchableField]
    public Address Address { get; set; }

    public Room[] Rooms { get; set; }

Auswählen einer Feldklasse

Beim Definieren von Feldern können Sie die SearchField-Basisklasse verwenden, oder Sie nutzen abgeleitete Hilfsprogrammmodelle, die als „Vorlagen“ fungieren, mit vorab konfigurierten Eigenschaften.

Genau ein Feld in Ihrem Index muss als Dokumentschlüssel dienen (IsKey = true). Dabei muss es sich um eine Zeichenfolge handeln, und diese muss jedes Dokument eindeutig identifizieren können. Außerdem ist IsHidden = true erforderlich, das heißt, die Sichtbarkeit in den Suchergebnissen darf hier nicht gegeben sein.

Feldtyp Beschreibung und Verwendung
SearchField Hierbei handelt es sich um eine Basisklasse, für die die meisten Eigenschaften auf NULL festgelegt sind, mit Ausnahme von einem erforderlichen Wert für Name und von AnalyzerName, wofür der Standardwert „Lucene“ gilt.
SimpleField Hierbei handelt es sich um ein Hilfsprogrammmodell. Hierbei kann es sich um einen beliebigen Datentyp handeln, der immer nicht durchsuchbar (wird bei Volltextsuchabfragen ignoriert) und immer abrufbar (nicht ausgeblendet) ist. Andere Attribute sind standardmäßig deaktiviert, können jedoch aktiviert werden. Sie können ein SimpleField für Dokument-IDs oder Felder verwenden, die nur in Filtern, Facets oder Bewertungsprofilen verwendet werden. Sofern dies der Fall ist, stellen Sie sicher, dass Sie alle Attribute anwenden, die für das Szenario erforderlich sind, z. B. IsKey = true für eine Dokument-ID. Weitere Informationen finden Sie unter SimpleFieldAttribute.cs im Quellcode.
SearchableField Hierbei handelt es sich um ein Hilfsprogrammmodell. Hierbei muss es sich um eine Zeichenfolge handeln, die immer durchsuchbar und abrufbar ist. Andere Attribute sind standardmäßig deaktiviert, können jedoch aktiviert werden. Da dieser Feldtyp durchsuchbar ist, unterstützt er Synonyme und die gesamte Palette der Eigenschaften des Analysetools. Weitere Informationen finden Sie unter SearchableFieldAttribute.cs im Quellcode.

Ob Sie die grundlegende SearchField-API oder eines der Hilfsmodelle verwenden, müssen Sie Filter-, Facet- und Sortierattribute explizit aktivieren. Beispielsweise müssen IsFilterable, IsSortable und IsFacetable explizit mit Attributen versehen werden, wie im obigen Beispiel gezeigt.

Hinzufügen von Feldattributen

Beachten Sie, wie jedes Feld mit Attributen wie IsFilterable, IsSortable, IsKey und AnalyzerName ergänzt wird. Diese Attribute werden direkt den entsprechenden Feldattributen in einem Azure AI Search Index zugeordnet. Die FieldBuilder-Klasse verwendet diese Eigenschaften, um Felddefinitionen für den Index zu erstellen.

Feldtypzuordnung

Die .NET-Typen dieser Eigenschaften stimmen mit den entsprechenden Feldtypen in der Indexdefinition überein. Die Zeichenfolgeeigenschaft Category passt zum Beispiel zum Feld category, das den Typ Edm.String hat. Ähnliche Zuordnungen bestehen auch zwischen bool?, Edm.Boolean, DateTimeOffset? und Edm.DateTimeOffset usw.

Haben Sie die SmokingAllowed-Eigenschaft bemerkt?

[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;

Das JsonIgnore-Attribut in dieser Eigenschaft weist FieldBuilder an, sie nicht als Feld an den Index zu serialisieren. Dies ist eine gute Möglichkeit, clientseitig berechnete Eigenschaften zu erstellen, die Sie als Hilfsprogramme in Ihrer Anwendung nutzen können. In diesem Fall spiegelt die SmokingAllowed-Eigenschaft wider, ob in einem Room in der Rooms-Sammlung Rauchen erlaubt. Wenn alle „false“ sind, bedeutet dies, dass im gesamten Hotel Rauchverbot gilt.

Laden eines Index

Im nächsten Schritt in Main wird der neu erstellte Index „hotels“ aufgefüllt. Zum Auffüllen des Index wird die folgende Methode verwendet: (Ein Teil des Codes wurde zur Veranschaulichung durch „...“ ersetzt. Beachten Sie die vollständige Beispiellösung für den vollständigen Datenauffüllungscode.)

private static void UploadDocuments(SearchClient searchClient)
{
    IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "1",
                HotelName = "Secret Point Motel",
                ...
                Address = new Address()
                {
                    StreetAddress = "677 5th Ave",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Budget Room, 1 Queen Bed (Cityside)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Budget Room, 1 King Bed (Mountain View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Deluxe Room, 2 Double Beds (City View)",
                        ...
                    }
                }
            }),
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "2",
                HotelName = "Twin Dome Motel",
                ...
                {
                    StreetAddress = "140 University Town Center Dr",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Suite, 2 Double Beds (Mountain View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Standard Room, 1 Queen Bed (City View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Budget Room, 1 King Bed (Waterfront View)",
                        ...
                    }
                }
            }),
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "3",
                HotelName = "Triple Landscape Hotel",
                ...
                Address = new Address()
                {
                    StreetAddress = "3393 Peachtree Rd",
                    ...
                },
                Rooms = new Room[]
                {
                    new Room()
                    {
                        Description = "Standard Room, 2 Queen Beds (Amenities)",
                        ...
                    },
                    new Room ()
                    {
                        Description = "Standard Room, 2 Double Beds (Waterfront View)",
                        ...
                    },
                    new Room()
                    {
                        Description = "Deluxe Room, 2 Double Beds (Cityside)",
                        ...
                    }
                }
            }
        };

    try
    {
        IndexDocumentsResult result = searchClient.IndexDocuments(batch);
    }
    catch (Exception)
    {
        // Sometimes when your Search service is under load, indexing will fail for some of the documents in
        // the batch. Depending on your application, you can take compensating actions like delaying and
        // retrying. For this simple demo, we just log the failed document keys and continue.
        Console.WriteLine("Failed to index some of the documents: {0}");
    }

    Console.WriteLine("Waiting for documents to be indexed...\n");
    Thread.Sleep(2000);

Diese Methode besteht aus vier Teilen. Die erste erstellt ein Array aus drei Hotel Objekten mit jeweils drei Room Objekten, die als unsere Eingabedaten zum Hochladen in den Index dienen. Diese Daten sind der Einfachheit halber fest codiert. In einer tatsächlichen Anwendung stammen Daten wahrscheinlich aus einer externen Datenquelle, z. B. einer SQL-­Datenbank.

Im zweiten Teil wird IndexDocumentsBatch mit den Dokumenten erstellt. Sie geben den Vorgang an, den Sie auf den Batch zum Zeitpunkt seiner Erstellung anwenden möchten, in diesem Fall durch Aufruf von IndexDocumentsAction.Upload. Der Stapel wird dann mit der Methode IndexDocuments in den Azure AI Search-Index hochgeladen.

Hinweis

In diesem Beispiel werden nur Dokumente hochgeladen. Wenn Sie z.B. Änderungen in vorhandenen Dokumenten zusammenführen oder Dokumente löschen möchten, können Sie zum Erstellen von Batches IndexDocumentsAction.Merge, IndexDocumentsAction.MergeOrUpload oder IndexDocumentsAction.Delete aufrufen. Sie können auch verschiedene Operationen in einem einzigen Stapel mischen, indem Sie IndexBatch.New aufrufen, der eine Sammlung von IndexDocumentsAction Objekten enthält, von denen jedes Azure AI Search anweist, eine bestimmte Operation an einem Dokument durchzuführen. Sie können jede IndexDocumentsAction mit ihrem eigenen Vorgang erstellen, indem Sie die entsprechende Methode aufrufen, z.B. IndexDocumentsAction.Merge, IndexAction.Upload usw.

Der dritte Teil dieser Methode ist ein Catch-Block, der einen wichtigen Fehlerfall bei der Indizierung abfängt. Falls der Suchdienst Dokumente im Batch nicht indizieren kann, wird eine RequestFailedException ausgelöst. Eine Ausnahme kann auftreten, wenn Sie Dokumente indizieren, während Ihr Dienst stark ausgelastet ist. Es wird dringend empfohlen, diesen Fall in Ihrem Code explizit zu behandeln. Zum Beispiel kann die Indizierung der zuvor nicht indizierten Dokumente nach einer Weile wieder aufgenommen werden oder der Vorgang kann, wie im Beispiel gezeigt, nach der Aufzeichnung des Fehlers fortgeführt werden. Je nach Datenkonsistenzanforderungen Ihrer Anwendung sind aber auch andere Lösungen möglich. Eine Alternative besteht darin, SearchIndexingBufferedSender für intelligente Batchverarbeitung, automatische Bereinigung und die Wiederholung fehlgeschlagener Indizierungsaktionen zu verwenden. Mehr Kontext dazu finden Sie in diesem Beispiel.

Der letzte Teil der Methode UploadDocuments fügt eine Verzögerung von zwei Sekunden hinzu. Da die Indizierung in Ihrem Suchdienst asynchron erfolgt, muss die Beispielanwendung einen Augenblick warten, damit sichergestellt ist, dass die Dokumente für Suchen zur Verfügung stehen. Verzögerungen wie diese sind in der Regel nur in Demos, Tests und Beispielanwendungen erforderlich.

Aufrufen von UploadDocuments in Main()

Der folgende Codeausschnitt richtet eine Instanz von SearchClient mithilfe der GetSearchClient-Methode von indexClient ein. indexClient verwendet einen API-Schlüssel für Administratoren für die dazugehörigen Anforderungen. Dieser ist erforderlich, um Dokumente laden oder aktualisieren zu können.

Ein alternativer Ansatz besteht darin, SearchClient direkt aufzurufen und dabei einen API-Schlüssel für Administratoren für AzureKeyCredential zu übergeben.

SearchClient searchClient = indexClient.GetSearchClient(indexName);

Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(searchClient);

Ausführen von Abfragen

Richten Sie zunächst einen SearchClient ein, der den Dienstendpunkt und den Abfrage-API-Schlüssel aus appsettings.json liest:

private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
    string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
    string queryApiKey = configuration["SearchServiceQueryApiKey"];

    SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
    return searchClient;
}

Definieren Sie dann eine Methode, die eine Abfrageanforderung sendet.

Jedes Mal, wenn eine Abfrage von der Methode ausgeführt wird, erstellt diese Methode ein neues SearchOptions-Objekt. Mit diesem Objekt werden zusätzliche Abfrageoptionen wie Sortierung, Filter, Paging und Facettierung festgelegt. Bei dieser Methode werden die Eigenschaften Filter, Select und OrderBy für verschiedene Abfragen festgelegt. Weitere Informationen zur Syntax von Suchabfrageausdrücken finden Sie unter Einfache Abfragesyntax in Azure Cognitive Search.

Der nächste Schritt ist die Abfrageausführung. Zum Ausführen der Suche wird die SearchClient.Search-Methode verwendet. Übergeben Sie für jede Abfrage den zu verwendenden Suchtext als Zeichenfolge (bzw. "*", wenn kein Suchtext vorliegt) sowie die zuvor erstellten Suchoptionen. Außerdem geben wir Hotel als Typparameter für SearchClient.Search an, wodurch das SDK angewiesen wird, die Dokumente im Suchergebnis in Objekte des Typs Hotel zu deserialisieren.

private static void RunQueries(SearchClient searchClient)
{
    SearchOptions options;
    SearchResults<Hotel> results;

    Console.WriteLine("Query 1: Search for 'motel'. Return only the HotelName in results:\n");

    options = new SearchOptions();
    options.Select.Add("HotelName");

    results = searchClient.Search<Hotel>("motel", options);

    WriteDocuments(results);

    Console.Write("Query 2: Apply a filter to find hotels with rooms cheaper than $100 per night, ");
    Console.WriteLine("returning the HotelId and Description:\n");

    options = new SearchOptions()
    {
        Filter = "Rooms/any(r: r/BaseRate lt 100)"
    };
    options.Select.Add("HotelId");
    options.Select.Add("Description");

    results = searchClient.Search<Hotel>("*", options);

    WriteDocuments(results);

    Console.Write("Query 3: Search the entire index, order by a specific field (lastRenovationDate) ");
    Console.Write("in descending order, take the top two results, and show only hotelName and ");
    Console.WriteLine("lastRenovationDate:\n");

    options =
        new SearchOptions()
        {
            Size = 2
        };
    options.OrderBy.Add("LastRenovationDate desc");
    options.Select.Add("HotelName");
    options.Select.Add("LastRenovationDate");

    results = searchClient.Search<Hotel>("*", options);

    WriteDocuments(results);

    Console.WriteLine("Query 4: Search the HotelName field for the term 'hotel':\n");

    options = new SearchOptions();
    options.SearchFields.Add("HotelName");

    //Adding details to select, because "Location" isn't supported yet when deserializing search result to "Hotel"
    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Category");
    options.Select.Add("Tags");
    options.Select.Add("ParkingIncluded");
    options.Select.Add("LastRenovationDate");
    options.Select.Add("Rating");
    options.Select.Add("Address");
    options.Select.Add("Rooms");

    results = searchClient.Search<Hotel>("hotel", options);

    WriteDocuments(results);
}

Definieren Sie als dritten Schritt eine Methode, die die Antwort schreibt und dabei jedes Dokument in die Konsole schreibt:

private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.GetResults())
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

Aufrufen von RunQueries in Main()

SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);

Console.WriteLine("{0}", "Running queries...\n");
RunQueries(indexClientForQueries);

Informationen zu Abfragekonstrukten

Lassen Sie uns die einzelnen Abfragen einmal näher betrachten. Hier ist der Code zum Ausführen der ersten Abfrage:

options = new SearchOptions();
options.Select.Add("HotelName");

results = searchClient.Search<Hotel>("motel", options);

WriteDocuments(results);

In diesem Fall durchsuchen wir den gesamten Index nach dem Wort „motel“ in einem beliebigen Suchfeld und möchten nur die Hotelnamen abrufen, wie durch die Option Select angegeben. Dies sind die Ergebnisse:

Name: Secret Point Motel

Name: Twin Dome Motel

Verwenden Sie in der zweiten Abfrage einen Filter, um Räume mit einem nächtlichen Preis von weniger als 100 $ auszuwählen. Geben Sie in den Ergebnissen nur Hotel-ID und -Beschreibung zurück:

options = new SearchOptions()
{
    Filter = "Rooms/any(r: r/BaseRate lt 100)"
};
options.Select.Add("HotelId");
options.Select.Add("Description");

results = searchClient.Search<Hotel>("*", options);

Diese Abfrage verwendet einen $filter-OData-Ausdruck, Rooms/any(r: r/BaseRate lt 100), um die Dokumente im Index zu filtern. Dadurch wird der Any-Operator verwendet, um „BaseRate lt 100“ auf jedes Element in der Sammlung „Rooms“ anzuwenden. Weitere Informationen finden Sie unter OData-Filtersyntax.

Suchen Sie in der dritten Abfrage nach den zwei besten, in jüngster Zeit renovierten Hotels, und zeigen Sie deren Hotelnamen und das Datum der letzten Renovierung an. Hier ist der -Code:

options =
    new SearchOptions()
    {
        Size = 2
    };
options.OrderBy.Add("LastRenovationDate desc");
options.Select.Add("HotelName");
options.Select.Add("LastRenovationDate");

results = searchClient.Search<Hotel>("*", options);

WriteDocuments(results);

Suchen Sie in der letzten Abfrage nach allen Hotelnamen, die das Wort „hotel“ enthalten:

options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Description");
options.Select.Add("Category");
options.Select.Add("Tags");
options.Select.Add("ParkingIncluded");
options.Select.Add("LastRenovationDate");
options.Select.Add("Rating");
options.Select.Add("Address");
options.Select.Add("Rooms");

results = searchClient.Search<Hotel>("hotel", options);

WriteDocuments(results);

In diesem Abschnitt schließt sich der Kreis zur Einführung in das .NET SDK, hören Sie hier aber noch nicht auf. Im nächsten Abschnitt werden weitere Ressourcen vorgeschlagen, um mehr über die Programmierung mit Azure AI Search zu erfahren.

Nächste Schritte