Freigeben über


Schnellstart: Volltextsuche

In dieser Schnellstartanleitung verwenden Sie die Azure.Search.Documents-Clientbibliothek, um einen Suchindex mit Beispieldaten für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Voraussetzungen für Microsoft Entra ID

Für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra-ID müssen Sie:

Abrufen von Ressourceninformationen

Sie müssen die folgenden Informationen abrufen, um Ihre Anwendung bei Ihrem Azure KI-Suche-Dienst zu authentifizieren:

Variablenname Wert
SEARCH_API_ENDPOINT Diesen Wert finden Sie im Azure-Portal. Wählen Sie Ihren Suchdienst und dann im linken Menü die Option Übersicht aus. Der URL-Wert unter Essentials ist der Endpunkt, den Sie benötigen. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

Erfahren Sie mehr über schlüssellose Authentifizierung und das Festlegen von Umgebungsvariablen.

Einrichten

  1. Erstellen Sie einen neuen Ordner full-text-quickstart für die Anwendung, und öffnen Sie Visual Studio Code in diesem Ordner mit dem folgenden Befehl:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Erstellen Sie eine neue Konsolenanwendung mit dem folgenden Befehl:

    dotnet new console
    
  3. Installieren Sie die Clientbibliothek von Azure KI-Suche (Azure.Search.Documents) für .NET mit:

    dotnet add package Azure.Search.Documents
    
  4. Installieren Sie für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra ID das Paket Azure.Identity mit:

    dotnet add package Azure.Identity
    
  5. Melden Sie sich für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra ID mit dem folgenden Befehl bei Azure an:

    az login
    

Erstellen, Laden und Abfragen eines Suchindexes

Im vorherigen Abschnitt Einrichten haben Sie eine neue Konsolenanwendung erstellt und die Clientbibliothek von Azure KI-Suche installiert.

In diesem Abschnitt fügen Sie Code hinzu, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie führen das Programm aus, um die Ergebnisse in der Konsole anzuzeigen. Eine ausführliche Erläuterung des Codes finden Sie im Abschnitt Erläuterung des Codes.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
  1. Fügen Sie in Program.cs den folgenden Code ein. Bearbeiten Sie die Variablen serviceName und apiKey mit dem Namen Ihres Suchdienstes und dem Administrator-API-Schlüssel.

    using System;
    using Azure;
    using Azure.Identity;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Azure.Search.Documents.Models;
    
    namespace AzureSearch.Quickstart
    
    {
        class Program
        {
            static void Main(string[] args)
            {    
                // Your search service endpoint
                Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
    
                // Use the recommended keyless credential instead of the AzureKeyCredential credential.
                DefaultAzureCredential credential = new();
                //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");
    
                // Create a SearchIndexClient to send create/delete index commands
                SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential);
    
                // Create a SearchClient to load and query documents
                string indexName = "hotels-quickstart";
                SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential);
    
                // Delete index if it exists
                Console.WriteLine("{0}", "Deleting index...\n");
                DeleteIndexIfExists(indexName, searchIndexClient);
    
                // Create index
                Console.WriteLine("{0}", "Creating index...\n");
                CreateIndex(indexName, searchIndexClient);
    
                SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);
    
                // Load documents
                Console.WriteLine("{0}", "Uploading documents...\n");
                UploadDocuments(ingesterClient);
    
                // Wait 2 secondsfor indexing to complete before starting queries (for demo and console-app purposes only)
                Console.WriteLine("Waiting for indexing...\n");
                System.Threading.Thread.Sleep(2000);
    
                // Call the RunQueries method to invoke a series of queries
                Console.WriteLine("Starting queries...\n");
                RunQueries(searchClient);
    
                // End the program
                Console.WriteLine("{0}", "Complete. Press any key to end this program...\n");
                Console.ReadKey();
            }
    
            // Delete the hotels-quickstart index to reuse its name
            private static void DeleteIndexIfExists(string indexName, SearchIndexClient searchIndexClient)
            {
                searchIndexClient.GetIndexNames();
                {
                    searchIndexClient.DeleteIndex(indexName);
                }
            }
            // Create hotels-quickstart index
            private static void CreateIndex(string indexName, SearchIndexClient searchIndexClient)
            {
                FieldBuilder fieldBuilder = new FieldBuilder();
                var searchFields = fieldBuilder.Build(typeof(Hotel));
    
                var definition = new SearchIndex(indexName, searchFields);
    
                var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
                definition.Suggesters.Add(suggester);
    
                searchIndexClient.CreateOrUpdateIndex(definition);
            }
    
            // Upload documents in a single Upload request.
            private static void UploadDocuments(SearchClient searchClient)
            {
                IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
                    IndexDocumentsAction.Upload(
                        new Hotel()
                        {
                            HotelId = "1",
                            HotelName = "Stay-Kay City Hotel",
                            Description = "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                            Category = "Boutique",
                            Tags = new[] { "view", "air conditioning", "concierge" },
                            ParkingIncluded = false,
                            LastRenovationDate = new DateTimeOffset(2022, 1, 18, 0, 0, 0, TimeSpan.Zero),
                            Rating = 3.6,
                            Address = new Address()
                            {
                                StreetAddress = "677 5th Ave",
                                City = "New York",
                                StateProvince = "NY",
                                PostalCode = "10022",
                                Country = "USA"
                            }
                        }),
                    IndexDocumentsAction.Upload(
                        new Hotel()
                        {
                            HotelId = "2",
                            HotelName = "Old Century Hotel",
                            Description = "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
                            Category = "Boutique",
                            Tags = new[] { "pool", "free wifi", "concierge" },
                            ParkingIncluded = false,
                            LastRenovationDate = new DateTimeOffset(2019, 2, 18, 0, 0, 0, TimeSpan.Zero),
                            Rating = 3.60,
                            Address = new Address()
                            {
                                StreetAddress = "140 University Town Center Dr",
                                City = "Sarasota",
                                StateProvince = "FL",
                                PostalCode = "34243",
                                Country = "USA"
                            }
                        }),
                    IndexDocumentsAction.Upload(
                        new Hotel()
                        {
                            HotelId = "3",
                            HotelName = "Gastronomic Landscape Hotel",
                            Description = "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                            Category = "Suite",
                            Tags = new[] { "restaurant", "bar", "continental breakfast" },
                            ParkingIncluded = true,
                            LastRenovationDate = new DateTimeOffset(2015, 9, 20, 0, 0, 0, TimeSpan.Zero),
                            Rating = 4.80,
                            Address = new Address()
                            {
                                StreetAddress = "3393 Peachtree Rd",
                                City = "Atlanta",
                                StateProvince = "GA",
                                PostalCode = "30326",
                                Country = "USA"
                            }
                        }),
                    IndexDocumentsAction.Upload(
                        new Hotel()
                        {
                            HotelId = "4",
                            HotelName = "Sublime Palace Hotel",
                            Description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
                            Category = "Boutique",
                            Tags = new[] { "concierge", "view", "air conditioning" },
                            ParkingIncluded = true,
                            LastRenovationDate = new DateTimeOffset(2020, 2, 06, 0, 0, 0, TimeSpan.Zero),
                            Rating = 4.60,
                            Address = new Address()
                            {
                                StreetAddress = "7400 San Pedro Ave",
                                City = "San Antonio",
                                StateProvince = "TX",
                                PostalCode = "78216",
                                Country = "USA"
                            }
                        })
                    );
    
                try
                {
                    IndexDocumentsResult result = searchClient.IndexDocuments(batch);
                }
                catch (Exception)
                {
                    // If for some reason any documents are dropped during indexing, you can compensate by delaying and
                    // retrying. This simple demo just logs the failed document keys and continues.
                    Console.WriteLine("Failed to index some of the documents: {0}");
                }
            }
    
            // Run queries, use WriteDocuments to print output
            private static void RunQueries(SearchClient searchClient)
            {
                SearchOptions options;
                SearchResults<Hotel> response;
    
                // Query 1
                Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
                options = new SearchOptions()
                {
                    IncludeTotalCount = true,
                    Filter = "",
                    OrderBy = { "" }
                };
    
                options.Select.Add("HotelId");
                options.Select.Add("HotelName");
                options.Select.Add("Rating");
    
                response = searchClient.Search<Hotel>("*", options);
                WriteDocuments(response);
    
                // Query 2
                Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
                options = new SearchOptions()
                {
                    Filter = "Rating gt 4",
                    OrderBy = { "Rating desc" }
                };
    
                options.Select.Add("HotelId");
                options.Select.Add("HotelName");
                options.Select.Add("Rating");
    
                response = searchClient.Search<Hotel>("hotels", options);
                WriteDocuments(response);
    
                // Query 3
                Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
                options = new SearchOptions()
                {
                    SearchFields = { "Tags" }
                };
    
                options.Select.Add("HotelId");
                options.Select.Add("HotelName");
                options.Select.Add("Tags");
    
                response = searchClient.Search<Hotel>("pool", options);
                WriteDocuments(response);
    
                // Query 4 - Use Facets to return a faceted navigation structure for a given query
                // Filters are typically used with facets to narrow results on OnClick events
                Console.WriteLine("Query #4: Facet on 'Category'...\n");
    
                options = new SearchOptions()
                {
                    Filter = ""
                };
    
                options.Facets.Add("Category");
    
                options.Select.Add("HotelId");
                options.Select.Add("HotelName");
                options.Select.Add("Category");
    
                response = searchClient.Search<Hotel>("*", options);
                WriteDocuments(response);
    
                // Query 5
                Console.WriteLine("Query #5: Look up a specific document...\n");
    
                Response<Hotel> lookupResponse;
                lookupResponse = searchClient.GetDocument<Hotel>("3");
    
                Console.WriteLine(lookupResponse.Value.HotelId);
    
    
                // Query 6
                Console.WriteLine("Query #6: Call Autocomplete on HotelName...\n");
    
                var autoresponse = searchClient.Autocomplete("sa", "sg");
                WriteDocuments(autoresponse);
    
            }
    
            // Write search results to console
            private static void WriteDocuments(SearchResults<Hotel> searchResults)
            {
                foreach (SearchResult<Hotel> result in searchResults.GetResults())
                {
                    Console.WriteLine(result.Document);
                }
    
                Console.WriteLine();
            }
    
            private static void WriteDocuments(AutocompleteResults autoResults)
            {
                foreach (AutocompleteItem result in autoResults.Results)
                {
                    Console.WriteLine(result.Text);
                }
    
                Console.WriteLine();
            }
        }
    }
    
  2. Erstellen Sie im selben Ordner eine neue Datei mit dem Namen Hotel.cs, und fügen Sie den folgenden Code ein. Dieser Code definiert die Struktur eines Hoteldokuments.

    using System;
    using System.Text.Json.Serialization;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    
    namespace AzureSearch.Quickstart
    {
        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; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            [SearchableField]
            public Address Address { get; set; }
        }
    }
    
  3. Erstellen Sie eine neue Datei mit dem Namen Hotel.cs, und fügen Sie den folgenden Code ein, um die Struktur eines Hoteldokuments zu definieren. Attribute für das Feld bestimmen die Verwendung in einer Anwendung. So muss beispielsweise jedem Feld, das einen Filterausdruck unterstützt, das Attribut IsFilterable zugewiesen werden.

    using System;
    using System.Text.Json.Serialization;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    
    namespace AzureSearch.Quickstart
    {
        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; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            [SearchableField]
            public Address Address { get; set; }
        }
    }
    
  4. Erstellen Sie eine neue Datei mit dem Namen Address.cs, und fügen Sie den folgenden Code ein, um die Struktur eines Adressdokuments zu definieren.

    using Azure.Search.Documents.Indexes;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Address
        {
            [SearchableField(IsFilterable = true)]
            public string StreetAddress { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string City { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string StateProvince { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string PostalCode { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Country { get; set; }
        }
    }
    
  5. Erstellen Sie eine neue Datei mit dem Namen Hotel.Methods.cs, und fügen Sie den folgenden Code ein, um eine ToString()-Überschreibung für die Hotel-Klasse zu definieren.

    using System;
    using System.Text;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                if (!String.IsNullOrEmpty(HotelId))
                {
                    builder.AppendFormat("HotelId: {0}\n", HotelId);
                }
    
                if (!String.IsNullOrEmpty(HotelName))
                {
                    builder.AppendFormat("Name: {0}\n", HotelName);
                }
    
                if (!String.IsNullOrEmpty(Description))
                {
                    builder.AppendFormat("Description: {0}\n", Description);
                }
    
                if (!String.IsNullOrEmpty(Category))
                {
                    builder.AppendFormat("Category: {0}\n", Category);
                }
    
                if (Tags != null && Tags.Length > 0)
                {
                    builder.AppendFormat("Tags: [ {0} ]\n", String.Join(", ", Tags));
                }
    
                if (ParkingIncluded.HasValue)
                {
                    builder.AppendFormat("Parking included: {0}\n", ParkingIncluded.Value ? "yes" : "no");
                }
    
                if (LastRenovationDate.HasValue)
                {
                    builder.AppendFormat("Last renovated on: {0}\n", LastRenovationDate);
                }
    
                if (Rating.HasValue)
                {
                    builder.AppendFormat("Rating: {0}\n", Rating);
                }
    
                if (Address != null && !Address.IsEmpty)
                {
                    builder.AppendFormat("Address: \n{0}\n", Address.ToString());
                }
    
                return builder.ToString();
            }
        }
    }
    
  6. Erstellen Sie eine neue Datei mit dem Namen Address.Methods.cs, und fügen Sie den folgenden Code ein, um eine ToString()-Überschreibung für die Address-Klasse zu definieren.

    using System;
    using System.Text;
    using System.Text.Json.Serialization;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Address
        {
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                if (!IsEmpty)
                {
                    builder.AppendFormat("{0}\n{1}, {2} {3}\n{4}", StreetAddress, City, StateProvince, PostalCode, Country);
                }
    
                return builder.ToString();
            }
    
            [JsonIgnore]
            public bool IsEmpty => String.IsNullOrEmpty(StreetAddress) &&
                                   String.IsNullOrEmpty(City) &&
                                   String.IsNullOrEmpty(StateProvince) &&
                                   String.IsNullOrEmpty(PostalCode) &&
                                   String.IsNullOrEmpty(Country);
        }
    }
    
  7. Führen Sie den folgenden Befehl aus, um die Anwendung zu erstellen und auszuführen:

    dotnet run
    

Die Ausgabe umfasst Nachrichten aus Console.WriteLine, wobei Abfrageinformationen und -ergebnisse hinzugefügt werden.

Erläutern des Codes

In den vorherigen Abschnitten haben Sie eine neue Konsolenanwendung erstellt und die Clientbibliothek von Azure KI-Suche installiert. Sie haben Code hinzugefügt, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie haben das Programm ausgeführt, um die Ergebnisse in der Konsole anzuzeigen.

In diesem Abschnitt erläutern wir den Code, den Sie der Konsolenanwendung hinzugefügt haben.

Erstellen eines Suchclients

In Program.cs haben Sie zwei Clients erstellt:

Beide Clients benötigen den Suchdienst-Endpunkt und die Anmeldeinformationen, die weiter oben im Abschnitt Ressourceninformationen beschrieben wurden.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
static void Main(string[] args)
{
    // Your search service endpoint
    Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");

    // Use the recommended keyless credential instead of the AzureKeyCredential credential.
    DefaultAzureCredential credential = new();
    //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");

    // Create a SearchIndexClient to send create/delete index commands
    SearchIndexClient searchIndexClient = new SearchIndexClient(serviceEndpoint, credential);

    // Create a SearchClient to load and query documents
    string indexName = "hotels-quickstart";
    SearchClient searchClient = new SearchClient(serviceEndpoint, indexName, credential);
    
    // REDACTED FOR BREVITY . . . 
}

Erstellen eines Index

In dieser Schnellstartanleitung wird ein Hotelindex erstellt, in den Sie Hoteldaten laden und Abfragen dafür ausführen. In diesem Schritt definieren Sie die Felder im Index. Jede Felddefinition enthält einen Namen, einen Datentyp und Attribute, die bestimmen, wie das Feld verwendet wird.

In diesem Beispiel werden aus Gründen der Einfachheit und Lesbarkeit synchrone Methoden der Bibliothek Azure.Search.Documents verwendet. In Produktionsszenarien sollten Sie jedoch asynchrone Methoden verwenden, um Ihre App skalierbar und reaktionsfähig zu halten. So würden Sie beispielsweise CreateIndexAsync anstatt CreateIndex verwenden.

Definieren der Strukturen

Sie haben zwei Hilfsklassen erstellt, Hotel.cs und Address.cs, um die Struktur eines Hoteldokuments und seiner Adresse zu definieren. Die Hotel-Klasse enthält Felder für Hotel-ID, Name, Beschreibung, Kategorie, Tags, Parkplätze, Renovierungsdatum, Bewertung und Adresse. Die Address-Klasse enthält Felder für Straße und Hausnummer, Ort, Bundesland/Kanton, Postleitzahl und Land/Region.

In der Azure.Search.Documents-Clientbibliothek können Sie SearchableField und SimpleField verwenden, um Felddefinitionen zu optimieren. Beide sind Ableitungen von SearchField und können Ihren Code möglicherweise vereinfachen:

  • SimpleField kann ein beliebiger Datentyp sein, ist nie durchsuchbar (wird bei Volltextsuchabfragen ignoriert) und ist abrufbar (nicht ausgeblendet). 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 muss eine Zeichenfolge sein und ist immer „durchsuchbar“ und „abrufbar“. 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 vorherigen Beispiel gezeigt.

Erstellen des Suchindex

In Program.cs erstellen Sie ein SearchIndex-Objekt und rufen dann die CreateIndex-Methode auf, um den Index in Ihrem Suchdienst auszudrücken. Der Index enthält auch ein SearchSuggester-Element, um die automatische Vervollständigung für die angegebenen Felder zu ermöglichen.

// Create hotels-quickstart index
private static void CreateIndex(string indexName, SearchIndexClient searchIndexClient)
{
    FieldBuilder fieldBuilder = new FieldBuilder();
    var searchFields = fieldBuilder.Build(typeof(Hotel));

    var definition = new SearchIndex(indexName, searchFields);

    var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
    definition.Suggesters.Add(suggester);

    searchIndexClient.CreateOrUpdateIndex(definition);
}

Laden von Dokumenten

Azure KI Search durchsucht im Dienst gespeicherte Inhalte. In diesem Schritt laden Sie JSON-Dokumente, die dem erstellten Hotelindex entsprechen.

Bei Azure AI Search sind die Suchdokumente Datenstrukturen, die sowohl Eingaben für die Indizierung als auch Ausgaben von Abfragen sind. Beispiele für Dokumenteingaben aus einer externen Datenquelle wären etwa Zeilen in einer Datenbank, Blobs in Blob Storage oder JSON-Dokumente auf dem Datenträger. In diesem Beispiel nehmen wir eine Abkürzung und betten JSON-Dokumente für vier Hotels direkt in den Code ein.

Beim Hochladen von Dokumenten muss ein IndexDocumentsBatch-Objekt verwendet werden. Ein IndexDocumentsBatch-Objekt enthält eine Sammlung von Aktionen, und jede dieser Aktionen enthält wiederum ein Dokument und eine Eigenschaft, die Azure AI Search mitteilt, welche Aktion ausgeführt werden soll („upload“, „merge“, „delete“ oder „mergeOrUpload“).

In Program.cs erstellen Sie ein Array mit Dokumenten und Indexaktionen und übergeben es anschließend an IndexDocumentsBatch. Die folgenden Dokumente entsprechen dem Index „hotels-quickstart“ (gemäß Definition durch die Klasse „hotel“).

// Upload documents in a single Upload request.
private static void UploadDocuments(SearchClient searchClient)
{
    IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
        IndexDocumentsAction.Upload(
            new Hotel()
            {
                HotelId = "1",
                HotelName = "Stay-Kay City Hotel",
                Description = "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                Category = "Boutique",
                Tags = new[] { "view", "air conditioning", "concierge" },
                ParkingIncluded = false,
                LastRenovationDate = new DateTimeOffset(2022, 1, 18, 0, 0, 0, TimeSpan.Zero),
                Rating = 3.6,
                Address = new Address()
                {
                    StreetAddress = "677 5th Ave",
                    City = "New York",
                    StateProvince = "NY",
                    PostalCode = "10022",
                    Country = "USA"
                }
            }),
        // REDACTED FOR BREVITY
}

Nachdem Sie das IndexDocumentsBatch-Objekt initialisiert haben, können Sie es an den Index senden, indem Sie IndexDocuments für Ihr SearchClient-Objekt aufrufen.

Sie laden Dokumente mithilfe von „SearchClient“ in Main(), aber für den Vorgang werden auch Administratorrechte für den Dienst benötigt, der normalerweise „SearchIndexClient“ zugeordnet ist. Eine Möglichkeit zum Einrichten dieses Vorgangs ist das Abrufen von „SearchClient“ über SearchIndexClient (in diesem Beispiel searchIndexClient).

SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);

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

Da es sich hierbei um eine Konsolen-App handelt, die alle Befehle sequenziell ausführt, fügen wir eine Wartezeit von 2 Sekunden zwischen der Indizierung und Abfragen hinzu.

// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
Console.WriteLine("Waiting for indexing...\n");
System.Threading.Thread.Sleep(2000);

Die Verzögerung von zwei Sekunden dient zur Kompensierung der Indizierung. Diese erfolgt asynchron, sodass alle Dokumente vor der Ausführung der Abfragen indiziert werden können. Die Programmierung einer solchen Verzögerung ist in der Regel nur in Demos, bei Tests und in Beispielanwendungen erforderlich.

Durchsuchen eines Index

Abfrageergebnisse können abgerufen werden, sobald das erste Dokument indiziert wurde. Mit dem Testen des Index sollte aber gewartet werden, bis alle Dokumente indiziert wurden.

In diesem Abschnitt werden zwei Funktionen hinzugefügt: Abfragelogik und Ergebnisse. Verwenden Sie für Abfragen die Search-Methode. Diese Methode akzeptiert Suchtext (die Abfragezeichenfolge) und andere Optionen.

Die SearchResults-Klasse stellt die Ergebnisse dar.

In Program.cs gibt die WriteDocuments-Methode Suchergebnisse in der Konsole aus.

// Write search results to console
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
    foreach (SearchResult<Hotel> result in searchResults.GetResults())
    {
        Console.WriteLine(result.Document);
    }

    Console.WriteLine();
}

private static void WriteDocuments(AutocompleteResults autoResults)
{
    foreach (AutocompleteItem result in autoResults.Results)
    {
        Console.WriteLine(result.Text);
    }

    Console.WriteLine();
}

Abfragebeispiel 1

Die RunQueries-Methode führt Abfragen aus und gibt Ergebnisse zurück. Bei den Ergebnissen handelt es sich um Objekte vom Typ „Hotel“. In diesem Beispiel werden die Methodensignatur und die erste Abfrage veranschaulicht. Mit dieser Abfrage wird der Select-Parameter veranschaulicht, mit dem Sie das Ergebnis erstellen können, indem Sie ausgewählte Felder aus dem Dokument verwenden.

// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient searchClient)
{
    SearchOptions options;
    SearchResults<Hotel> response;
    
    // Query 1
    Console.WriteLine("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");

    options = new SearchOptions()
    {
        IncludeTotalCount = true,
        Filter = "",
        OrderBy = { "" }
    };

    options.Select.Add("HotelId");
    options.Select.Add("HotelName");
    options.Select.Add("Address/City");

    response = searchClient.Search<Hotel>("*", options);
    WriteDocuments(response);
    // REDACTED FOR BREVITY
}

Abfragebeispiel 2

Suchen Sie in der zweiten Abfrage nach einem Begriff, und fügen Sie einen Filter hinzu, mit dem Dokumente mit einer höheren Bewertung als 4 ausgewählt werden. Sortieren Sie dann nach Bewertung in absteigender Reihenfolge. Ein Filter ist ein boolescher Ausdruck, der für Felder vom Typ IsFilterable in einem Index ausgewertet wird. Filterabfragen schließen Werte ein oder aus. Daher ist einer Filterabfrage keine Relevanzbewertung zugeordnet.

// Query 2
Console.WriteLine("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");

options = new SearchOptions()
{
    Filter = "Rating gt 4",
    OrderBy = { "Rating desc" }
};

options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Rating");

response = searchClient.Search<Hotel>("hotels", options);
WriteDocuments(response);

Abfragebeispiel 3

Mit der dritten Abfrage wird searchFields veranschaulicht, um einen Volltextsuche-Vorgang auf bestimmte Felder festzulegen.

// Query 3
Console.WriteLine("Query #3: Limit search to specific fields (pool in Tags field)...\n");

options = new SearchOptions()
{
    SearchFields = { "Tags" }
};

options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Tags");

response = searchClient.Search<Hotel>("pool", options);
WriteDocuments(response);

Abfragebeispiel 4

Mit der vierten Abfrage werden facets veranschaulicht, die zum Strukturieren einer Facettennavigationsstruktur verwendet werden können.

// Query 4
Console.WriteLine("Query #4: Facet on 'Category'...\n");

options = new SearchOptions()
{
    Filter = ""
};

options.Facets.Add("Category");

options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Category");

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

Abfragebeispiel 5

Bei der fünften Abfrage wird ein bestimmtes Dokument zurückgegeben. Eine Dokumentsuche ist eine typische Antwort auf das OnClick-Ereignis in einem Resultset.

// Query 5
Console.WriteLine("Query #5: Look up a specific document...\n");

Response<Hotel> lookupResponse;
lookupResponse = searchClient.GetDocument<Hotel>("3");

Console.WriteLine(lookupResponse.Value.HotelId);

Abfragebeispiel 6

Mit der letzten Abfrage wird die Syntax für die automatische Vervollständigung veranschaulicht. Hierbei wird die Teileingabe sa eines Benutzers simuliert, für die sich zwei mögliche Übereinstimmungen in den „sourceFields“ ergeben, die der im Index definierten Vorschlagsfunktion zugeordnet sind.

// Query 6
Console.WriteLine("Query #6: Call Autocomplete on HotelName that starts with 'sa'...\n");

var autoresponse = searchClient.Autocomplete("sa", "sg");
WriteDocuments(autoresponse);

Zusammenfassung der Abfragen

Die obigen Abfragen verdeutlichen mehrere Möglichkeiten zum Abgleichen von Begriffen in einer Abfrage: Volltextsuche, Filter und AutoVervollständigen.

Für die Volltextsuche und Filter wird die SearchClient.Search-Methode verwendet. Eine Suchabfrage kann in der searchText-Zeichenfolge und ein Filterausdruck kann in der Filter-Eigenschaft der SearchOptions-Klasse übergeben werden. Um ohne Suche zu filtern, übergeben Sie einfach "*" für den Parameter searchText der Search-Methode. Wenn Sie ohne Filter suchen möchten, legen Sie die Eigenschaft Filter nicht fest, oder übergeben Sie einfach keine SearchOptions-Instanz.

In dieser Schnellstartanleitung verwenden Sie die Azure.Search.Documents-Clientbibliothek, um einen Suchindex mit Beispieldaten für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Voraussetzungen für Microsoft Entra ID

Für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra-ID müssen Sie:

Abrufen von Ressourceninformationen

Sie müssen die folgenden Informationen abrufen, um Ihre Anwendung bei Ihrem Azure KI-Suche-Dienst zu authentifizieren:

Variablenname Wert
SEARCH_API_ENDPOINT Diesen Wert finden Sie im Azure-Portal. Wählen Sie Ihren Suchdienst und dann im linken Menü die Option Übersicht aus. Der URL-Wert unter Essentials ist der Endpunkt, den Sie benötigen. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

Erfahren Sie mehr über schlüssellose Authentifizierung und das Festlegen von Umgebungsvariablen.

Einrichten

Das Beispiel in diesem Schnellstart funktioniert mit der Java-Runtime. Installieren Sie ein Java Development Kit wie z. B. Azul Zulu OpenJDK. Der Microsoft-Build von OpenJDK oder Ihr bevorzugtes JDK sollte ebenfalls funktionieren.

  1. Installieren Sie Apache Maven. Führen Sie dann mvn -v aus, um die erfolgreiche Installation zu bestätigen.

  2. Erstellen Sie im Stammverzeichnis Ihres Projekts eine neue Datei pom.xml, und kopieren Sie folgende Code in diese Datei:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>azure.search.sample</groupId>
        <artifactId>azuresearchquickstart</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <build>
            <sourceDirectory>src</sourceDirectory>
            <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                <source>1.8</source>
                <target>1.8</target>
                </configuration>
            </plugin>
            </plugins>
        </build>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-search-documents</artifactId>
                <version>11.7.3</version>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-core</artifactId>
                <version>1.53.0</version>
            </dependency>
            <dependency>
                <groupId>com.azure</groupId>
                <artifactId>azure-identity</artifactId>
                <version>1.15.1</version>
            </dependency>
        </dependencies>
    </project>
    
  3. Installieren Sie die Abhängigkeiten, einschließlich der Clientbibliothek von Azure KI-Suche (Azure.Search.Documents) für Java und der Azure Identity-Clientbibliothek für Java mit:

    mvn clean dependency:copy-dependencies
    
  4. Melden Sie sich für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra ID mit dem folgenden Befehl bei Azure an:

    az login
    

Erstellen, Laden und Abfragen eines Suchindexes

Im vorherigen Abschnitt Einrichten haben Sie die Clientbibliothek von Azure KI-Suche und andere Abhängigkeiten installiert.

In diesem Abschnitt fügen Sie Code hinzu, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie führen das Programm aus, um die Ergebnisse in der Konsole anzuzeigen. Eine ausführliche Erläuterung des Codes finden Sie im Abschnitt Erläuterung des Codes.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Erstellen Sie eine neue Datei mit dem Namen App.java, und fügen Sie den folgenden Code in App.java ein:

    import java.util.Arrays;
    import java.util.ArrayList;
    import java.time.OffsetDateTime;
    import java.time.ZoneOffset;
    import java.time.LocalDateTime;
    import java.time.LocalDate;
    import java.time.LocalTime;
    import com.azure.core.util.Configuration;
    import com.azure.core.util.Context;
    import com.azure.identity.DefaultAzureCredential;
    import com.azure.identity.DefaultAzureCredentialBuilder;
    import com.azure.search.documents.SearchClient;
    import com.azure.search.documents.SearchClientBuilder;
    import com.azure.search.documents.indexes.SearchIndexClient;
    import com.azure.search.documents.indexes.SearchIndexClientBuilder;
    import com.azure.search.documents.indexes.models.IndexDocumentsBatch;
    import com.azure.search.documents.models.SearchOptions;
    import com.azure.search.documents.indexes.models.SearchIndex;
    import com.azure.search.documents.indexes.models.SearchSuggester;
    import com.azure.search.documents.util.AutocompletePagedIterable;
    import com.azure.search.documents.util.SearchPagedIterable;
    
    public class App {
    
        public static void main(String[] args) {
            // Your search service endpoint
            "https://<Put your search service NAME here>.search.windows.net/";
    
            // Use the recommended keyless credential instead of the AzureKeyCredential credential.
            DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
            //AzureKeyCredential credential = new AzureKeyCredential("<Your search service admin key>");
    
            // Create a SearchIndexClient to send create/delete index commands
            SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
                .endpoint(searchServiceEndpoint)
                .credential(credential)
                .buildClient();
    
            // Create a SearchClient to load and query documents
            String indexName = "hotels-quickstart-java";
            SearchClient searchClient = new SearchClientBuilder()
                .endpoint(searchServiceEndpoint)
                .credential(credential)
                .indexName(indexName)
                .buildClient();
    
            // Create Search Index for Hotel model
            searchIndexClient.createOrUpdateIndex(
                new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
                .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));
    
            // Upload sample hotel documents to the Search Index
            uploadDocuments(searchClient);
    
            // Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
            System.out.println("Waiting for indexing...\n");
            try
            {
                Thread.sleep(2000);
            }
            catch (InterruptedException e)
            {
            }
    
            // Call the RunQueries method to invoke a series of queries
            System.out.println("Starting queries...\n");
            RunQueries(searchClient);
    
            // End the program
            System.out.println("Complete.\n");
        }
    
        // Upload documents in a single Upload request.
        private static void uploadDocuments(SearchClient searchClient)
        {
            var hotelList = new ArrayList<Hotel>();
    
            var hotel = new Hotel();
            hotel.hotelId = "1";
            hotel.hotelName = "Stay-Kay City Hotel";
            hotel.description = "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "view", "air conditioning", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2022, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.6;
            hotel.address = new Address();
            hotel.address.streetAddress = "677 5th Ave";
            hotel.address.city = "New York";
            hotel.address.stateProvince = "NY";
            hotel.address.postalCode = "10022";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "2";
            hotel.hotelName = "Old Century Hotel";
            hotel.description = "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "free wifi", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2019, 2, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "140 University Town Center Dr";
            hotel.address.city = "Sarasota";
            hotel.address.stateProvince = "FL";
            hotel.address.postalCode = "34243";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "3";
            hotel.hotelName = "Gastronomic Landscape Hotel";
            hotel.description = "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.";
            hotel.category = "Suite";
            hotel.tags = new String[] { "restaurant", "bar", "continental breakfast" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2015, 9, 20), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.80;
            hotel.address = new Address();
            hotel.address.streetAddress = "3393 Peachtree Rd";
            hotel.address.city = "Atlanta";
            hotel.address.stateProvince = "GA";
            hotel.address.postalCode = "30326";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "4";
            hotel.hotelName = "Sublime Palace Hotel";
            hotel.description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "concierge", "view", "air conditioning" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2020, 2, 06), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "7400 San Pedro Ave";
            hotel.address.city = "San Antonio";
            hotel.address.stateProvince = "TX";
            hotel.address.postalCode = "78216";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            var batch = new IndexDocumentsBatch<Hotel>();
            batch.addMergeOrUploadActions(hotelList);
            try
            {
                searchClient.indexDocuments(batch);
            }
            catch (Exception e)
            {
                e.printStackTrace();
                // If for some reason any documents are dropped during indexing, you can compensate by delaying and
                // retrying. This simple demo just logs failure and continues
                System.err.println("Failed to index some of the documents");
            }
        }
    
        // Write search results to console
        private static void WriteSearchResults(SearchPagedIterable searchResults)
        {
            searchResults.iterator().forEachRemaining(result ->
            {
                Hotel hotel = result.getDocument(Hotel.class);
                System.out.println(hotel);
            });
    
            System.out.println();
        }
    
        // Write autocomplete results to console
        private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
        {
            autocompleteResults.iterator().forEachRemaining(result ->
            {
                String text = result.getText();
                System.out.println(text);
            });
    
            System.out.println();
        }
    
        // Run queries, use WriteDocuments to print output
        private static void RunQueries(SearchClient searchClient)
        {
            // Query 1
            System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
            SearchOptions options = new SearchOptions();
            options.setIncludeTotalCount(true);
            options.setFilter("");
            options.setOrderBy("");
            options.setSelect("HotelId", "HotelName", "Address/City");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 2
            System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
            options = new SearchOptions();
            options.setFilter("Rating gt 4");
            options.setOrderBy("Rating desc");
            options.setSelect("HotelId", "HotelName", "Rating");
    
            WriteSearchResults(searchClient.search("hotels", options, Context.NONE));
    
            // Query 3
            System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
            options = new SearchOptions();
            options.setSearchFields("Tags");
    
            options.setSelect("HotelId", "HotelName", "Tags");
    
            WriteSearchResults(searchClient.search("pool", options, Context.NONE));
    
            // Query 4
            System.out.println("Query #4: Facet on 'Category'...\n");
    
            options = new SearchOptions();
            options.setFilter("");
            options.setFacets("Category");
            options.setSelect("HotelId", "HotelName", "Category");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 5
            System.out.println("Query #5: Look up a specific document...\n");
    
            Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
            System.out.println(lookupResponse.hotelId);
            System.out.println();
    
             // Query 6
            System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
    
            WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
        }
    }
    
  2. Erstellen Sie eine neue Datei mit dem Namen Hotel.java, und fügen Sie den folgenden Code in Hotel.java ein:

    import com.azure.search.documents.indexes.SearchableField;
    import com.azure.search.documents.indexes.SimpleField;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    
    import java.time.OffsetDateTime;
    
    /**
     * Model class representing a hotel.
     */
    @JsonInclude(Include.NON_NULL)
    public class Hotel {
        /**
         * Hotel ID
         */
        @JsonProperty("HotelId")
        @SimpleField(isKey = true)
        public String hotelId;
    
        /**
         * Hotel name
         */
        @JsonProperty("HotelName")
        @SearchableField(isSortable = true)
        public String hotelName;
    
        /**
         * Description
         */
        @JsonProperty("Description")
        @SearchableField(analyzerName = "en.microsoft")
        public String description;
    
        /**
         * Category
         */
        @JsonProperty("Category")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String category;
    
        /**
         * Tags
         */
        @JsonProperty("Tags")
        @SearchableField(isFilterable = true, isFacetable = true)
        public String[] tags;
    
        /**
         * Whether parking is included
         */
        @JsonProperty("ParkingIncluded")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public Boolean parkingIncluded;
    
        /**
         * Last renovation time
         */
        @JsonProperty("LastRenovationDate")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public OffsetDateTime lastRenovationDate;
    
        /**
         * Rating
         */
        @JsonProperty("Rating")
        @SimpleField(isFilterable = true, isSortable = true, isFacetable = true)
        public Double rating;
    
        /**
         * Address
         */
        @JsonProperty("Address")
        public Address address;
    
        @Override
        public String toString()
        {
            try
            {
                return new ObjectMapper().writeValueAsString(this);
            }
            catch (JsonProcessingException e)
            {
                e.printStackTrace();
                return "";
            }
        }
    }
    
  3. Erstellen Sie eine neue Datei mit dem Namen Address.java, und fügen Sie den folgenden Code in Address.java ein:

    import com.azure.search.documents.indexes.SearchableField;
    import com.fasterxml.jackson.annotation.JsonInclude;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    
    /**
     * Model class representing an address.
     */
    @JsonInclude(Include.NON_NULL)
    public class Address {
        /**
         * Street address
         */
        @JsonProperty("StreetAddress")
        @SearchableField
        public String streetAddress;
    
        /**
         * City
         */
        @JsonProperty("City")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String city;
    
        /**
         * State or province
         */
        @JsonProperty("StateProvince")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String stateProvince;
    
        /**
         * Postal code
         */
        @JsonProperty("PostalCode")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String postalCode;
    
        /**
         * Country
         */
        @JsonProperty("Country")
        @SearchableField(isFilterable = true, isSortable = true, isFacetable = true)
        public String country;
    }
    
  4. Führen Sie Ihre neue Konsolenanwendung aus:

    javac Address.java App.java Hotel.java -cp ".;target\dependency\*"
    java -cp ".;target\dependency\*" App
    

Erläutern des Codes

In den vorherigen Abschnitten haben Sie eine neue Konsolenanwendung erstellt und die Clientbibliothek von Azure KI-Suche installiert. Sie haben Code hinzugefügt, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie haben das Programm ausgeführt, um die Ergebnisse in der Konsole anzuzeigen.

In diesem Abschnitt erläutern wir den Code, den Sie der Konsolenanwendung hinzugefügt haben.

Erstellen eines Suchclients

In App.java haben Sie zwei Clients erstellt:

  • SearchIndexClient erstellt den Index.
  • SearchClient lädt und fragt einen vorhandenen Index ab.

Beide Clients benötigen den Suchdienst-Endpunkt und die Anmeldeinformationen, die weiter oben im Abschnitt „Ressourceninformationen“ beschrieben wurden.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
public static void main(String[] args) {
    // Your search service endpoint
    String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";

    // Use the recommended keyless credential instead of the AzureKeyCredential credential.
    DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
    //AzureKeyCredential credential = new AzureKeyCredential("Your search service admin key");

    // Create a SearchIndexClient to send create/delete index commands
    SearchIndexClient searchIndexClient = new SearchIndexClientBuilder()
        .endpoint(searchServiceEndpoint)
        .credential(credential)
        .buildClient();
    
    // Create a SearchClient to load and query documents
    String indexName = "hotels-quickstart-java";
    SearchClient searchClient = new SearchClientBuilder()
        .endpoint(searchServiceEndpoint)
        .credential(credential)
        .indexName(indexName)
        .buildClient();

    // Create Search Index for Hotel model
    searchIndexClient.createOrUpdateIndex(
        new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
        .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));

    // REDACTED FOR BREVITY . . . 
}

Erstellen eines Index

In dieser Schnellstartanleitung wird ein Hotelindex erstellt, in den Sie Hoteldaten laden und Abfragen dafür ausführen. In diesem Schritt definieren Sie die Felder im Index. Jede Felddefinition enthält einen Namen, einen Datentyp und Attribute, die bestimmen, wie das Feld verwendet wird.

In diesem Beispiel werden aus Gründen der Einfachheit und Lesbarkeit synchrone Methoden der Bibliothek Azure.Search.Documents verwendet. In Produktionsszenarien sollten Sie jedoch asynchrone Methoden verwenden, um Ihre App skalierbar und reaktionsfähig zu halten. So würden Sie beispielsweise CreateIndexAsync anstatt CreateIndex verwenden.

Definieren der Strukturen

Sie haben zwei Hilfsklassen erstellt, Hotel.java und Address.java, um die Struktur eines Hoteldokuments und seiner Adresse zu definieren. Die Klasse „Hotel“ enthält Felder für Hotel-ID, Name, Beschreibung, Kategorie, Tags, Parkplätze, Renovierungsdatum, Bewertung und Adresse. Die Klasse „Address“ enthält Felder für Straße und Hausnummer, Ort, Bundesland/Kanton, Postleitzahl und Land/Region.

In der Azure.Search.Documents-Clientbibliothek können Sie SearchableField und SimpleField verwenden, um Felddefinitionen zu optimieren.

  • SimpleField kann ein beliebiger Datentyp sein, ist nie durchsuchbar (wird bei Volltextsuchabfragen ignoriert) und ist abrufbar (nicht ausgeblendet). Andere Attribute sind standardmäßig deaktiviert, können jedoch aktiviert werden. Sie können ein SimpleField-Objekt 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.
  • SearchableField muss eine Zeichenfolge sein und ist immer „durchsuchbar“ und „abrufbar“. 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.

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 vorherigen Beispiel gezeigt.

Erstellen des Suchindex

In App.java erstellen Sie ein SearchIndex-Objekt in der main-Methode und rufen dann die createOrUpdateIndex-Methode auf, um den Index in Ihrem Suchdienst zu erstellen. Der Index enthält auch ein SearchSuggester-Element, um die automatische Vervollständigung für die angegebenen Felder zu ermöglichen.

// Create Search Index for Hotel model
searchIndexClient.createOrUpdateIndex(
    new SearchIndex(indexName, SearchIndexClient.buildSearchFields(Hotel.class, null))
    .setSuggesters(new SearchSuggester("sg", Arrays.asList("HotelName"))));

Laden von Dokumenten

Azure KI Search durchsucht im Dienst gespeicherte Inhalte. In diesem Schritt laden Sie JSON-Dokumente, die dem erstellten Hotelindex entsprechen.

Bei Azure AI Search sind die Suchdokumente Datenstrukturen, die sowohl Eingaben für die Indizierung als auch Ausgaben von Abfragen sind. Beispiele für Dokumenteingaben aus einer externen Datenquelle wären etwa Zeilen in einer Datenbank, Blobs in Blob Storage oder JSON-Dokumente auf dem Datenträger. In diesem Beispiel nehmen wir eine Abkürzung und betten JSON-Dokumente für vier Hotels direkt in den Code ein.

Beim Hochladen von Dokumenten muss ein IndexDocumentsBatch-Objekt verwendet werden. Ein IndexDocumentsBatch-Objekt enthält eine Sammlung von IndexActions, und jede dieser Aktionen enthält wiederum ein Dokument und eine Eigenschaft, die Azure AI Search mitteilt, welche Aktion ausgeführt werden soll („upload“, „merge“, „delete“ oder „mergeOrUpload“).

In App.java erstellen Dokumente und Indexaktionen und übergeben diese dann an IndexDocumentsBatch. Die folgenden Dokumente entsprechen dem Index „hotels-quickstart“ (gemäß Definition durch die Klasse „hotel“).

private static void uploadDocuments(SearchClient searchClient)
{
    var hotelList = new ArrayList<Hotel>();

    var hotel = new Hotel();
    hotel.hotelId = "1";
    hotel.hotelName = "Stay-Kay City Hotel";
    hotel.description = "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
    hotel.category = "Boutique";
    hotel.tags = new String[] { "view", "air conditioning", "concierge" };
    hotel.parkingIncluded = false;
    hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2022, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
    hotel.rating = 3.6;
    hotel.address = new Address();
    hotel.address.streetAddress = "677 5th Ave";
    hotel.address.city = "New York";
    hotel.address.stateProvince = "NY";
    hotel.address.postalCode = "10022";
    hotel.address.country = "USA";
    hotelList.add(hotel);
    
    // REDACTED FOR BREVITY

    var batch = new IndexDocumentsBatch<Hotel>();
    batch.addMergeOrUploadActions(hotelList);
    try
    {
        searchClient.indexDocuments(batch);
    }
    catch (Exception e)
    {
        e.printStackTrace();
        // If for some reason any documents are dropped during indexing, you can compensate by delaying and
        // retrying. This simple demo just logs failure and continues
        System.err.println("Failed to index some of the documents");
    }
}

Nachdem Sie das Objekt IndexDocumentsBatch initialisiert haben, können Sie es an den Index senden, indem Sie indexDocuments für Ihr SearchClient-Objekt aufrufen.

Sie laden Dokumente mithilfe von „SearchClient“ in main(), aber für den Vorgang werden auch Administratorrechte für den Dienst benötigt, der normalerweise „SearchIndexClient“ zugeordnet ist. Eine Möglichkeit zum Einrichten dieses Vorgangs ist das Abrufen von „SearchClient“ über SearchIndexClient (in diesem Beispiel searchIndexClient).

uploadDocuments(searchClient);

Da es sich hierbei um eine Konsolen-App handelt, die alle Befehle sequenziell ausführt, fügen wir eine Wartezeit von 2 Sekunden zwischen der Indizierung und Abfragen hinzu.

// Wait 2 seconds for indexing to complete before starting queries (for demo and console-app purposes only)
System.out.println("Waiting for indexing...\n");
try
{
    Thread.sleep(2000);
}
catch (InterruptedException e)
{
}

Die Verzögerung von zwei Sekunden dient zur Kompensierung der Indizierung. Diese erfolgt asynchron, sodass alle Dokumente vor der Ausführung der Abfragen indiziert werden können. Die Programmierung einer solchen Verzögerung ist in der Regel nur in Demos, bei Tests und in Beispielanwendungen erforderlich.

Durchsuchen eines Index

Abfrageergebnisse können abgerufen werden, sobald das erste Dokument indiziert wurde. Mit dem Testen des Index sollte aber gewartet werden, bis alle Dokumente indiziert wurden.

In diesem Abschnitt werden zwei Funktionen hinzugefügt: Abfragelogik und Ergebnisse. Verwenden Sie für Abfragen die Search-Methode. Diese Methode akzeptiert Suchtext (die Abfragezeichenfolge) und andere Optionen.

In App.java gibt die WriteDocuments-Methode Suchergebnisse in der Konsole aus.

// Write search results to console
private static void WriteSearchResults(SearchPagedIterable searchResults)
{
    searchResults.iterator().forEachRemaining(result ->
    {
        Hotel hotel = result.getDocument(Hotel.class);
        System.out.println(hotel);
    });

    System.out.println();
}

// Write autocomplete results to console
private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
{
    autocompleteResults.iterator().forEachRemaining(result ->
    {
        String text = result.getText();
        System.out.println(text);
    });

    System.out.println();
}

Abfragebeispiel 1

Die RunQueries-Methode führt Abfragen aus und gibt Ergebnisse zurück. Bei den Ergebnissen handelt es sich um Objekte vom Typ „Hotel“. In diesem Beispiel werden die Methodensignatur und die erste Abfrage veranschaulicht. Mit dieser Abfrage wird der Select-Parameter veranschaulicht, mit dem Sie das Ergebnis erstellen können, indem Sie ausgewählte Felder aus dem Dokument verwenden.

// Run queries, use WriteDocuments to print output
private static void RunQueries(SearchClient searchClient)
{
    // Query 1
    System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");

    SearchOptions options = new SearchOptions();
    options.setIncludeTotalCount(true);
    options.setFilter("");
    options.setOrderBy("");
    options.setSelect("HotelId", "HotelName", "Address/City");

    WriteSearchResults(searchClient.search("*", options, Context.NONE));
}

Abfragebeispiel 2

Suchen Sie in der zweiten Abfrage nach einem Begriff, und fügen Sie einen Filter hinzu, mit dem Dokumente mit einer höheren Bewertung als 4 ausgewählt werden. Sortieren Sie dann nach Bewertung in absteigender Reihenfolge. Ein Filter ist ein boolescher Ausdruck, der für Felder vom Typ isFilterable in einem Index ausgewertet wird. Filterabfragen schließen Werte ein oder aus. Daher ist einer Filterabfrage keine Relevanzbewertung zugeordnet.

// Query 2
System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");

options = new SearchOptions();
options.setFilter("Rating gt 4");
options.setOrderBy("Rating desc");
options.setSelect("HotelId", "HotelName", "Rating");

WriteSearchResults(searchClient.search("hotels", options, Context.NONE));

Abfragebeispiel 3

Mit der dritten Abfrage wird searchFields veranschaulicht, um einen Volltextsuche-Vorgang auf bestimmte Felder festzulegen.

// Query 3
System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");

options = new SearchOptions();
options.setSearchFields("Tags");

options.setSelect("HotelId", "HotelName", "Tags");

WriteSearchResults(searchClient.search("pool", options, Context.NONE));

Abfragebeispiel 4

Mit der vierten Abfrage werden facets veranschaulicht, die zum Strukturieren einer Facettennavigationsstruktur verwendet werden können.

// Query 4
System.out.println("Query #4: Facet on 'Category'...\n");

options = new SearchOptions();
options.setFilter("");
options.setFacets("Category");
options.setSelect("HotelId", "HotelName", "Category");

WriteSearchResults(searchClient.search("*", options, Context.NONE));

Abfragebeispiel 5

Bei der fünften Abfrage wird ein bestimmtes Dokument zurückgegeben.

// Query 5
System.out.println("Query #5: Look up a specific document...\n");

Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
System.out.println(lookupResponse.hotelId);
System.out.println();

Abfragebeispiel 6

Mit der letzten Abfrage wird die Syntax für die automatische Vervollständigung veranschaulicht. Hierbei wird die Teileingabe s eines Benutzers simuliert, für die sich zwei mögliche Übereinstimmungen in den sourceFields ergeben, die der im Index definierten Vorschlagsfunktion zugeordnet sind.

// Query 6
System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");

WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));

Zusammenfassung der Abfragen

Die obigen Abfragen verdeutlichen mehrere Möglichkeiten zum Abgleichen von Begriffen in einer Abfrage: Volltextsuche, Filter und Auto-Vervollständigen.

Für die Volltextsuche und Filter wird die SearchClient.search-Methode verwendet. Eine Suchabfrage kann in der searchText-Zeichenfolge und ein Filterausdruck kann in der filter-Eigenschaft der SearchOptions-Klasse übergeben werden. Um ohne Suche zu filtern, übergeben Sie einfach „*“ für den Parameter searchText der search-Methode. Wenn Sie ohne Filter suchen möchten, legen Sie die Eigenschaft filter nicht fest, oder übergeben Sie einfach keine SearchOptions-Instanz.

In dieser Schnellstartanleitung verwenden Sie die Azure.Search.Documents-Clientbibliothek, um einen Suchindex mit Beispieldaten für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Voraussetzungen für Microsoft Entra ID

Für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra-ID müssen Sie:

Abrufen von Ressourceninformationen

Sie müssen die folgenden Informationen abrufen, um Ihre Anwendung bei Ihrem Azure KI-Suche-Dienst zu authentifizieren:

Variablenname Wert
SEARCH_API_ENDPOINT Diesen Wert finden Sie im Azure-Portal. Wählen Sie Ihren Suchdienst und dann im linken Menü die Option Übersicht aus. Der URL-Wert unter Essentials ist der Endpunkt, den Sie benötigen. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

Erfahren Sie mehr über schlüssellose Authentifizierung und das Festlegen von Umgebungsvariablen.

Einrichten

  1. Erstellen Sie einen neuen Ordner full-text-quickstart für die Anwendung, und öffnen Sie Visual Studio Code in diesem Ordner mit dem folgenden Befehl:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Erstellen Sie package.json mit dem folgenden Befehl:

    npm init -y
    
  3. Installieren Sie die Clientbibliothek von Azure KI-Suche (Azure.Search.Documents) für JavaScript mit:

    npm install @azure/search-documents
    
  4. Installieren Sie für die empfohlene kennwortlose Authentifizierung die Azure Identity-Clientbibliothek mit:

    npm install @azure/identity
    

Erstellen, Laden und Abfragen eines Suchindexes

Im vorherigen Abschnitt Einrichten haben Sie die Clientbibliothek von Azure KI-Suche und andere Abhängigkeiten installiert.

In diesem Abschnitt fügen Sie Code hinzu, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie führen das Programm aus, um die Ergebnisse in der Konsole anzuzeigen. Eine ausführliche Erläuterung des Codes finden Sie im Abschnitt Erläuterung des Codes.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Erstellen Sie eine neue Datei mit dem Namen index.js, und fügen Sie den folgenden Code in index.js ein:

    // Import from the @azure/search-documents library
    import { SearchIndexClient, odata } from "@azure/search-documents";
    // Import from the Azure Identity library
    import { DefaultAzureCredential } from "@azure/identity";
    // Importing the hotels sample data
    import hotelData from './hotels.json' assert { type: "json" };
    // Load the .env file if it exists
    import * as dotenv from "dotenv";
    dotenv.config();
    // Defining the index definition
    const indexDefinition = {
        "name": "hotels-quickstart",
        "fields": [
            {
                "name": "HotelId",
                "type": "Edm.String",
                "key": true,
                "filterable": true
            },
            {
                "name": "HotelName",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": true,
                "facetable": false
            },
            {
                "name": "Description",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": false,
                "facetable": false,
                "analyzerName": "en.lucene"
            },
            {
                "name": "Description_fr",
                "type": "Edm.String",
                "searchable": true,
                "filterable": false,
                "sortable": false,
                "facetable": false,
                "analyzerName": "fr.lucene"
            },
            {
                "name": "Category",
                "type": "Edm.String",
                "searchable": true,
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Tags",
                "type": "Collection(Edm.String)",
                "searchable": true,
                "filterable": true,
                "sortable": false,
                "facetable": true
            },
            {
                "name": "ParkingIncluded",
                "type": "Edm.Boolean",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "LastRenovationDate",
                "type": "Edm.DateTimeOffset",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Rating",
                "type": "Edm.Double",
                "filterable": true,
                "sortable": true,
                "facetable": true
            },
            {
                "name": "Address",
                "type": "Edm.ComplexType",
                "fields": [
                    {
                        "name": "StreetAddress",
                        "type": "Edm.String",
                        "filterable": false,
                        "sortable": false,
                        "facetable": false,
                        "searchable": true
                    },
                    {
                        "name": "City",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "StateProvince",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "PostalCode",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    },
                    {
                        "name": "Country",
                        "type": "Edm.String",
                        "searchable": true,
                        "filterable": true,
                        "sortable": true,
                        "facetable": true
                    }
                ]
            }
        ],
        "suggesters": [
            {
                "name": "sg",
                "searchMode": "analyzingInfixMatching",
                "sourceFields": [
                    "HotelName"
                ]
            }
        ]
    };
    async function main() {
        // Your search service endpoint
        const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
        // Use the recommended keyless credential instead of the AzureKeyCredential credential.
        const credential = new DefaultAzureCredential();
        //const credential = new AzureKeyCredential(Your search service admin key);
        // Create a SearchIndexClient to send create/delete index commands
        const searchIndexClient = new SearchIndexClient(searchServiceEndpoint, credential);
        // Creating a search client to upload documents and issue queries
        const indexName = "hotels-quickstart";
        const searchClient = searchIndexClient.getSearchClient(indexName);
        console.log('Checking if index exists...');
        await deleteIndexIfExists(searchIndexClient, indexName);
        console.log('Creating index...');
        let index = await searchIndexClient.createIndex(indexDefinition);
        console.log(`Index named ${index.name} has been created.`);
        console.log('Uploading documents...');
        let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
        console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `);
        // waiting one second for indexing to complete (for demo purposes only)
        sleep(1000);
        console.log('Querying the index...');
        console.log();
        await sendQueries(searchClient);
    }
    async function deleteIndexIfExists(searchIndexClient, indexName) {
        try {
            await searchIndexClient.deleteIndex(indexName);
            console.log('Deleting index...');
        }
        catch {
            console.log('Index does not exist yet.');
        }
    }
    async function sendQueries(searchClient) {
        // Query 1
        console.log('Query #1 - search everything:');
        let searchOptions = {
            includeTotalCount: true,
            select: ["HotelId", "HotelName", "Rating"]
        };
        let searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log(`Result count: ${searchResults.count}`);
        console.log();
        // Query 2
        console.log('Query #2 - search with filter, orderBy, and select:');
        let state = 'FL';
        searchOptions = {
            filter: odata `Address/StateProvince eq ${state}`,
            orderBy: ["Rating desc"],
            select: ["HotelId", "HotelName", "Rating"]
        };
        searchResults = await searchClient.search("wifi", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 3
        console.log('Query #3 - limit searchFields:');
        searchOptions = {
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
        searchResults = await searchClient.search("sublime palace", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Erstellen Sie eine Datei mit dem Namen hotels.json, und fügen Sie den folgenden Code in hotels.json ein:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Stay-Kay City Hotel",
                "Description": "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                "Category": "Boutique",
                "Tags": ["view", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "2022-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Old Century Hotel",
                "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
                "Category": "Boutique",
                "Tags": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "2019-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Gastronomic Landscape Hotel",
                "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                "Category": "Suite",
                "Tags": ["restaurant", "bar", "continental breakfast"],
                "ParkingIncluded": "true",
                "LastRenovationDate": "2015-09-20T00:00:00Z",
                "Rating": 4.8,
                "Address": {
                    "StreetAddress": "3393 Peachtree Rd",
                    "City": "Atlanta",
                    "StateProvince": "GA",
                    "PostalCode": "30326"
                }
            },
            {
                "HotelId": "4",
                "HotelName": "Sublime Palace Hotel",
                "Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "air conditioning"],
                "ParkingIncluded": true,
                "LastRenovationDate": "2020-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Erstellen Sie eine Datei mit dem Namen hotels_quickstart_index.json, und fügen Sie den folgenden Code in hotels_quickstart_index.json ein:

    {
    	"name": "hotels-quickstart",
    	"fields": [
    		{
    			"name": "HotelId",
    			"type": "Edm.String",
    			"key": true,
    			"filterable": true
    		},
    		{
    			"name": "HotelName",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": false,
    			"sortable": true,
    			"facetable": false
    		},
    		{
    			"name": "Description",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "en.lucene"
    		},
    		{
    			"name": "Category",
    			"type": "Edm.String",
    			"searchable": true,
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Tags",
    			"type": "Collection(Edm.String)",
    			"searchable": true,
    			"filterable": true,
    			"sortable": false,
    			"facetable": true
    		},
    		{
    			"name": "ParkingIncluded",
    			"type": "Edm.Boolean",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "LastRenovationDate",
    			"type": "Edm.DateTimeOffset",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Rating",
    			"type": "Edm.Double",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Address",
    			"type": "Edm.ComplexType",
    			"fields": [
    				{
    					"name": "StreetAddress",
    					"type": "Edm.String",
    					"filterable": false,
    					"sortable": false,
    					"facetable": false,
    					"searchable": true
    				},
    				{
    					"name": "City",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "StateProvince",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "PostalCode",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "Country",
    					"type": "Edm.String",
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				}
    			]
    		}
    	],
    	"suggesters": [
    		{
    			"name": "sg",
    			"searchMode": "analyzingInfixMatching",
    			"sourceFields": [
    				"HotelName"
    			]
    		}
    	]
    }
    
  4. Melden Sie sich mithilfe des folgenden Befehls bei Azure an:

    az login
    
  5. Führen Sie den JavaScript-Code mit dem folgenden Befehl aus:

    node index.js
    

Erläutern des Codes

Erstellen eines Index

Die Datei hotels_quickstart_index.json definiert, wie Azure KI-Suche mit den Dokumenten verfährt, die Sie im nächsten Schritt laden. Jedes Feld wird durch einen name identifiziert und hat einen bestimmten type. Jedes Feld verfügt zudem über eine Reihe von Indexattributen, die angeben, ob Azure AI Search das Feld durchsuchen, filtern, sortieren und facettieren kann. Bei den meisten Feldern handelt es sich um einfache Datentypen, einige wie z.B. AddressType sind aber komplexe Typen, die es Ihnen ermöglichen, umfangreiche Datenstrukturen in Ihrem Index zu erstellen. Informieren Sie sich weiter über die unterstützten Datentypen und die Indexattribute, die unter Erstellen eines Index (REST) beschrieben sind.

Wenn die Indexdefinition eingerichtet ist, importieren Sie hotels_quickstart_index.json am Anfang von index.js, damit die main-Funktion auf die Indexdefinition zugreifen kann.

const indexDefinition = require('./hotels_quickstart_index.json');

In der main-Funktion erstellen Sie ein SearchIndexClient-Objekt, das zum Erstellen und Verwalten von Indizes für Azure AI Search verwendet wird.

const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));

Als nächstes löschen Sie den Index, falls er bereits existiert. Dies ist eine gängige Vorgehensweise für Test-/Democode.

Dazu wird eine einfache Funktion definiert, die versucht, den Index zu löschen.

async function deleteIndexIfExists(indexClient, indexName) {
    try {
        await indexClient.deleteIndex(indexName);
        console.log('Deleting index...');
    } catch {
        console.log('Index does not exist yet.');
    }
}

Um die Funktion auszuführen, wird der Indexname aus der Indexdefinition extrahiert und das indexName-Objekt zusammen mit dem indexClient-Objekt an die deleteIndexIfExists()-Funktion übergeben.

const indexName = indexDefinition["name"];

console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);

Danach sind Sie bereit, den Index mit der createIndex()-Methode zu erstellen.

console.log('Creating index...');
let index = await indexClient.createIndex(indexDefinition);

console.log(`Index named ${index.name} has been created.`);

Laden von Dokumenten

In Azure AI Search sind die Dokumente Datenstrukturen, bei denen es sich sowohl um Eingaben für die Indizierung als auch um Ausgaben von Abfragen handeln kann. Sie können diese Daten per Push an den Index senden oder einen Indexer verwenden. In diesem Fall übertragen wir die Dokumente programmgesteuert an den Index.

Dokumenteingaben können Zeilen in einer Datenbank, Blobs in einem Blobspeicher oder JSON-Dokumente auf einem Datenträger sein. Ähnlich wie bei der indexDefinition müssen Sie auch hierbei hotels.json am Anfang von index.js importieren, damit in der main-Funktion auf die Daten zugegriffen werden kann.

const hotelData = require('./hotels.json');

Um Daten in den Suchindex zu indizieren, müssen Sie nun ein SearchClient-Objekt erstellen. Während das SearchIndexClient-Objekt zum Erstellen und Verwalten eines Index verwendet wird, dient das SearchClient-Objekt zum Hochladen von Dokumenten und Abfragen des Index.

Es gibt zwei Möglichkeiten zum Erstellen einer SearchClient: Die erste Option besteht darin, ein SearchClient-Objekt von Grund auf neu zu erstellen:

 const searchClient = new SearchClient(endpoint, indexName, new AzureKeyCredential(apiKey));

Alternativ können Sie die getSearchClient()-Methode des SearchIndexClient-Objekts verwenden, um das SearchClient-Objekt zu erstellen:

const searchClient = indexClient.getSearchClient(indexName);

Nachdem der Client definiert wurde, laden Sie die Dokumente in den Suchindex hoch. In diesem Fall verwenden Sie die mergeOrUploadDocuments()-Methode zum Hochladen der Dokumente, oder Sie führen sie mit einem bestehenden Dokument zusammen, wenn bereits ein Dokument mit demselben Schlüssel existiert.

console.log('Uploading documents...');
let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);

console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);

Durchsuchen eines Index

Nachdem Sie einen Index erstellt und Dokumente hochgeladen haben, können Sie nun Abfragen an den Index senden. In diesem Abschnitt werden fünf verschiedene Abfragen an den Suchindex gesendet, um verschiedene verfügbare Abfragefunktionen zu veranschaulichen.

Die Abfragen werden in einer sendQueries()-Funktion geschrieben, die in der main-Funktion wie folgt aufgerufen wird:

await sendQueries(searchClient);

Abfragen werden mithilfe der search()-Methode von searchClient gesendet. Der erste Parameter ist der Suchtext, und der zweite Parameter gibt die Suchoptionen an.

Abfragebeispiel 1

Die erste Abfrage durchsucht *, was dem Durchsuchen aller Elemente entspricht, und wählt drei der Felder im Index aus. Es ist ein bewährtes Verfahren, nur die Felder per select auszuwählen, die Sie benötigen, da das Pullen unnötiger Daten die Latenz Ihrer Abfragen erhöhen kann.

Beim searchOptions-Objekt für diese Abfrage ist zudem includeTotalCount auf true festgelegt, wodurch die Anzahl der gefundenen Treffer zurückgegeben wird.

async function sendQueries(searchClient) {
    console.log('Query #1 - search everything:');
    let searchOptions = {
        includeTotalCount: true,
        select: ["HotelId", "HotelName", "Rating"]
    };

    let searchResults = await searchClient.search("*", searchOptions);
    for await (const result of searchResults.results) {
        console.log(`${JSON.stringify(result.document)}`);
    }
    console.log(`Result count: ${searchResults.count}`);

    // remaining queries go here
}

Die übrigen unten beschriebenen Abfragen sollten auch der sendQueries()-Funktion hinzugefügt werden. Sie sind hier aus Gründen der Lesbarkeit getrennt.

Abfragebeispiel 2

In der nächsten Abfrage geben Sie den Suchbegriff "wifi" an und fügen zudem einen Filter hinzu, um nur Ergebnisse zurückzugeben, deren Zustand gleich 'FL' ist. Die Ergebnisse werden außerdem nach dem Rating-Objekt des Hotels sortiert.

console.log('Query #2 - Search with filter, orderBy, and select:');
let state = 'FL';
searchOptions = {
    filter: odata`Address/StateProvince eq ${state}`,
    orderBy: ["Rating desc"],
    select: ["HotelId", "HotelName", "Rating"]
};

searchResults = await searchClient.search("wifi", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Abfragebeispiel 3

Anschließend wird die Suche mit dem Parameter searchFields auf ein einzelnes durchsuchbares Feld beschränkt. Dieser Ansatz ist eine großartige Möglichkeit, Ihre Abfrage effizienter zu gestalten, wenn Sie wissen, dass Sie nur an Übereinstimmungen in bestimmten Feldern interessiert sind.

console.log('Query #3 - Limit searchFields:');
searchOptions = {
    select: ["HotelId", "HotelName", "Rating"],
    searchFields: ["HotelName"]
};

searchResults = await searchClient.search("Sublime Palace", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}
console.log();

Abfragebeispiel 4

Eine weitere gängige Option, die in eine Abfrage einbezogen werden kann, ist facets. Mithilfe von Facetten können Sie Filter auf Ihrer Benutzeroberfläche erstellen, durch die Ihre Benutzern leichter erkennen, nach welchen Werten sie filtern können.

console.log('Query #4 - Use facets:');
searchOptions = {
    facets: ["Category"],
    select: ["HotelId", "HotelName", "Rating"],
    searchFields: ["HotelName"]
};

searchResults = await searchClient.search("*", searchOptions);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Abfragebeispiel 5

Die abschließende Abfrage verwendet die getDocument()-Methode des searchClient-Objekts. Auf diese Weise können Sie ein Dokument effizient anhand seines Schlüssels abrufen.

console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument(key='3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)

Zusammenfassung der Abfragen

Die obigen Abfragen verdeutlichen mehrere Möglichkeiten zum Abgleichen von Begriffen in einer Abfrage: Volltextsuche, Filter und Auto-Vervollständigen.

Volltextsuche und Filtervorgänge werden mit der searchClient.search-Methode durchgeführt. Eine Suchanfrage kann in der searchText-Zeichenfolge übergeben werden, während ein Filterausdruck in der filter-Eigenschaft der SearchOptions-Klasse übergeben werden kann. Um ohne Suche zu filtern, übergeben Sie einfach „*“ für den Parameter searchText der search-Methode. Wenn Sie ohne Filter suchen möchten, legen Sie die Eigenschaft filter nicht fest, oder übergeben Sie einfach keine SearchOptions-Instanz.

In dieser Schnellstartanleitung verwenden Sie PowerShell und die Azure AI Search-REST-APIs , um einen Suchindex für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Konfigurieren des Zugriffs

Sie können eine Verbindung mit Ihrem Azure AI Search-Dienst über API-Schlüssel oder Microsoft Entra-ID mit Rollenzuweisungen herstellen. Schlüssel sind für ein Einstieg einfacher, Rollen sind jedoch sicherer.

So konfigurieren Sie den empfohlenen rollenbasierten Zugriff:

  1. Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Suchdienst aus.

  2. Wählen Sie im linken Bereich Einstellungen>Schlüssel aus.

  3. Wählen Sie unter API-Zugriffssteuerung die Option Beide aus.

    Diese Option ermöglicht sowohl die schlüsselbasierte als auch die schlüssellose Authentifizierung. Nachdem Sie Rollen zugewiesen haben, können Sie zu diesem Schritt zurückkehren und die rollenbasierte Zugriffssteuerung auswählen.

  4. Wählen Sie im linken Bereich access control (IAM) aus.

  5. Wählen Sie Hinzufügen>Rollenzuweisung hinzufügen.

  6. Weisen Sie Ihrem Benutzerkonto die Rollen Suchdienst-Teilnehmer und Suchindex-Daten-Teilnehmer zu.

Weitere Informationen finden Sie unter Herstellen einer Verbindung mit Azure KI-Suche mithilfe von Rollen.

Endpunkt abrufen

Im nächsten Abschnitt geben Sie den folgenden Endpunkt an, um eine Verbindung mit Ihrem Azure AI Search-Dienst herzustellen. Bei diesen Schritten wird davon ausgegangen, dass Sie den rollenbasierten Zugriff konfiguriert haben.

So rufen Sie Ihren Dienstendpunkt ab:

  1. Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Suchdienst aus.

  2. Wählen Sie im linken Bereich die Option "Übersicht" aus.

  3. Notieren Sie sich die URL, die https://my-service.search.windows.net ähnlich sein soll.

Bevor Sie REST-API-Aufrufe an Ihren Azure AI Search-Dienst tätigen können, müssen Sie sich authentifizieren und eine Verbindung mit dem Dienst herstellen. Sie führen die folgenden Schritte in PowerShell aus, die die azure CLI-Befehle unterstützt, die in den Schritten 2 und drei verwendet werden.

So stellen Sie eine Verbindung mit Ihrem Suchdienst her:

  1. Öffnen Sie auf Ihrem lokalen System PowerShell.

  2. Anmeldung bei Ihrem Azure-Abonnement Wenn Sie über mehrere Abonnements verfügen, wählen Sie das Abonnement aus, das Ihren Suchdienst enthält.

    az login
    
  3. Erstellen Sie ein $token Objekt zum Speichern Ihres Zugriffstokens.

    $token = az account get-access-token --resource https://search.azure.com/ --query accessToken --output tsv
    
  4. Erstellen Sie ein $headers Objekt zum Speichern Ihres Tokens und Inhaltstyps.

    $headers = @{
    'Authorization' = "Bearer $token"
    'Content-Type' = 'application/json' 
    'Accept' = 'application/json' }
    

    Sie müssen den Header nur einmal pro Sitzung festlegen, aber Sie müssen ihn jeder Anforderung hinzufügen.

  5. Erstellen Sie ein $url Objekt, das auf die Indizesauflistung ihres Suchdiensts ausgerichtet ist. Ersetzen Sie <YOUR-SEARCH-SERVICE> mit dem Wert, den Sie im Get-Endpunkt abgerufen haben.

    $url = "<YOUR-SEARCH-SERVICE>/indexes?api-version=2024-07-01&`$select=name"
    
  6. Führen Sie diese Invoke-RestMethod Aktion aus, um eine GET-Anforderung an Ihren Suchdienst zu senden. Fügen Sie ConvertTo-Json hinzu, um Antworten vom Dienst anzuzeigen.

    Invoke-RestMethod -Uri $url -Headers $headers | ConvertTo-Json
    

    Wenn Ihr Dienst leer ist und keine Indizes enthält, ähnelt die Antwort dem folgenden Beispiel. Andernfalls wird eine JSON-Darstellung der Indexdefinitionen angezeigt.

    {
        "@odata.context":  "https://my-service.search.windows.net/$metadata#indexes",
        "value":  [
    
                  ]
    }
    

Erstellen eines Suchindexes

Bevor Sie Azure AI Search Inhalte hinzufügen, müssen Sie einen Index erstellen, um zu definieren, wie die Inhalte gespeichert und strukturiert werden. Ein Index ähnelt einer Tabelle in einer relationalen Datenbank, ist jedoch speziell für Suchvorgänge wie die Volltextsuche konzipiert.

Führen Sie die folgenden Befehle in derselben PowerShell-Sitzung aus, die Sie im vorherigen Abschnitt gestartet haben.

So erstellen Sie einen Index:

  1. Erstellen Sie ein $body Objekt, um das Indexschema zu definieren.

    $body = @"
    {
        "name": "hotels-quickstart",  
        "fields": [
            {"name": "HotelId", "type": "Edm.String", "key": true, "filterable": true},
            {"name": "HotelName", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": true, "facetable": false},
            {"name": "Description", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzer": "en.lucene"},
            {"name": "Category", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
            {"name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true},
            {"name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true},
            {"name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true},
            {"name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true},
            {"name": "Address", "type": "Edm.ComplexType", 
                "fields": [
                {"name": "StreetAddress", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "searchable": true},
                {"name": "City", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "StateProvince", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "PostalCode", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "Country", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true}
            ]
         }
      ]
    }
    "@
    
  2. Aktualisieren Sie das $url Objekt so, dass es auf den neuen Index ausgerichtet ist. Ersetzen Sie <YOUR-SEARCH-SERVICE> mit dem Wert, den Sie im Get-Endpunkt abgerufen haben.

    $url = "<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart?api-version=2024-07-01"
    
  3. Führen Sie den Befehl aus Invoke-RestMethod , um den Index für Ihren Suchdienst zu erstellen.

    Invoke-RestMethod -Uri $url -Headers $headers -Method Put -Body $body | ConvertTo-Json
    

    Die Antwort sollte die JSON-Darstellung des Indexschemas enthalten.

Informationen zur Anforderung zum Erstellen von Indexen

In dieser Schnellstartanleitung werden Indizes – Erstellen (REST-API) aufgerufen, um einen Suchindex mit dem Namen hotels-quickstart und den physischen Datenstrukturen in Ihrem Suchdienst zu erstellen.

Innerhalb des Indexschemas definiert die fields Sammlung die Struktur von Hoteldokumenten. Jedes Feld verfügt über eine name, Daten typeund Attribute, die sein Verhalten während der Indizierung und Abfragen bestimmen. Das HotelId Feld ist als Schlüssel gekennzeichnet, den Azure AI Search benötigt, um jedes Dokument in einem Index eindeutig zu identifizieren.

Wichtige Punkte zum Indexschema:

  • Verwenden Sie Zeichenfolgenfelder (Edm.String), um numerische Daten volltextdurchsuchbar zu machen. Andere unterstützte Datentypen, wie zum Beispiel Edm.Int32, sind filterbar, sortierbar, facettierbar und abrufbar, aber nicht durchsuchbar.

  • Die meisten unserer Felder sind einfache Datentypen, aber Sie können komplexe Typen definieren, um geschachtelte Daten wie das Address Feld darzustellen.

  • Feldattribute bestimmen zulässige Aktionen. Die REST-APIs lassen standardmäßig viele Aktionen zu. Beispielsweise sind alle Zeichenfolgen durchsuchbar und abrufbar. Bei den REST-APIs können Sie nur Attribute verwenden, wenn Sie ein Verhalten deaktivieren müssen.

Laden des Indexes

Neu erstellte Indizes sind leer. Um einen Index aufzufüllen und durchsuchbar zu machen, müssen Sie JSON-Dokumente hochladen, die dem Indexschema entsprechen.

In Azure AI Search dienen Dokumente sowohl als Eingaben für die Indizierung als auch als Ausgaben für Abfragen. Aus Gründen der Einfachheit stellt diese Schnellstartanleitung Beispiel-Hoteldokumente als Inline-JSON bereit. In Produktionsszenarien werden Inhalte jedoch häufig aus verbundenen Datenquellen abgerufen und mithilfe von Indexern in JSON transformiert.

So laden Sie Dokumente in Ihren Index hoch:

  1. Erstellen Sie ein $body Objekt, um die JSON-Nutzlast von vier Beispieldokumenten zu speichern.

    $body = @"
        {
            "value": [
            {
            "@search.action": "upload",
            "HotelId": "1",
            "HotelName": "Stay-Kay City Hotel",
            "Description": "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
            "Category": "Boutique",
            "Tags": [ "view", "air conditioning", "concierge" ],
            "ParkingIncluded": false,
            "LastRenovationDate": "2022-01-18T00:00:00Z",
            "Rating": 3.60,
            "Address": 
                {
                "StreetAddress": "677 5th Ave",
                "City": "New York",
                "StateProvince": "NY",
                "PostalCode": "10022",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "2",
            "HotelName": "Old Century Hotel",
            "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
            "Category": "Boutique",
            "Tags": [ "pool", "free wifi", "concierge" ],
            "ParkingIncluded": false,
            "LastRenovationDate": "2019-02-18T00:00:00Z",
            "Rating": 3.60,
            "Address": 
                {
                "StreetAddress": "140 University Town Center Dr",
                "City": "Sarasota",
                "StateProvince": "FL",
                "PostalCode": "34243",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "3",
            "HotelName": "Gastronomic Landscape Hotel",
            "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
            "Category": "Suite",
            "Tags": [ "restaurant", "bar", "continental breakfast" ],
            "ParkingIncluded": true,
            "LastRenovationDate": "2015-09-20T00:00:00Z",
            "Rating": 4.80,
            "Address": 
                {
                "StreetAddress": "3393 Peachtree Rd",
                "City": "Atlanta",
                "StateProvince": "GA",
                "PostalCode": "30326",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "4",
            "HotelName": "Sublime Palace Hotel",
            "Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
            "Category": "Boutique",
            "Tags": [ "concierge", "view", "air conditioning" ],
            "ParkingIncluded": true,
            "LastRenovationDate": "2020-02-06T00:00:00Z",
            "Rating": 4.60,
            "Address": 
                {
                "StreetAddress": "7400 San Pedro Ave",
                "City": "San Antonio",
                "StateProvince": "TX",
                "PostalCode": "78216",
                "Country": "USA"
                }
            }
          ]
        }
    "@
    
  2. Aktualisieren Sie das $url-Objekt auf den Indexierungsendpunkt. Ersetzen Sie <YOUR-SEARCH-SERVICE> mit dem Wert, den Sie im Get-Endpunkt abgerufen haben.

    $url = "<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs/index?api-version=2024-07-01"
    
  3. Führen Sie Invoke-RestMethod diesen Befehl aus, um die Uploadanforderung an Ihren Suchdienst zu senden.

    Invoke-RestMethod -Uri $url -Headers $headers -Method Post -Body $body | ConvertTo-Json
    

    Die Antwort sollte den Schlüssel und den Status jedes hochgeladenen Dokuments enthalten.

Informationen zur Uploadanforderung

Diese Schnellstartanleitung ruft Dokumente – Index (REST-API) auf, um Ihrem Index vier Beispiel-Hoteldokumente hinzuzufügen. Im Vergleich zur vorherigen Anforderung wird der URI erweitert, um die docs Sammlung und den index Vorgang einzuschließen.

Jedes Dokument im value Array stellt ein Hotel dar und enthält Felder, die dem Indexschema entsprechen. Der @search.action Parameter gibt den Vorgang an, der für jedes Dokument ausgeführt werden soll. In unserem Beispiel verwenden wir upload, welches das Dokument hinzufügt, wenn es nicht vorhanden ist, oder es aktualisiert, falls es existiert.

Indexabfragen

Nachdem Dokumente in Ihren Index geladen wurden, können Sie die Volltextsuche verwenden, um bestimmte Begriffe oder Ausdrücke innerhalb ihrer Felder zu finden.

So führen Sie eine Volltextabfrage für Ihren Index aus:

  1. Aktualisieren Sie das $url Objekt, um Suchparameter anzugeben. Ersetzen Sie <YOUR-SEARCH-SERVICE> mit dem Wert, den Sie im Get-Endpunkt abgerufen haben.

    $url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=attached restaurant&searchFields=Description,Tags&$select=HotelId,HotelName,Tags,Description&$count=true'
    
  2. Führen Sie die Ausführung Invoke-RestMethod aus, um die Abfrageanforderung an Ihren Suchdienst zu senden.

    Invoke-RestMethod -Uri $url -Headers $headers | ConvertTo-Json
    

    Die Antwort sollte dem folgenden Beispiel ähneln, in dem ein übereinstimmende Hoteldokument, dessen Relevanzbewertung und die ausgewählten Felder angezeigt werden.

    {
      "@odata.context": "https://my-service.search.windows.net/indexes('hotels-quickstart')/$metadata#docs(*)",
      "@odata.count": 1,
      "value": [
        {
          "@search.score": 0.5575875,
          "HotelId": "3",
          "HotelName": "Gastronomic Landscape Hotel",
          "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services.",
          "Tags": "restaurant bar continental breakfast"
        }
      ]
    }
    

Informationen zur Abfrageanforderung

Diese Schnellstartanleitung ruft Dokumente - Search Post (REST API) auf, um Hoteldokumente zu finden, die Ihren Suchkriterien entsprechen. Der URI zielt weiterhin auf die docs Auflistung ab, schließt den index Vorgang aber nicht mehr ein.

Volltextsuchanforderungen enthalten immer einen search Parameter, der den Abfragetext enthält. Der Abfragetext kann einen oder mehrere Begriffe, Ausdrücke oder Operatoren enthalten. Zusätzlich dazu searchkönnen Sie andere Parameter angeben, um das Suchverhalten und die Ergebnisse zu verfeinern.

Unsere Abfrage sucht nach den Begriffen "angefügtes Restaurant" in den DescriptionTags Feldern jedes Hoteldokuments. Der $select Parameter beschränkt die in der Antwort zurückgegebenen Felder auf HotelId, , HotelName, und TagsDescription. Der $count Parameter fordert die Gesamtanzahl der übereinstimmenden Dokumente an.

Andere Abfragebeispiele

Führen Sie die folgenden Befehle aus, um die Abfragesyntax zu untersuchen. Sie können Zeichensuchen durchführen, $filter Ausdrücke verwenden, Ergebnislisten einschränken, bestimmte Felder auswählen und vieles mehr. Denken Sie daran, den wert, den Sie im <YOUR-SEARCH-SERVICE>erhalten haben, zu ersetzen.

# Query example 1
# Search the index for the terms 'restaurant' and 'wifi'
# Return only the HotelName, Description, and Tags fields
$url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=restaurant wifi&$count=true&$select=HotelName,Description,Tags'

# Query example 2 
# Use a filter to find hotels rated 4 or higher
# Return only the HotelName and Rating fields
$url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=*&$filter=Rating gt 4&$select=HotelName,Rating'

# Query example 3
# Take the top two results
# Return only the HotelName and Category fields
$url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=boutique&$top=2&$select=HotelName,Category'

# Query example 4
# Sort by a specific field (Address/City) in ascending order
# Return only the HotelName, Address/City, Tags, and Rating fields
$url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=pool&$orderby=Address/City asc&$select=HotelName, Address/City, Tags, Rating'

In dieser Schnellstartanleitung verwenden Sie die Azure.Search.Documents-Clientbibliothek, um einen Suchindex mit Beispieldaten für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können ein fertiges Notebook herunterladen und ausführen.

Voraussetzungen

Voraussetzungen für Microsoft Entra ID

Für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra-ID müssen Sie:

Abrufen von Ressourceninformationen

Sie müssen die folgenden Informationen abrufen, um Ihre Anwendung bei Ihrem Azure KI-Suche-Dienst zu authentifizieren:

Variablenname Wert
SEARCH_API_ENDPOINT Diesen Wert finden Sie im Azure-Portal. Wählen Sie Ihren Suchdienst und dann im linken Menü die Option Übersicht aus. Der URL-Wert unter Essentials ist der Endpunkt, den Sie benötigen. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

Erfahren Sie mehr über schlüssellose Authentifizierung und das Festlegen von Umgebungsvariablen.

Einrichten Ihrer Umgebung

Sie führen den Beispielcode in einem Jupyter Notebook aus. Sie müssen also Ihre Umgebung für die Ausführung von Jupyter Notebooks einrichten.

  1. Laden Sie das Beispielnotebook von GitHub herunter, oder kopieren Sie es.

  2. Öffnen Sie das Notebook in Visual Studio Code.

  3. Erstellen Sie eine neue Python-Umgebung, um die für dieses Tutorial erforderlichen Pakete zu installieren.

    Wichtig

    Installieren Sie keine Pakete in Ihrer globalen Python-Installation. Sie sollten beim Installieren von Python-Paketen immer eine virtuelle oder Conda-Umgebung verwenden, andernfalls können Sie die globale Python-Installation beschädigen.

    py -3 -m venv .venv
    .venv\scripts\activate
    

    Die Einrichtung kann eine Minute dauern. Wenn Probleme auftreten, sehen Sie unter Python-Umgebungen in VS Code nach.

  4. Installieren Sie Jupyter Notebooks und den IPython-Kernel für Jupyter Notebooks, falls Sie diese noch nicht haben.

    pip install jupyter
    pip install ipykernel
    python -m ipykernel install --user --name=.venv
    
  5. Wählen Sie den Notebook-Kernel aus.

    1. Klicken Sie im Notebook rechts oben auf Kernel auswählen.
    2. Wenn .venv in der Liste angezeigt wird, wählen Sie es aus. Wenn es nicht angezeigt wird, wählen Sie Anderen Kernel auswählen>Python-Umgebungen>.venv.

Erstellen, Laden und Abfragen eines Suchindexes

In diesem Abschnitt fügen Sie Code hinzu, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie führen das Programm aus, um die Ergebnisse in der Konsole anzuzeigen. Eine ausführliche Erläuterung des Codes finden Sie im Abschnitt Erläuterung des Codes.

  1. Stellen Sie sicher, dass das Notebook im .venv-Kernel geöffnet ist, wie im vorherigen Abschnitt beschrieben.

  2. Führen Sie die erste Codezelle aus, um die erforderlichen Pakete zu installieren, einschließlich azure-search-documents.

    ! pip install azure-search-documents==11.6.0b1 --quiet
    ! pip install azure-identity --quiet
    ! pip install python-dotenv --quiet
    
  3. Ersetzen Sie den Inhalt der zweiten Codezelle je nach Ihrer Authentifizierungsmethode durch den folgenden Code.

    Hinweis

    Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

    from azure.core.credentials import AzureKeyCredential
    from azure.identity import DefaultAzureCredential, AzureAuthorityHosts
    
    search_endpoint: str = "https://<Put your search service NAME here>.search.windows.net/"
    authority = AzureAuthorityHosts.AZURE_PUBLIC_CLOUD
    credential = DefaultAzureCredential(authority=authority)
    
    index_name: str = "hotels-quickstart-python"
    
  4. Entfernen Sie die folgenden beiden Zeilen aus der Codezelle Index erstellen. Anmeldeinformationen werden bereits in der vorherigen Codezelle festgelegt.

    from azure.core.credentials import AzureKeyCredential
    credential = AzureKeyCredential(search_api_key)
    
  5. Führen Sie die Codezelle Index erstellen aus, um einen Suchindex zu erstellen.

  6. Führen Sie die verbleibenden Codezellen nacheinander aus, um Dokumente zu laden und Abfragen auszuführen.

Erläutern des Codes

Erstellen eines Index

SearchIndexClient wird zum Erstellen und Verwalten von Indizes für Azure KI-Suche verwendet. Jedes Feld wird durch einen name identifiziert und hat einen bestimmten type.

Jedes Feld verfügt zudem über eine Reihe von Indexattributen, die angeben, ob Azure AI Search das Feld durchsuchen, filtern, sortieren und facettieren kann. Bei den meisten Feldern handelt es sich um einfache Datentypen, einige wie z.B. AddressType sind aber komplexe Typen, die es Ihnen ermöglichen, umfangreiche Datenstrukturen in Ihrem Index zu erstellen. Informieren Sie sich weiter über die unterstützten Datentypen und die Indexattribute, die unter Erstellen eines Index (REST) beschrieben sind.

Erstellen von Dokumentnutzdaten und Hochladen von Dokumenten

Verwenden Sie eine Indexaktion als Vorgangstyp, wie z.B. Hochladen oder Zusammenführen und Hochladen. Die Dokumente stammen von dem Beispiel HotelsData auf GitHub.

Durchsuchen eines Index

Abfrageergebnisse können abgerufen werden, sobald das erste Dokument indiziert wurde. Mit dem Testen des Index sollte aber gewartet werden, bis alle Dokumente indiziert wurden.

Verwenden Sie die Suchmethode von search.client class.

Die Beispielabfragen im Notebook sind:

  • Einfache Abfrage: Führt eine leere Suche aus (search=*) und gibt eine ungeordnete Liste (search score = 1,0) beliebiger Dokumente zurück. Da keine einschränkenden Kriterien vorhanden sind, umfasst das Ergebnis alle Dokumente.
  • Begriffsabfrage: Fügt ganze Begriffe zum Suchausdruck hinzu („WLAN“). Bei dieser Abfrage wird angegeben, dass das Ergebnis nur die Felder in der select-Anweisung umfasst. Durch die Einschränkung der zurückgegebenen Felder wird die Menge der Daten für die Rückübertragung verringert und die Latenz der Suche reduziert.
  • Gefilterte Abfrage: Fügt einen Filterausdruck hinzu, der nur Hotels mit einer Bewertung von mehr als vier zurückgibt, sortiert in absteigender Reihenfolge.
  • Feldbasierte Eingrenzung: Fügt search_fields hinzu, um die Ausführung der Abfrage auf bestimmte Felder zu beschränken.
  • Facetten: Generiert Facetten für positive Übereinstimmungen, die in den Suchergebnissen gefunden wurden. Es gibt keine Nulltreffer. Wenn die Suchergebnisse den Begriff wifi nicht enthalten, wird wifi auch nicht in der Struktur der Facettennavigation angezeigt.
  • Nachschlagen eines Dokuments: Gibt ein Dokument basierend auf seinem Schlüssel zurück. Dieser Vorgang ist nützlich, wenn Sie Drillthrough bereitstellen möchten, wenn Benutzer ein Element in einem Suchergebnis auswählen.
  • AutoVervollständigen: Gibt potenzielle Übereinstimmungen an, während Benutzende in das Suchfeld tippen. AutoVervollständigen verwendet eine Vorschlagsfunktion (sg), um zu bestimmen, welche Felder potenzielle Übereinstimmungen mit Anforderungen der Vorschlagsfunktion enthalten. In diesem Schnellstart sind diese Felder Tags, Address/City, Address/Country. Übergeben Sie die Buchstaben sa als Teilzeichenfolge, um AutoVervollständigen zu simulieren. Mit der autocomplete-Methode von SearchClient werden potenzielle Übereinstimmungen mit Begriffen zurückgesendet.

Entfernen des Indexes

Wenn Sie mit diesem Index fertig sind, können Sie ihn löschen, indem Sie die Codezelle Aufräumen ausführen. Durch das Löschen unnötiger Indizes wird Speicherplatz für weitere Schnellstarts und Tutorials freigegeben.

In dieser Schnellstartanleitung verwenden Sie die AZURE AI Search-REST-APIs zum Erstellen, Laden und Abfragen eines Suchindexes für die Volltextsuche. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Konfigurieren des Zugriffs

Sie können eine Verbindung mit Ihrem Azure AI Search-Dienst über API-Schlüssel oder Microsoft Entra-ID mit Rollenzuweisungen herstellen. Schlüssel sind für ein Einstieg einfacher, Rollen sind jedoch sicherer.

So konfigurieren Sie den empfohlenen rollenbasierten Zugriff:

  1. Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Suchdienst aus.

  2. Wählen Sie im linken Bereich Einstellungen>Schlüssel aus.

  3. Wählen Sie unter API-Zugriffssteuerung die Option Beide aus.

    Diese Option ermöglicht sowohl die schlüsselbasierte als auch die schlüssellose Authentifizierung. Nachdem Sie Rollen zugewiesen haben, können Sie zu diesem Schritt zurückkehren und die rollenbasierte Zugriffssteuerung auswählen.

  4. Wählen Sie im linken Bereich access control (IAM) aus.

  5. Wählen Sie Hinzufügen>Rollenzuweisung hinzufügen.

  6. Weisen Sie Ihrem Benutzerkonto die Rollen Suchdienst-Teilnehmer und Suchindex-Daten-Teilnehmer zu.

Weitere Informationen finden Sie unter Herstellen einer Verbindung mit Azure KI-Suche mithilfe von Rollen.

Endpunkt und Token abrufen

Im nächsten Abschnitt geben Sie den folgenden Endpunkt und das folgende Token an, um eine Verbindung mit Ihrem Azure AI Search-Dienst herzustellen. Bei diesen Schritten wird davon ausgegangen, dass Sie den rollenbasierten Zugriff konfiguriert haben.

So rufen Sie Ihren Dienstendpunkt und Ihr Token ab:

  1. Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Suchdienst aus.

  2. Wählen Sie im linken Bereich die Option "Übersicht" aus.

  3. Notieren Sie sich die URL, die https://my-service.search.windows.net ähnlich sein soll.

  4. Öffnen Sie auf Ihrem lokalen System ein Terminal.

  5. Anmeldung bei Ihrem Azure-Abonnement Wenn Sie über mehrere Abonnements verfügen, wählen Sie das Abonnement aus, das Ihren Suchdienst enthält.

    az login
    
  6. Notieren Sie sich Ihr Microsoft Entra-Token.

    az account get-access-token --scope https://search.azure.com/.default
    

Richten Sie Ihre Datei ein

Bevor Sie REST-API-Aufrufe an Ihren Azure AI Search-Dienst durchführen können, müssen Sie eine Datei erstellen, um Ihren Dienstendpunkt, das Authentifizierungstoken und die eventuellen Anforderungen zu speichern. Die REST-Clienterweiterung in Visual Studio Code unterstützt diese Aufgabe.

So richten Sie Ihre Anforderungsdatei ein:

  1. Öffnen Sie visual Studio Code auf Ihrem lokalen System.

  2. Erstellen Sie eine .rest- oder eine .http-Datei.

  3. Fügen Sie die folgenden Platzhalter und Anfragen in die Datei ein.

    @baseUrl = PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE
    @token = PUT-YOUR-PERSONAL-IDENTITY-TOKEN-HERE
    
    ### List existing indexes by name
    GET {{baseUrl}}/indexes?api-version=2024-07-01  HTTP/1.1
        Authorization: Bearer {{token}}
    
  4. Ersetzen Sie die @baseUrl und @token Platzhalter durch die Werte, die Sie in Get-Endpunkt und -Token erhalten haben. Geben Sie keine Anführungszeichen ein.

  5. Wählen Sie unter ### List existing indexes by name" "Anfrage senden" aus.

    Im angrenzenden Bereich sollte eine Antwort angezeigt werden. Sind Indizes vorhanden, werden diese aufgelistet. Andernfalls ist die Liste leer. Wenn der HTTP-Code 200 OK lautet, können Sie die nächsten Schritte ausführen.

    Screenshot: REST-Client, der für eine Suchdienstanfrage konfiguriert ist

Erstellen eines Suchindexes

Bevor Sie Azure AI Search Inhalte hinzufügen, müssen Sie einen Index erstellen, um zu definieren, wie die Inhalte gespeichert und strukturiert werden. Ein Index ähnelt einer Tabelle in einer relationalen Datenbank, ist jedoch speziell für Suchvorgänge wie die Volltextsuche konzipiert.

So erstellen Sie einen Index:

  1. Fügen Sie die folgende Anforderung in Ihre Datei ein.

    ### Create a new index
    POST {{baseUrl}}/indexes?api-version=2024-07-01  HTTP/1.1
        Content-Type: application/json
        Authorization: Bearer {{token}}
    
        {
            "name": "hotels-quickstart",  
            "fields": [
                {"name": "HotelId", "type": "Edm.String", "key": true, "filterable": true},
                {"name": "HotelName", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": true, "facetable": false},
                {"name": "Description", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzer": "en.lucene"},
                {"name": "Category", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true},
                {"name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true},
                {"name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true},
                {"name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true},
                {"name": "Address", "type": "Edm.ComplexType", 
                    "fields": [
                    {"name": "StreetAddress", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "searchable": true},
                    {"name": "City", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                    {"name": "StateProvince", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                    {"name": "PostalCode", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                    {"name": "Country", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true}
                    ]
                }
            ]
        }
    
  2. Wählen Sie unter ### Create a new index" "Anfrage senden" aus.

    Sie sollten eine HTTP/1.1 201 Created Antwort erhalten, deren Textkörper die JSON-Darstellung des Indexschemas enthält.

Informationen zur Anforderung zum Erstellen von Indexen

In dieser Schnellstartanleitung werden Indizes – Erstellen (REST-API) aufgerufen, um einen Suchindex mit dem Namen hotels-quickstart und den physischen Datenstrukturen in Ihrem Suchdienst zu erstellen.

Innerhalb des Indexschemas definiert die fields Sammlung die Struktur von Hoteldokumenten. Jedes Feld verfügt über eine name, Daten typeund Attribute, die sein Verhalten während der Indizierung und Abfragen bestimmen. Das HotelId Feld ist als Schlüssel gekennzeichnet, den Azure AI Search benötigt, um jedes Dokument in einem Index eindeutig zu identifizieren.

Wichtige Punkte zum Indexschema:

  • Verwenden Sie Zeichenfolgenfelder (Edm.String), um numerische Daten volltextdurchsuchbar zu machen. Andere unterstützte Datentypen, wie zum Beispiel Edm.Int32, sind filterbar, sortierbar, facettierbar und abrufbar, aber nicht durchsuchbar.

  • Die meisten unserer Felder sind einfache Datentypen, aber Sie können komplexe Typen definieren, um geschachtelte Daten wie das Address Feld darzustellen.

  • Feldattribute bestimmen zulässige Aktionen. Die REST-APIs lassen standardmäßig viele Aktionen zu. Beispielsweise sind alle Zeichenfolgen durchsuchbar und abrufbar. Bei den REST-APIs können Sie nur Attribute verwenden, wenn Sie ein Verhalten deaktivieren müssen.

Laden des Indexes

Neu erstellte Indizes sind leer. Um einen Index aufzufüllen und durchsuchbar zu machen, müssen Sie JSON-Dokumente hochladen, die dem Indexschema entsprechen.

In Azure AI Search dienen Dokumente sowohl als Eingaben für die Indizierung als auch als Ausgaben für Abfragen. Aus Gründen der Einfachheit stellt diese Schnellstartanleitung Beispiel-Hoteldokumente als Inline-JSON bereit. In Produktionsszenarien werden Inhalte jedoch häufig aus verbundenen Datenquellen abgerufen und mithilfe von Indexern in JSON transformiert.

So laden Sie Dokumente in Ihren Index hoch:

  1. Fügen Sie die folgende Anforderung in Ihre Datei ein.

    ### Upload documents
    POST {{baseUrl}}/indexes/hotels-quickstart/docs/index?api-version=2024-07-01  HTTP/1.1
        Content-Type: application/json
        Authorization: Bearer {{token}}
    
        {
            "value": [
            {
            "@search.action": "upload",
            "HotelId": "1",
            "HotelName": "Stay-Kay City Hotel",
            "Description": "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
            "Category": "Boutique",
            "Tags": [ "view", "air conditioning", "concierge" ],
            "ParkingIncluded": false,
            "LastRenovationDate": "2022-01-18T00:00:00Z",
            "Rating": 3.60,
            "Address": 
                {
                "StreetAddress": "677 5th Ave",
                "City": "New York",
                "StateProvince": "NY",
                "PostalCode": "10022",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "2",
            "HotelName": "Old Century Hotel",
            "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
             "Category": "Boutique",
            "Tags": [ "pool", "free wifi", "concierge" ],
            "ParkingIncluded": false,
            "LastRenovationDate": "2019-02-18T00:00:00Z",
            "Rating": 3.60,
            "Address": 
                {
                "StreetAddress": "140 University Town Center Dr",
                "City": "Sarasota",
                "StateProvince": "FL",
                "PostalCode": "34243",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "3",
            "HotelName": "Gastronomic Landscape Hotel",
            "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services.",
            "Category": "Suite",
            "Tags": [ "restaurant", "bar", "continental breakfast" ],
            "ParkingIncluded": true,
            "LastRenovationDate": "2015-09-20T00:00:00Z",
            "Rating": 4.80,
            "Address": 
                {
                "StreetAddress": "3393 Peachtree Rd",
                "City": "Atlanta",
                "StateProvince": "GA",
                "PostalCode": "30326",
                "Country": "USA"
                } 
            },
            {
            "@search.action": "upload",
            "HotelId": "4",
            "HotelName": "Sublime Palace Hotel",
            "Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
            "Category": "Luxury",
            "Tags": [ "concierge", "view", "air conditioning" ],
            "ParkingIncluded": true,
            "LastRenovationDate": "2020-02-06T00:00:00Z",
            "Rating": 4.60,
            "Address": 
                {
                "StreetAddress": "7400 San Pedro Ave",
                "City": "San Antonio",
                "StateProvince": "TX",
                "PostalCode": "78216",
                "Country": "USA"
                }
            }
          ]
        }
    
  2. Wählen Sie unter ### Upload documents" "Anfrage senden" aus.

    Sie sollten eine HTTP/1.1 200 OK Antwort erhalten, deren Textkörper den Schlüssel und den Status jedes hochgeladenen Dokuments enthält.

Informationen zur Uploadanforderung

Diese Schnellstartanleitung ruft Dokumente – Index (REST-API) auf, um Ihrem Index vier Beispiel-Hoteldokumente hinzuzufügen. Im Vergleich zur vorherigen Anforderung wird der URI erweitert, um die docs Sammlung und den index Vorgang einzuschließen.

Jedes Dokument im value Array stellt ein Hotel dar und enthält Felder, die dem Indexschema entsprechen. Der @search.action Parameter gibt den Vorgang an, der für jedes Dokument ausgeführt werden soll. In unserem Beispiel verwenden wir upload, welches das Dokument hinzufügt, wenn es nicht vorhanden ist, oder es aktualisiert, falls es existiert.

Indexabfragen

Nachdem Dokumente in Ihren Index geladen wurden, können Sie die Volltextsuche verwenden, um bestimmte Begriffe oder Ausdrücke innerhalb ihrer Felder zu finden.

So führen Sie eine Volltextabfrage für Ihren Index aus:

  1. Fügen Sie die folgende Anforderung in Ihre Datei ein.

    ### Run a query
    POST {{baseUrl}}/indexes/hotels-quickstart/docs/search?api-version=2024-07-01  HTTP/1.1
      Content-Type: application/json
      Authorization: Bearer {{token}}
    
      {
          "search": "attached restaurant",
          "select": "HotelId, HotelName, Tags, Description",
          "searchFields": "Description, Tags",
          "count": true
      }
    
  2. Wählen Sie unter ### Run a query" "Anfrage senden" aus.

    Sie sollten eine HTTP/1.1 200 OK Antwort ähnlich dem folgenden Beispiel erhalten, in dem ein übereinstimmende Hoteldokument, dessen Relevanzbewertung und die ausgewählten Felder angezeigt werden.

    {
      "@odata.context": "https://my-service.search.windows.net/indexes('hotels-quickstart')/$metadata#docs(*)",
      "@odata.count": 1,
      "value": [
        {
          "@search.score": 0.5575875,
          "HotelId": "3",
          "HotelName": "Gastronomic Landscape Hotel",
          "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel\u2019s restaurant services.",
          "Tags": [
            "restaurant",
            "bar",
            "continental breakfast"
          ]
        }
      ]
    }
    

Informationen zur Abfrageanforderung

Diese Schnellstartanleitung ruft Dokumente - Search Post (REST API) auf, um Hoteldokumente zu finden, die Ihren Suchkriterien entsprechen. Der URI zielt jetzt auf den /docs/search Vorgang ab.

Volltextsuchanforderungen enthalten immer einen search Parameter, der den Abfragetext enthält. Der Abfragetext kann einen oder mehrere Begriffe, Ausdrücke oder Operatoren enthalten. Zusätzlich dazu searchkönnen Sie andere Parameter angeben, um das Suchverhalten und die Ergebnisse zu verfeinern.

Unsere Abfrage sucht nach den Begriffen "angefügtes Restaurant" in den DescriptionTags Feldern jedes Hoteldokuments. Der select Parameter beschränkt die in der Antwort zurückgegebenen Felder auf HotelId, , HotelName, und TagsDescription. Der count Parameter fordert die Gesamtanzahl der übereinstimmenden Dokumente an.

In dieser Schnellstartanleitung verwenden Sie die Azure.Search.Documents-Clientbibliothek, um einen Suchindex mit Beispieldaten für die Volltextsuche zu erstellen, zu laden und abzufragen. Die Volltextsuche verwendet Apache Lucene für die Indizierung und die Abfragen sowie den BM25-Rangfolgealgorithmus, um Ergebnisse zu bewerten.

In dieser Schnellstartanleitung werden fiktive Hoteldaten aus dem Azure-Search-Beispieldaten-Repository verwendet, um den Index aufzufüllen.

Tipp

Sie können den Quellcode herunterladen, um mit einem fertigen Projekt zu beginnen, oder führen Sie die folgenden Schritte aus, um Eigene zu erstellen.

Voraussetzungen

Voraussetzungen für Microsoft Entra ID

Für die empfohlene schlüssellose Authentifizierung mit Microsoft Entra-ID müssen Sie:

Abrufen von Ressourceninformationen

Sie müssen die folgenden Informationen abrufen, um Ihre Anwendung bei Ihrem Azure KI-Suche-Dienst zu authentifizieren:

Variablenname Wert
SEARCH_API_ENDPOINT Diesen Wert finden Sie im Azure-Portal. Wählen Sie Ihren Suchdienst und dann im linken Menü die Option Übersicht aus. Der URL-Wert unter Essentials ist der Endpunkt, den Sie benötigen. Ein Beispiel für einen Endpunkt ist https://mydemo.search.windows.net.

Erfahren Sie mehr über schlüssellose Authentifizierung und das Festlegen von Umgebungsvariablen.

Einrichten

  1. Erstellen Sie einen neuen Ordner full-text-quickstart für die Anwendung, und öffnen Sie Visual Studio Code in diesem Ordner mit dem folgenden Befehl:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Erstellen Sie package.json mit dem folgenden Befehl:

    npm init -y
    
  3. Aktualisieren Sie package.json mit dem folgenden Befehl auf ECMAScript:

    npm pkg set type=module
    
  4. Installieren Sie die Clientbibliothek von Azure KI-Suche (Azure.Search.Documents) für JavaScript mit:

    npm install @azure/search-documents
    
  5. Installieren Sie für die empfohlene kennwortlose Authentifizierung die Azure Identity-Clientbibliothek mit:

    npm install @azure/identity
    

Erstellen, Laden und Abfragen eines Suchindexes

Im vorherigen Abschnitt Einrichten haben Sie die Clientbibliothek von Azure KI-Suche und andere Abhängigkeiten installiert.

In diesem Abschnitt fügen Sie Code hinzu, um einen Suchindex zu erstellen, ihn mit Dokumenten zu laden und Abfragen auszuführen. Sie führen das Programm aus, um die Ergebnisse in der Konsole anzuzeigen. Eine ausführliche Erläuterung des Codes finden Sie im Abschnitt Erläuterung des Codes.

Der Beispielcode in diesem Schnellstart verwendet Microsoft Entra ID für die empfohlene schlüssellose Authentifizierung. Wenn Sie einen API-Schlüssel verwenden möchten, können Sie das DefaultAzureCredential-Objekt durch ein AzureKeyCredential-Objekt ersetzen.

const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
  1. Erstellen Sie eine neue Datei mit dem Namen index.ts, und fügen Sie den folgenden Code in index.ts ein:

    // Import from the @azure/search-documents library
    import {
        SearchIndexClient,
        SearchClient,
        SearchFieldDataType,
        AzureKeyCredential,
        odata,
        SearchIndex
    } from "@azure/search-documents";
    
    // Import from the Azure Identity library
    import { DefaultAzureCredential } from "@azure/identity";
    
    // Importing the hotels sample data
    import hotelData from './hotels.json' assert { type: "json" };
    
    // Load the .env file if it exists
    import * as dotenv from "dotenv";
    dotenv.config();
    
    // Defining the index definition
    const indexDefinition: SearchIndex = {
    	"name": "hotels-quickstart",
    	"fields": [
    		{
    			"name": "HotelId",
    			"type": "Edm.String" as SearchFieldDataType,
    			"key": true,
    			"filterable": true
    		},
    		{
    			"name": "HotelName",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": true,
    			"facetable": false
    		},
    		{
    			"name": "Description",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "en.lucene"
    		},
    		{
    			"name": "Category",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Tags",
    			"type": "Collection(Edm.String)",
    			"searchable": true,
    			"filterable": true,
    			"sortable": false,
    			"facetable": true
    		},
    		{
    			"name": "ParkingIncluded",
    			"type": "Edm.Boolean",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "LastRenovationDate",
    			"type": "Edm.DateTimeOffset",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Rating",
    			"type": "Edm.Double",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Address",
    			"type": "Edm.ComplexType",
    			"fields": [
    				{
    					"name": "StreetAddress",
    					"type": "Edm.String" as SearchFieldDataType,
    					"filterable": false,
    					"sortable": false,
    					"facetable": false,
    					"searchable": true
    				},
    				{
    					"name": "City",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "StateProvince",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "PostalCode",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "Country",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				}
    			]
    		}
    	],
    	"suggesters": [
    		{
    			"name": "sg",
    			"searchMode": "analyzingInfixMatching",
    			"sourceFields": [
    				"HotelName"
    			]
    		}
    	]
    };
    
    async function main() {
    
    	// Your search service endpoint
    	const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
    
    	// Use the recommended keyless credential instead of the AzureKeyCredential credential.
    	const credential = new DefaultAzureCredential();
    	//const credential = new AzureKeyCredential(Your search service admin key);
    
    	// Create a SearchIndexClient to send create/delete index commands
    	const searchIndexClient: SearchIndexClient = new SearchIndexClient(
    		searchServiceEndpoint,
    		credential
    	);
    
    	// Creating a search client to upload documents and issue queries
    	const indexName: string  = "hotels-quickstart";
        const searchClient: SearchClient<any> = searchIndexClient.getSearchClient(indexName);
    
        console.log('Checking if index exists...');
        await deleteIndexIfExists(searchIndexClient, indexName);
    
        console.log('Creating index...');
        let index: SearchIndex = await searchIndexClient.createIndex(indexDefinition);
        console.log(`Index named ${index.name} has been created.`);
    
        console.log('Uploading documents...');
        let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
        console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `);
    
        // waiting one second for indexing to complete (for demo purposes only)
        sleep(1000);
    
        console.log('Querying the index...');
        console.log();
        await sendQueries(searchClient);
    }
    
    async function deleteIndexIfExists(searchIndexClient: SearchIndexClient, indexName: string) {
        try {
            await searchIndexClient.deleteIndex(indexName);
            console.log('Deleting index...');
        } catch {
            console.log('Index does not exist yet.');
        }
    }
    
    async function sendQueries(searchClient: SearchClient<any>) {
        // Query 1
        console.log('Query #1 - search everything:');
        let searchOptions: any = {
            includeTotalCount: true,
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        let searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log(`Result count: ${searchResults.count}`);
        console.log();
    
    
        // Query 2
        console.log('Query #2 - search with filter, orderBy, and select:');
        let state = 'FL';
        searchOptions = {
            filter: odata`Address/StateProvince eq ${state}`,
            orderBy: ["Rating desc"],
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        searchResults = await searchClient.search("wifi", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 3
        console.log('Query #3 - limit searchFields:');
        searchOptions = {
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("sublime palace", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    
    function sleep(ms: number) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Erstellen Sie eine Datei mit dem Namen hotels.json, und fügen Sie den folgenden Code in hotels.json ein:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Stay-Kay City Hotel",
                "Description": "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
                "Category": "Boutique",
                "Tags": ["view", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "2022-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Old Century Hotel",
                "Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
                "Category": "Boutique",
                "Tags": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "2019-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Gastronomic Landscape Hotel",
                "Description": "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                "Category": "Suite",
                "Tags": ["restaurant, "bar", "continental breakfast"],
                "ParkingIncluded": "true",
                "LastRenovationDate": "2015-09-20T00:00:00Z",
                "Rating": 4.8,
                "Address": {
                    "StreetAddress": "3393 Peachtree Rd",
                    "City": "Atlanta",
                    "StateProvince": "GA",
                    "PostalCode": "30326"
                }
            },
            {
                "HotelId": "4",
                "HotelName": "Sublime Palace Hotel",
                "Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "air conditioning"],
                "ParkingIncluded": true,
                "LastRenovationDate": "2020-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Erstellen Sie die Datei tsconfig.json, um den TypeScript-Code zu transpilieren, und kopieren Sie den folgenden Code für ECMAScript.

    {
        "compilerOptions": {
          "module": "NodeNext",
          "target": "ES2022", // Supports top-level await
          "moduleResolution": "NodeNext",
          "skipLibCheck": true, // Avoid type errors from node_modules
          "strict": true // Enable strict type-checking options
        },
        "include": ["*.ts"]
    }
    
  4. Transpilieren Sie von TypeScript in JavaScript:

    tsc
    
  5. Melden Sie sich mithilfe des folgenden Befehls bei Azure an:

    az login
    
  6. Führen Sie den JavaScript-Code mit dem folgenden Befehl aus:

    node index.js
    

Erläutern des Codes

Erstellen eines Index

Erstellen Sie die Datei hotels_quickstart_index.json. Diese Datei definiert, wie Azure KI-Suche mit den Dokumenten verfährt, die Sie im nächsten Schritt laden. Jedes Feld wird durch einen name identifiziert und hat einen bestimmten type. Jedes Feld verfügt zudem über eine Reihe von Indexattributen, die angeben, ob Azure AI Search das Feld durchsuchen, filtern, sortieren und facettieren kann. Bei den meisten Feldern handelt es sich um einfache Datentypen, einige wie z.B. AddressType sind aber komplexe Typen, die es Ihnen ermöglichen, umfangreiche Datenstrukturen in Ihrem Index zu erstellen. Informieren Sie sich weiter über die unterstützten Datentypen und die Indexattribute, die unter Erstellen eines Index (REST) beschrieben sind.

Wir möchten hotels_quickstart_index.json importieren, damit die Hauptfunktion auf die Indexdefinition zugreifen kann.

import indexDefinition from './hotels_quickstart_index.json';

interface HotelIndexDefinition {
    name: string;
    fields: SimpleField[] | ComplexField[];
    suggesters: SearchSuggester[];
};
const hotelIndexDefinition: HotelIndexDefinition = indexDefinition as HotelIndexDefinition;

In der main-Funktion erstellen Sie ein SearchIndexClient-Objekt, das zum Erstellen und Verwalten von Indizes für Azure AI Search verwendet wird.

const indexClient = new SearchIndexClient(endpoint, new AzureKeyCredential(apiKey));

Als nächstes löschen Sie den Index, falls er bereits existiert. Dies ist eine gängige Vorgehensweise für Test-/Democode.

Dazu wird eine einfache Funktion definiert, die versucht, den Index zu löschen.

async function deleteIndexIfExists(indexClient: SearchIndexClient, indexName: string): Promise<void> {
    try {
        await indexClient.deleteIndex(indexName);
        console.log('Deleting index...');
    } catch {
        console.log('Index does not exist yet.');
    }
}

Um die Funktion auszuführen, wird der Indexname aus der Indexdefinition extrahiert und das indexName-Objekt zusammen mit dem indexClient-Objekt an die deleteIndexIfExists()-Funktion übergeben.

// Getting the name of the index from the index definition
const indexName: string = hotelIndexDefinition.name;

console.log('Checking if index exists...');
await deleteIndexIfExists(indexClient, indexName);

Danach sind Sie bereit, den Index mit der createIndex()-Methode zu erstellen.

console.log('Creating index...');
let index = await indexClient.createIndex(hotelIndexDefinition);

console.log(`Index named ${index.name} has been created.`);

Laden von Dokumenten

In Azure AI Search sind die Dokumente Datenstrukturen, bei denen es sich sowohl um Eingaben für die Indizierung als auch um Ausgaben von Abfragen handeln kann. Sie können diese Daten per Push an den Index senden oder einen Indexer verwenden. In diesem Fall übertragen wir die Dokumente programmgesteuert an den Index.

Dokumenteingaben können Zeilen in einer Datenbank, Blobs in einem Blobspeicher oder JSON-Dokumente auf einem Datenträger sein. Sie können entweder hotels.json herunterladen oder selbst eine hotels.json-Datei mit folgendem Inhalt erstellen:

Ähnlich wie bei der Indexdefinition müssen Sie auch hierbei hotels.json am Anfang von index.ts importieren, damit in der main-Funktion auf die Daten zugegriffen werden kann.

import hotelData from './hotels.json';

interface Hotel {
    HotelId: string;
    HotelName: string;
    Description: string;
    Category: string;
    Tags: string[];
    ParkingIncluded: string | boolean;
    LastRenovationDate: string;
    Rating: number;
    Address: {
        StreetAddress: string;
        City: string;
        StateProvince: string;
        PostalCode: string;
    };
};

const hotels: Hotel[] = hotelData["value"];

Um Daten in den Suchindex zu indizieren, müssen Sie nun ein SearchClient-Objekt erstellen. Während das SearchIndexClient-Objekt zum Erstellen und Verwalten eines Index verwendet wird, dient das SearchClient-Objekt zum Hochladen von Dokumenten und Abfragen des Index.

Es gibt zwei Möglichkeiten zum Erstellen einer SearchClient: Die erste Option besteht darin, ein SearchClient-Objekt von Grund auf neu zu erstellen:

 const searchClient = new SearchClient<Hotel>(endpoint, indexName, new AzureKeyCredential(apiKey));

Alternativ können Sie die getSearchClient()-Methode des SearchIndexClient-Objekts verwenden, um das SearchClient-Objekt zu erstellen:

const searchClient = indexClient.getSearchClient<Hotel>(indexName);

Nachdem der Client definiert wurde, laden Sie die Dokumente in den Suchindex hoch. In diesem Fall verwenden Sie die mergeOrUploadDocuments()-Methode zum Hochladen der Dokumente, oder Sie führen sie mit einem bestehenden Dokument zusammen, wenn bereits ein Dokument mit demselben Schlüssel existiert. Überprüfen Sie dann, ob der Vorgang erfolgreich war, da zumindest das erste Dokument vorhanden ist.

console.log("Uploading documents...");
const indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotels);

console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)}`);

Führen Sie das Programm erneut mit tsc && node index.ts aus. Jetzt werden etwas andere Meldungen angezeigt als in Schritt 1. Dieses Mal ist der Index vorhanden, und es sollte eine Meldung angezeigt werden, die besagt, dass dieser gelöscht wird, bevor die App den neuen Index erstellt und dort Daten einfügt.

Bevor Sie die Abfragen im nächsten Schritt ausführen, definieren Sie eine Funktion, die das Programm eine Sekunde lang warten lässt. Dies geschieht nur zu Test-/Demozwecken, um sicherzustellen, dass die Indizierung abgeschlossen ist und die Dokumente im Index für Abfragen zur Verfügung stehen.

function sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

Um das Programm eine Sekunde lang warten zu lassen, rufen Sie die Funktion sleep auf:

sleep(1000);

Durchsuchen eines Index

Nachdem Sie einen Index erstellt und Dokumente hochgeladen haben, können Sie nun Abfragen an den Index senden. In diesem Abschnitt werden fünf verschiedene Abfragen an den Suchindex gesendet, um verschiedene verfügbare Abfragefunktionen zu veranschaulichen.

Die Abfragen werden in einer sendQueries()-Funktion geschrieben, die in der main-Funktion wie folgt aufgerufen wird:

await sendQueries(searchClient);

Abfragen werden mithilfe der search()-Methode von searchClient gesendet. Der erste Parameter ist der Suchtext, und der zweite Parameter gibt die Suchoptionen an.

Abfragebeispiel 1

Die erste Abfrage durchsucht *, was dem Durchsuchen aller Elemente entspricht, und wählt drei der Felder im Index aus. Es ist ein bewährtes Verfahren, nur die Felder per select auszuwählen, die Sie benötigen, da das Pullen unnötiger Daten die Latenz Ihrer Abfragen erhöhen kann.

Beim searchOptions-Objekt für diese Abfrage ist zudem includeTotalCount auf true festgelegt, wodurch die Anzahl der gefundenen Treffer zurückgegeben wird.

async function sendQueries(
    searchClient: SearchClient<Hotel>
): Promise<void> {

    // Query 1
    console.log('Query #1 - search everything:');
    const selectFields: SearchFieldArray<Hotel> = [
        "HotelId",
        "HotelName",
        "Rating",
    ];
    const searchOptions1 = { 
        includeTotalCount: true, 
        select: selectFields 
    };

    let searchResults = await searchClient.search("*", searchOptions1);
    for await (const result of searchResults.results) {
        console.log(`${JSON.stringify(result.document)}`);
    }
    console.log(`Result count: ${searchResults.count}`);

    // remaining queries go here
}

Die übrigen unten beschriebenen Abfragen sollten auch der sendQueries()-Funktion hinzugefügt werden. Sie sind hier aus Gründen der Lesbarkeit getrennt.

Abfragebeispiel 2

In der nächsten Abfrage geben Sie den Suchbegriff "wifi" an und fügen zudem einen Filter hinzu, um nur Ergebnisse zurückzugeben, deren Zustand gleich 'FL' ist. Die Ergebnisse werden außerdem nach dem Rating-Objekt des Hotels sortiert.

console.log('Query #2 - search with filter, orderBy, and select:');
let state = 'FL';
const searchOptions2 = {
    filter: odata`Address/StateProvince eq ${state}`,
    orderBy: ["Rating desc"],
    select: selectFields
};
searchResults = await searchClient.search("wifi", searchOptions2);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Abfragebeispiel 3

Anschließend wird die Suche mit dem Parameter searchFields auf ein einzelnes durchsuchbares Feld beschränkt. Dieser Ansatz ist eine großartige Möglichkeit, Ihre Abfrage effizienter zu gestalten, wenn Sie wissen, dass Sie nur an Übereinstimmungen in bestimmten Feldern interessiert sind.

console.log('Query #3 - limit searchFields:');
const searchOptions3 = {
    select: selectFields,
    searchFields: ["HotelName"] as const
};

searchResults = await searchClient.search("Sublime Palace", searchOptions3);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Abfragebeispiel 4

Eine weitere gängige Option, die in eine Abfrage einbezogen werden kann, ist facets. Mithilfe von Facets können Sie einen selbstgesteuerten Drilldown für die Ergebnisse auf der Benutzeroberfläche bereitstellen. Die Facets-Ergebnisse können im Ergebnisbereich in Kontrollkästchen umgewandelt werden.

console.log('Query #4 - limit searchFields and use facets:');
const searchOptions4 = {
    facets: ["Category"],
    select: selectFields,
    searchFields: ["HotelName"] as const
};

searchResults = await searchClient.search("*", searchOptions4);
for await (const result of searchResults.results) {
    console.log(`${JSON.stringify(result.document)}`);
}

Abfragebeispiel 5

Die abschließende Abfrage verwendet die getDocument()-Methode des searchClient-Objekts. Auf diese Weise können Sie ein Dokument effizient anhand seines Schlüssels abrufen.

console.log('Query #5 - Lookup document:');
let documentResult = await searchClient.getDocument('3')
console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`)

Zusammenfassung der Abfragen

Die obigen Abfragen verdeutlichen mehrere Möglichkeiten zum Abgleichen von Begriffen in einer Abfrage: Volltextsuche, Filter und Auto-Vervollständigen.

Volltextsuche und Filtervorgänge werden mit der searchClient.search-Methode durchgeführt. Eine Suchanfrage kann in der searchText-Zeichenfolge übergeben werden, während ein Filterausdruck in der filter-Eigenschaft der SearchOptions-Klasse übergeben werden kann. Um ohne Suche zu filtern, übergeben Sie einfach „*“ für den Parameter searchText der search-Methode. Wenn Sie ohne Filter suchen möchten, legen Sie die Eigenschaft filter nicht fest, oder übergeben Sie einfach keine SearchOptions-Instanz.

Bereinigen von Ressourcen

Wenn Sie in Ihrem eigenen Abonnement arbeiten, sollten Sie ein Projekt abschließen, indem Sie bestimmen, ob Sie die von Ihnen erstellten Ressourcen noch benötigen. Ressourcen, die nicht genutzt aber ausgeführt werden, können Sie Geld kosten. Sie können Ressourcen einzeln löschen oder die Ressourcengruppe löschen, um den gesamten Satz von Ressourcen zu löschen.

Im Azure-Portal können Sie Ressourcen suchen und verwalten, indem Sie im linken Bereich "Alle Ressourcen " oder "Ressourcengruppen " auswählen.

Denken Sie bei Verwendung eines kostenlosen Diensts an die Beschränkung auf maximal drei Indizes, Indexer und Datenquellen. Sie können einzelne Elemente über das Azure-Portal löschen, um unter dem Grenzwert zu bleiben.