Udostępnij za pośrednictwem


Szybki start: wyszukiwanie pełnotekstowe przy użyciu zestawów SDK platformy Azure

Dowiedz się, jak używać biblioteki klienta Azure.Search.Documents do tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania przy użyciu przykładowych danych na potrzeby wyszukiwania pełnotekstowego. Wyszukiwanie pełnotekstowe używa rozwiązania Apache Lucene do indeksowania i zapytań oraz algorytmu klasyfikacji BM25 na potrzeby oceniania wyników.

Ten przewodnik Szybki start tworzy i wykonuje zapytania dotyczące małego indeksu hotels-quickstart zawierającego dane dotyczące czterech hoteli.

Napiwek

Możesz pobrać kod źródłowy, aby rozpocząć od ukończonego projektu lub wykonać następujące kroki, aby utworzyć własny.

Wymagania wstępne

  • Aktywna subskrypcja platformy Azure — utwórz jedną bezpłatnie
  • Usługa wyszukiwania sztucznej inteligencji platformy Azure. Utwórz usługę , jeśli jej nie masz. W tym przewodniku Szybki start możesz użyć warstwy Bezpłatna.

Wymagania wstępne dotyczące identyfikatora entra firmy Microsoft

W przypadku zalecanego uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft należy wykonać następujące czynności:

  • Zainstaluj interfejs wiersza polecenia platformy Azure używany do uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft.
  • Przypisz zarówno role, jak Search Service Contributor i Search Index Data Contributor do konta użytkownika. Role można przypisać w witrynie Azure Portal w obszarze Kontrola dostępu (IAM)>Dodawanie przypisania roli. Aby uzyskać więcej informacji, zobacz Nawiązywanie połączenia z usługą Azure AI Search przy użyciu ról.

Pobieranie informacji o zasobie

Aby uwierzytelnić aplikację przy użyciu usługi Azure AI usługa wyszukiwania, należy pobrać następujące informacje:

Nazwa zmiennej Wartość
SEARCH_API_ENDPOINT Tę wartość można znaleźć w witrynie Azure Portal. Wybierz usługę wyszukiwania, a następnie z menu po lewej stronie wybierz pozycję Przegląd. Wartość adresu URL w obszarze Podstawy to potrzebny punkt końcowy. Przykładowy punkt końcowy może wyglądać podobnie jak https://mydemo.search.windows.net.

Dowiedz się więcej na temat uwierzytelniania bez klucza i ustawiania zmiennych środowiskowych.

Konfiguruj

  1. Utwórz nowy folder full-text-quickstart zawierający aplikację i otwórz program Visual Studio Code w tym folderze za pomocą następującego polecenia:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Utwórz nową aplikację konsolową za pomocą następującego polecenia:

    dotnet new console
    
  3. Zainstaluj bibliotekę klienta usługi Azure AI Search (Azure.Search.Documents) dla platformy .NET przy użyciu następujących elementów:

    dotnet add package Azure.Search.Documents
    
  4. Aby uzyskać zalecane uwierzytelnianie bez klucza za pomocą identyfikatora Entra firmy Microsoft, zainstaluj pakiet Azure.Identity za pomocą polecenia:

    dotnet add package Azure.Identity
    
  5. Aby uzyskać zalecane uwierzytelnianie bez klucza przy użyciu identyfikatora Entra firmy Microsoft, zaloguj się na platformie Azure za pomocą następującego polecenia:

    az login
    

Tworzenie, ładowanie i wykonywanie zapytań względem indeksu wyszukiwania

W poprzedniej sekcji konfiguracji utworzono nową aplikację konsolową i zainstalowano bibliotekę klienta usługi Azure AI Search.

W tej sekcji dodasz kod, aby utworzyć indeks wyszukiwania, załadować go przy użyciu dokumentów i uruchomić zapytania. Uruchom program, aby wyświetlić wyniki w konsoli programu . Aby uzyskać szczegółowe wyjaśnienie kodu, zobacz sekcję wyjaśniającą kod .

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
  1. W Program.cs wklej następujący kod. serviceName Edytuj zmienne i apiKey przy użyciu nazwy usługi wyszukiwania i klucza interfejsu API administratora.

    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 = "Secret Point Motel",
                            Description = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.",
                            DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                            Category = "Boutique",
                            Tags = new[] { "pool", "air conditioning", "concierge" },
                            ParkingIncluded = false,
                            LastRenovationDate = new DateTimeOffset(1970, 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 = "Twin Dome Motel",
                            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.",
                            DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                            Category = "Boutique",
                            Tags = new[] { "pool", "free wifi", "concierge" },
                            ParkingIncluded = false,
                            LastRenovationDate = new DateTimeOffset(1979, 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 = "Triple Landscape Hotel",
                            Description = "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                            DescriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                            Category = "Resort and Spa",
                            Tags = new[] { "air conditioning", "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 Cliff Hotel",
                            Description = "Sublime Cliff 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 1800 palace.",
                            DescriptionFr = "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.",
                            Category = "Boutique",
                            Tags = new[] { "concierge", "view", "24-hour front desk service" },
                            ParkingIncluded = true,
                            LastRenovationDate = new DateTimeOffset(1960, 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. W tym samym folderze utwórz nowy plik o nazwie Hotel.cs i wklej następujący kod. Ten kod definiuje strukturę dokumentu hotelowego.

    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(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { 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. Utwórz nowy plik o nazwie Hotel.cs i wklej następujący kod, aby zdefiniować strukturę dokumentu hotelowego. Atrybuty w polu określają sposób jej użycia w aplikacji. Na przykład IsFilterable atrybut musi być przypisany do każdego pola obsługującego wyrażenie filtru.

    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(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { 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. Utwórz nowy plik o nazwie Address.cs i wklej następujący kod, aby zdefiniować strukturę dokumentu adresu.

    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. Utwórz nowy plik o nazwie Hotel.Methods.cs i wklej następujący kod, aby zdefiniować ToString() przesłonięcia dla Hotel klasy.

    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(DescriptionFr))
                {
                    builder.AppendFormat("Description (French): {0}\n", DescriptionFr);
                }
    
                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. Utwórz nowy plik o nazwie Address.Methods.cs i wklej następujący kod, aby zdefiniować przesłonięć klasę ToString()Address.

    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. Skompiluj i uruchom aplikację za pomocą następującego polecenia:

    dotnet run
    

Dane wyjściowe obejmują komunikaty z elementu Console.WriteLine z dodawaniem informacji o zapytaniu i wynikach.

Wyjaśnienie kodu

W poprzednich sekcjach utworzono nową aplikację konsolową i zainstalowano bibliotekę klienta usługi Azure AI Search. Dodano kod umożliwiający utworzenie indeksu wyszukiwania, załadowanie go za pomocą dokumentów i uruchomienie zapytań. Uruchomiono program, aby wyświetlić wyniki w konsoli programu .

W tej sekcji wyjaśnimy kod dodany do aplikacji konsolowej.

Tworzenie klienta wyszukiwania

W Program.cs utworzono dwóch klientów:

Oba klienci potrzebują punktu końcowego usługi wyszukiwania i poświadczeń opisanych wcześniej w sekcji informacji o zasobie.

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

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 . . . 
}

Tworzenie indeksu

Ten przewodnik Szybki start tworzy indeks Hotels ładowany przy użyciu danych hotelowych i wykonywania zapytań względem. W tym kroku zdefiniujesz pola w indeksie. Każda definicja pola zawiera nazwę, typ danych i atrybuty określające sposób użycia pola.

W tym przykładzie metody synchroniczne biblioteki Azure.Search.Documents są używane dla uproszczenia i czytelności. Jednak w przypadku scenariuszy produkcyjnych należy użyć metod asynchronicznych, aby zapewnić skalowalność i elastyczność aplikacji. Na przykład należy użyć polecenia CreateIndexAsync zamiast polecenia CreateIndex.

Definiowanie struktur

Utworzono dwie klasy pomocnicze, Hotel.cs i Address.cs, aby zdefiniować strukturę dokumentu hotelowego i jego adresu. Klasa Hotel zawiera pola dla identyfikatora hotelu, nazwy, opisu, kategorii, tagów, parkingu, daty renowacji, klasyfikacji i adresu. Klasa Address zawiera pola adresów ulicznych, miasta, stanu/prowincji, kodu pocztowego i kraju/regionu.

W bibliotece klienta Azure.Search.Documents można użyć pól SearchableField i SimpleField, aby usprawnić definicje pól. Oba są pochodnymi pola wyszukiwania i mogą potencjalnie uprościć kod:

  • SimpleField może być dowolnym typem danych, jest zawsze niemożliwy do przeszukiwania (ignorowany w przypadku zapytań wyszukiwania pełnotekstowego) i jest pobierany (nie jest ukryty). Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Można użyć SimpleField elementu dla identyfikatorów dokumentów lub pól używanych tylko w filtrach, aspektach lub profilach oceniania. Jeśli tak, pamiętaj, aby zastosować wszelkie atrybuty niezbędne do scenariusza, takie jak IsKey = true identyfikator dokumentu. Aby uzyskać więcej informacji, zobacz SimpleFieldAttribute.cs w kodzie źródłowym.

  • SearchableField musi być ciągiem i zawsze można wyszukiwać i pobierać. Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Ponieważ ten typ pola można wyszukiwać, obsługuje synonimy i pełne uzupełnienie właściwości analizatora. Aby uzyskać więcej informacji, zobacz SearchableFieldAttribute.cs w kodzie źródłowym.

Niezależnie od tego, czy używasz podstawowego SearchField interfejsu API, czy jednego z modeli pomocnika, musisz jawnie włączyć atrybuty filtru, aspektu i sortowania. Na przykład IsFilterable, IsSortable i IsFacetable muszą być jawnie przypisane, jak pokazano w poprzednim przykładzie.

Tworzenie indeksu wyszukiwania

W Program.cs utworzysz obiekt SearchIndex, a następnie wywołasz metodę CreateIndex, aby wyrazić indeks w usłudze wyszukiwania. Indeks zawiera również element SearchSuggester umożliwiający automatyczne uzupełnianie w określonych polach.

// 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);
}

Ładowanie dokumentów

Usługa Azure AI Search wyszukuje zawartość przechowywaną w usłudze. W tym kroku załadujesz dokumenty JSON zgodne z utworzonym indeksem hotelowym.

W usłudze Azure AI Search dokumenty wyszukiwania to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Dane wejściowe dokumentu uzyskane z zewnętrznego źródła danych mogą być wierszami w bazie danych, obiektami blob w usłudze Blob Storage lub dokumentami JSON na dysku. W tym przykładzie używamy skrótu i osadzamy dokumenty JSON dla czterech hoteli w samym kodzie.

Podczas przekazywania dokumentów należy użyć obiektu IndexDocumentsBatch . Obiekt IndexDocumentsBatch zawiera kolekcję akcji, z których każda zawiera dokument i właściwość informującą usługę Azure AI Search o tym, jaką akcję wykonać (przekazywanie, scalanie, usuwanie i scalanieOrUpload).

W Program.cs utworzysz tablicę dokumentów i akcji indeksu, a następnie przekażesz tablicę do IndexDocumentsBatch. Następujące dokumenty są zgodne z indeksem hotels-quickstart zdefiniowanym przez klasę hotelową.

// 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 = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.",
                DescriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                Category = "Boutique",
                Tags = new[] { "pool", "air conditioning", "concierge" },
                ParkingIncluded = false,
                LastRenovationDate = new DateTimeOffset(1970, 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
}

Po zainicjowaniu obiektu IndexDocumentsBatch możesz wysłać go do indeksu, wywołując element IndexDocuments w obiekcie SearchClient.

Dokumenty są ładowane przy użyciu elementu SearchClient w Main()programie , ale operacja wymaga również uprawnień administratora w usłudze, która jest zwykle skojarzona z elementem SearchIndexClient. Jednym ze sposobów skonfigurowania tej operacji jest pobranie elementu SearchClient za pośrednictwem SearchIndexClient (searchIndexClient w tym przykładzie).

SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);

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

Ponieważ mamy aplikację konsolową, która uruchamia wszystkie polecenia sekwencyjnie, dodamy 2-sekundowy czas oczekiwania między indeksowaniem a zapytaniami.

// 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);

2-sekundowe opóźnienie rekompensuje indeksowanie, co jest asynchroniczne, dzięki czemu wszystkie dokumenty można indeksować przed wykonaniem zapytań. Kodowanie w opóźnieniu jest zwykle konieczne tylko w przypadku pokazów, testów i przykładowych aplikacji.

Przeszukiwanie indeksu

Wyniki zapytania można uzyskać zaraz po indeksowanym pierwszym dokumencie, ale rzeczywiste testowanie indeksu powinno poczekać, aż wszystkie dokumenty zostaną zindeksowane.

W tej sekcji dodano dwie funkcje: logikę zapytań i wyniki. W przypadku zapytań użyj metody Search . Ta metoda przyjmuje tekst wyszukiwania (ciąg zapytania) i inne opcje.

Klasa SearchResults reprezentuje wyniki.

W Program.csWriteDocuments metoda wyświetla wyniki wyszukiwania w konsoli programu .

// 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();
}

Przykład zapytania 1

Metoda RunQueries wykonuje zapytania i zwraca wyniki. Wyniki to obiekty hotelowe. W tym przykładzie przedstawiono podpis metody i pierwsze zapytanie. To zapytanie demonstruje Select parametr, który umożliwia tworzenie wyniku przy użyciu wybranych pól z dokumentu.

// 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
}

Przykład zapytania 2

W drugim zapytaniu wyszukaj termin, dodaj filtr, który wybiera dokumenty, w których ocena jest większa niż 4, a następnie sortuj według klasyfikacji w kolejności malejącej. Filter to wyrażenie logiczne, które jest obliczane za pośrednictwem pól IsFilterable w indeksie. Filtrowanie zapytań obejmuje lub wyklucza wartości. W związku z tym nie ma wskaźnika istotności skojarzonego z zapytaniem filtru.

// 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);

Przykład zapytania 3

Trzecie zapytanie demonstruje searchFieldsmetodę , używaną do określania zakresu operacji wyszukiwania pełnotekstowego do określonych pól.

// 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);

Przykład zapytania 4

Czwarte zapytanie demonstruje facetsmetodę , która może służyć do struktury struktury nawigacji aspektowej.

// 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);

Przykład zapytania 5

W piątym zapytaniu zwróć określony dokument. Wyszukiwanie dokumentu to typowa odpowiedź na OnClick zdarzenie w zestawie wyników.

// 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);

Przykład zapytania 6

Ostatnie zapytanie pokazuje składnię autouzupełniania, symulując częściowe dane wejściowe użytkownika sa , która rozpoznaje dwa możliwe dopasowania w polach źródłowych skojarzonych z sugerowanym elementem zdefiniowanym w indeksie.

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

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

Podsumowanie zapytań

Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.

Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu metody SearchClient.Search . Zapytanie wyszukiwania można przekazać w searchText ciągu, a wyrażenie filtru można przekazać we właściwości Filter klasy SearchOptions. Aby filtrować bez wyszukiwania, wystarczy przekazać "*"searchText parametr metody Search . Aby wyszukać bez filtrowania, pozostaw Filter właściwość niezastawioną lub nie przekazuj SearchOptions w ogóle wystąpienia.

Dowiedz się, jak używać biblioteki klienta Azure.Search.Documents do tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania przy użyciu przykładowych danych na potrzeby wyszukiwania pełnotekstowego. Wyszukiwanie pełnotekstowe używa rozwiązania Apache Lucene do indeksowania i zapytań oraz algorytmu klasyfikacji BM25 na potrzeby oceniania wyników.

Ten przewodnik Szybki start tworzy i wykonuje zapytania dotyczące małego indeksu hotels-quickstart zawierającego dane dotyczące czterech hoteli.

Napiwek

Możesz pobrać kod źródłowy, aby rozpocząć od ukończonego projektu lub wykonać następujące kroki, aby utworzyć własny.

Wymagania wstępne

  • Aktywna subskrypcja platformy Azure — utwórz jedną bezpłatnie
  • Usługa wyszukiwania sztucznej inteligencji platformy Azure. Utwórz usługę , jeśli jej nie masz. W tym przewodniku Szybki start możesz użyć warstwy Bezpłatna.

Wymagania wstępne dotyczące identyfikatora entra firmy Microsoft

W przypadku zalecanego uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft należy wykonać następujące czynności:

  • Zainstaluj interfejs wiersza polecenia platformy Azure używany do uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft.
  • Przypisz zarówno role, jak Search Service Contributor i Search Index Data Contributor do konta użytkownika. Role można przypisać w witrynie Azure Portal w obszarze Kontrola dostępu (IAM)>Dodawanie przypisania roli. Aby uzyskać więcej informacji, zobacz Nawiązywanie połączenia z usługą Azure AI Search przy użyciu ról.

Pobieranie informacji o zasobie

Aby uwierzytelnić aplikację przy użyciu usługi Azure AI usługa wyszukiwania, należy pobrać następujące informacje:

Nazwa zmiennej Wartość
SEARCH_API_ENDPOINT Tę wartość można znaleźć w witrynie Azure Portal. Wybierz usługę wyszukiwania, a następnie z menu po lewej stronie wybierz pozycję Przegląd. Wartość adresu URL w obszarze Podstawy to potrzebny punkt końcowy. Przykładowy punkt końcowy może wyglądać podobnie jak https://mydemo.search.windows.net.

Dowiedz się więcej na temat uwierzytelniania bez klucza i ustawiania zmiennych środowiskowych.

Konfiguruj

Przykład w tym przewodniku Szybki start współpracuje ze środowiskiem uruchomieniowym Języka Java. Zainstaluj zestaw Java Development Kit, taki jak Azul Zulu OpenJDK. Pakiet Microsoft Build zestawu OpenJDK lub preferowany zestaw JDK powinien również działać.

  1. Zainstaluj narzędzie Apache Maven. Następnie uruchom polecenie mvn -v , aby potwierdzić pomyślną instalację.

  2. Utwórz nowy pom.xml plik w katalogu głównym projektu i skopiuj do niego następujący kod:

    <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. Zainstaluj zależności, w tym bibliotekę klienta usługi Azure AI Search (Azure.Search.Documents) dla języka Java i bibliotekę klienta tożsamości platformy Azure dla języka Java za pomocą następujących elementów:

    mvn clean dependency:copy-dependencies
    
  4. Aby uzyskać zalecane uwierzytelnianie bez klucza przy użyciu identyfikatora Entra firmy Microsoft, zaloguj się na platformie Azure za pomocą następującego polecenia:

    az login
    

Tworzenie, ładowanie i wykonywanie zapytań względem indeksu wyszukiwania

W poprzedniej sekcji konfiguracji zainstalowano bibliotekę klienta usługi Azure AI Search i inne zależności.

W tej sekcji dodasz kod, aby utworzyć indeks wyszukiwania, załadować go przy użyciu dokumentów i uruchomić zapytania. Uruchom program, aby wyświetlić wyniki w konsoli programu . Aby uzyskać szczegółowe wyjaśnienie kodu, zobacz sekcję wyjaśniającą kod .

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Utwórz nowy plik o nazwie App.java i wklej następujący kod do App.java:

    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 = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.descriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 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.";
            hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "free wifi", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1979, 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 Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.";
            hotel.descriptionFr = "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.";
            hotel.category = "Resort and Spa";
            hotel.tags = new String[] { "air conditioning", "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 Palace is part of a lovingly restored 1800 palace.";
            hotel.descriptionFr = "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "concierge", "view", "24-hour front desk service" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1960, 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. Utwórz nowy plik o nazwie Hotel.java i wklej następujący kod do Hotel.java:

    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;
    
        /**
         * French description
         */
        @JsonProperty("DescriptionFr")
        @SearchableField(analyzerName = "fr.lucene")
        public String descriptionFr;
    
        /**
         * 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. Utwórz nowy plik o nazwie Address.java i wklej następujący kod do Address.java:

    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. Uruchom nową aplikację konsolową:

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

Wyjaśnienie kodu

W poprzednich sekcjach utworzono nową aplikację konsolową i zainstalowano bibliotekę klienta usługi Azure AI Search. Dodano kod umożliwiający utworzenie indeksu wyszukiwania, załadowanie go za pomocą dokumentów i uruchomienie zapytań. Uruchomiono program, aby wyświetlić wyniki w konsoli programu .

W tej sekcji wyjaśnimy kod dodany do aplikacji konsolowej.

Tworzenie klienta wyszukiwania

W App.java utworzono dwóch klientów:

  • Element SearchIndexClient tworzy indeks.
  • Element SearchClient ładuje istniejący indeks i wykonuje zapytanie względem istniejącego indeksu.

Oba klienci potrzebują punktu końcowego usługi wyszukiwania i poświadczeń opisanych wcześniej w sekcji informacji o zasobie.

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

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 . . . 
}

Tworzenie indeksu

Ten przewodnik Szybki start tworzy indeks Hotels ładowany przy użyciu danych hotelowych i wykonywania zapytań względem. W tym kroku zdefiniujesz pola w indeksie. Każda definicja pola zawiera nazwę, typ danych i atrybuty określające sposób użycia pola.

W tym przykładzie metody synchroniczne biblioteki Azure.Search.Documents są używane dla uproszczenia i czytelności. Jednak w przypadku scenariuszy produkcyjnych należy użyć metod asynchronicznych, aby zapewnić skalowalność i elastyczność aplikacji. Na przykład należy użyć polecenia CreateIndexAsync zamiast polecenia CreateIndex.

Definiowanie struktur

Utworzono dwie klasy pomocnicze, Hotel.java i Address.java, aby zdefiniować strukturę dokumentu hotelowego i jego adres. Klasa Hotel zawiera pola dla identyfikatora hotelu, nazwy, opisu, kategorii, tagów, parkingu, daty renowacji, oceny i adresu. Klasa Address zawiera pola dla adresu ulicznego, miasta, stanu/prowincji, kodu pocztowego i kraju/regionu.

W bibliotece klienta Azure.Search.Documents można użyć pól SearchableField i SimpleField , aby usprawnić definicje pól.

  • SimpleField może być dowolnym typem danych, jest zawsze niemożliwy do przeszukiwania (ignorowany w przypadku zapytań wyszukiwania pełnotekstowego) i jest pobierany (nie jest ukryty). Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Możesz użyć pola SimpleField dla identyfikatorów dokumentów lub pól używanych tylko w filtrach, aspektach lub profilach oceniania. Jeśli tak, pamiętaj, aby zastosować wszelkie atrybuty, które są niezbędne dla scenariusza, takie jak IsKey = true dla identyfikatora dokumentu.
  • SearchableField musi być ciągiem i zawsze można wyszukiwać i pobierać. Inne atrybuty są domyślnie wyłączone, ale można je włączyć. Ponieważ ten typ pola można wyszukiwać, obsługuje synonimy i pełne uzupełnienie właściwości analizatora.

Niezależnie od tego, czy używasz podstawowego SearchField interfejsu API, czy jednego z modeli pomocnika, musisz jawnie włączyć atrybuty filtru, aspektu i sortowania. Na przykład isFilterable, isSortablei isFacetable muszą być jawnie przypisane, jak pokazano w poprzednim przykładzie.

Tworzenie indeksu wyszukiwania

W App.javapliku utworzysz SearchIndex obiekt w metodzie main , a następnie wywołaj createOrUpdateIndex metodę , aby utworzyć indeks w usłudze wyszukiwania. Indeks zawiera również element umożliwiający SearchSuggester włączenie autouzupełniania w określonych polach.

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

Ładowanie dokumentów

Usługa Azure AI Search wyszukuje zawartość przechowywaną w usłudze. W tym kroku załadujesz dokumenty JSON zgodne z utworzonym indeksem hotelowym.

W usłudze Azure AI Search dokumenty wyszukiwania to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Dane wejściowe dokumentu uzyskane z zewnętrznego źródła danych mogą być wierszami w bazie danych, obiektami blob w usłudze Blob Storage lub dokumentami JSON na dysku. W tym przykładzie używamy skrótu i osadzamy dokumenty JSON dla czterech hoteli w samym kodzie.

Podczas przekazywania dokumentów należy użyć obiektu IndexDocumentsBatch . Obiekt IndexDocumentsBatch zawiera kolekcję indeksów, z których każda zawiera dokument i właściwość informującą usługę Azure AI Search o tym, jaką akcję należy wykonać (przekazywanie, scalanie, usuwanie i scalanieOrUpload).

W App.javaprogramie tworzysz dokumenty i akcje indeksu, a następnie przekazujesz je do IndexDocumentsBatchelementu . Następujące dokumenty są zgodne z indeksem hotels-quickstart zdefiniowanym przez klasę hotelową.

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 = "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.descriptionFr = "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.";
    hotel.category = "Boutique";
    hotel.tags = new String[] { "pool", "air conditioning", "concierge" };
    hotel.parkingIncluded = false;
    hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(1970, 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");
    }
}

Po zainicjowaniu obiektu można wysłać go do indeksu IndexDocumentsBatch , wywołując element indexDocuments w SearchClient obiekcie.

Dokumenty są ładowane przy użyciu elementu SearchClient w main()programie , ale operacja wymaga również uprawnień administratora w usłudze, która jest zwykle skojarzona z elementem SearchIndexClient. Jednym ze sposobów skonfigurowania tej operacji jest pobranie elementu SearchClient za pośrednictwem SearchIndexClient (searchIndexClient w tym przykładzie).

uploadDocuments(searchClient);

Ponieważ mamy aplikację konsolową, która uruchamia wszystkie polecenia sekwencyjnie, dodamy 2-sekundowy czas oczekiwania między indeksowaniem a zapytaniami.

// 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)
{
}

2-sekundowe opóźnienie rekompensuje indeksowanie, co jest asynchroniczne, dzięki czemu wszystkie dokumenty można indeksować przed wykonaniem zapytań. Kodowanie w opóźnieniu jest zwykle konieczne tylko w przypadku pokazów, testów i przykładowych aplikacji.

Przeszukiwanie indeksu

Wyniki zapytania można uzyskać zaraz po indeksowanym pierwszym dokumencie, ale rzeczywiste testowanie indeksu powinno poczekać, aż wszystkie dokumenty zostaną zindeksowane.

W tej sekcji dodano dwie funkcje: logikę zapytań i wyniki. W przypadku zapytań użyj metody Search. Ta metoda przyjmuje tekst wyszukiwania (ciąg zapytania) i inne opcje.

W App.javapliku WriteDocuments metoda wyświetla wyniki wyszukiwania w konsoli programu .

// 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();
}

Przykład zapytania 1

Metoda RunQueries wykonuje zapytania i zwraca wyniki. Wyniki to obiekty hotelowe. W tym przykładzie przedstawiono podpis metody i pierwsze zapytanie. To zapytanie demonstruje Select parametr, który umożliwia tworzenie wyniku przy użyciu wybranych pól z dokumentu.

// 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));
}

Przykład zapytania 2

W drugim zapytaniu wyszukaj termin, dodaj filtr, który wybiera dokumenty, w których ocena jest większa niż 4, a następnie sortuj według klasyfikacji w kolejności malejącej. Filter jest wyrażeniem logicznym, które jest obliczane względem isFilterable pól w indeksie. Filtrowanie zapytań obejmuje lub wyklucza wartości. W związku z tym nie ma wskaźnika istotności skojarzonego z zapytaniem filtru.

// 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));

Przykład zapytania 3

Trzecie zapytanie demonstruje searchFieldsmetodę , używaną do określania zakresu operacji wyszukiwania pełnotekstowego do określonych pól.

// 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));

Przykład zapytania 4

Czwarte zapytanie demonstruje facetsmetodę , która może służyć do struktury struktury nawigacji aspektowej.

// 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));

Przykład zapytania 5

W piątym zapytaniu zwróć określony dokument.

// 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();

Przykład zapytania 6

Ostatnie zapytanie pokazuje składnię autouzupełniania, s, która rozwiązuje dwa możliwe dopasowania w sourceFields skojarzonym z sugerowanym elementem zdefiniowanym w indeksie.

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

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

Podsumowanie zapytań

Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.

Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu metody SearchClient.search . Zapytanie wyszukiwania można przekazać w searchText ciągu, a wyrażenie filtru można przekazać we filter właściwości klasy SearchOptions . Aby filtrować bez wyszukiwania, wystarczy przekazać wartość "*" dla searchText parametru search metody . Aby wyszukać bez filtrowania, pozostaw filter właściwość niezastawioną lub nie przekazuj SearchOptions w ogóle wystąpienia.

Dowiedz się, jak używać biblioteki klienta Azure.Search.Documents do tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania przy użyciu przykładowych danych na potrzeby wyszukiwania pełnotekstowego. Wyszukiwanie pełnotekstowe używa rozwiązania Apache Lucene do indeksowania i zapytań oraz algorytmu klasyfikacji BM25 na potrzeby oceniania wyników.

Ten przewodnik Szybki start tworzy i wykonuje zapytania dotyczące małego indeksu hotels-quickstart zawierającego dane dotyczące czterech hoteli.

Napiwek

Możesz pobrać kod źródłowy, aby rozpocząć od ukończonego projektu lub wykonać następujące kroki, aby utworzyć własny.

Wymagania wstępne

  • Aktywna subskrypcja platformy Azure — utwórz jedną bezpłatnie
  • Usługa wyszukiwania sztucznej inteligencji platformy Azure. Utwórz usługę , jeśli jej nie masz. W tym przewodniku Szybki start możesz użyć warstwy Bezpłatna.

Wymagania wstępne dotyczące identyfikatora entra firmy Microsoft

W przypadku zalecanego uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft należy wykonać następujące czynności:

  • Zainstaluj interfejs wiersza polecenia platformy Azure używany do uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft.
  • Przypisz zarówno role, jak Search Service Contributor i Search Index Data Contributor do konta użytkownika. Role można przypisać w witrynie Azure Portal w obszarze Kontrola dostępu (IAM)>Dodawanie przypisania roli. Aby uzyskać więcej informacji, zobacz Nawiązywanie połączenia z usługą Azure AI Search przy użyciu ról.

Pobieranie informacji o zasobie

Aby uwierzytelnić aplikację przy użyciu usługi Azure AI usługa wyszukiwania, należy pobrać następujące informacje:

Nazwa zmiennej Wartość
SEARCH_API_ENDPOINT Tę wartość można znaleźć w witrynie Azure Portal. Wybierz usługę wyszukiwania, a następnie z menu po lewej stronie wybierz pozycję Przegląd. Wartość adresu URL w obszarze Podstawy to potrzebny punkt końcowy. Przykładowy punkt końcowy może wyglądać podobnie jak https://mydemo.search.windows.net.

Dowiedz się więcej na temat uwierzytelniania bez klucza i ustawiania zmiennych środowiskowych.

Konfiguruj

  1. Utwórz nowy folder full-text-quickstart zawierający aplikację i otwórz program Visual Studio Code w tym folderze za pomocą następującego polecenia:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Utwórz element package.json za pomocą następującego polecenia:

    npm init -y
    
  3. Zainstaluj bibliotekę klienta usługi Azure AI Search (Azure.Search.Documents) dla języka JavaScript za pomocą następujących elementów:

    npm install @azure/search-documents
    
  4. W przypadku zalecanego uwierzytelniania bez hasła zainstaluj bibliotekę klienta tożsamości platformy Azure za pomocą polecenia:

    npm install @azure/identity
    

Tworzenie, ładowanie i wykonywanie zapytań względem indeksu wyszukiwania

W poprzedniej sekcji konfiguracji zainstalowano bibliotekę klienta usługi Azure AI Search i inne zależności.

W tej sekcji dodasz kod, aby utworzyć indeks wyszukiwania, załadować go przy użyciu dokumentów i uruchomić zapytania. Uruchom program, aby wyświetlić wyniki w konsoli programu . Aby uzyskać szczegółowe wyjaśnienie kodu, zobacz sekcję wyjaśniającą kod .

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Utwórz nowy plik o nazwie index.js i wklej następujący kod do index.js:

    // 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 cliff", 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. Utwórz plik o nazwie hotels.json i wklej następujący kod do hotels.json:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Secret Point Motel",
                "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.",
                "Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                "Category": "Boutique",
                "Tags": ["pool", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "1970-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Twin Dome Motel",
                "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.",
                "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                "Category": "Boutique",
                "Tags": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "1979-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Triple Landscape Hotel",
                "Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                "Category": "Resort and Spa",
                "Tags": ["air conditioning", "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 Cliff Hotel",
                "Description": "Sublime Cliff 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 1800 palace.",
                "Description_fr": "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "24-hour front desk service"],
                "ParkingIncluded": true,
                "LastRenovationDate": "1960-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Utwórz plik o nazwie hotels_quickstart_index.json i wklej następujący kod do hotels_quickstart_index.json:

    {
    	"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"
    			]
    		}
    	]
    }
    
  4. Zaloguj się do platformy Azure przy użyciu następującego polecenia:

    az login
    
  5. Uruchom kod JavaScript za pomocą następującego polecenia:

    node index.js
    

Wyjaśnienie kodu

tworzenie indeksu

Plik hotels_quickstart_index.json definiuje sposób działania usługi Azure AI Search z dokumentami ładowanymi w następnym kroku. Każde pole jest identyfikowane przez element name i ma określony typeelement . Każde pole ma również szereg atrybutów indeksu, które określają, czy usługa Azure AI Search może wyszukiwać, filtrować, sortować i aspekty po polu. Większość pól to proste typy danych, ale niektóre, takie jak AddressType typy złożone, które umożliwiają tworzenie rozbudowanych struktur danych w indeksie. Więcej informacji na temat obsługiwanych typów danych i atrybutów indeksu opisano w artykule Create Index (REST) (Tworzenie indeksu (REST).

W naszej definicji indeksu chcemy zaimportować hotels_quickstart_index.json w górnej części index.js , aby funkcja główna mogła uzyskać dostęp do definicji indeksu.

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

W ramach funkcji main utworzymy element SearchIndexClient, który służy do tworzenia indeksów usługi Azure AI Search i zarządzania nimi.

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

Następnie chcemy usunąć indeks, jeśli już istnieje. Ta operacja jest powszechną praktyką w przypadku kodu testowego/demonstracyjnego.

Robimy to, definiując prostą funkcję, która próbuje usunąć indeks.

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

Aby uruchomić funkcję, wyodrębniamy nazwę indeksu z definicji indeksu i przekazujemy indexName element wraz z funkcją deleteIndexIfExists()indexClient .

const indexName = indexDefinition["name"];

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

Następnie możemy utworzyć indeks za pomocą createIndex() metody .

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

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

Ładowanie dokumentów

W usłudze Azure AI Search dokumenty to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Możesz wypchnąć takie dane do indeksu lub użyć indeksatora. W takim przypadku programowo wypchniemy dokumenty do indeksu.

Dane wejściowe dokumentu mogą być wierszami w bazie danych, obiektach blob w usłudze Blob Storage lub, jak w tym przykładzie, dokumentach JSON na dysku. Podobnie jak indexDefinitionw przypadku elementu , musimy również zaimportować hotels.json w górnej części index.js , aby można było uzyskać dostęp do danych w naszej funkcji głównej.

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

Aby zaindeksować dane do indeksu wyszukiwania, musimy teraz utworzyć element SearchClient. SearchIndexClient Element służy do tworzenia indeksu i zarządzania nim, SearchClient ale służy do przekazywania dokumentów i wykonywania zapytań względem indeksu.

Istnieją dwa sposoby tworzenia elementu SearchClient. Pierwszą opcją jest utworzenie od SearchClient podstaw:

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

Alternatywnie możesz użyć getSearchClient() metody , SearchIndexClient aby utworzyć element SearchClient:

const searchClient = indexClient.getSearchClient(indexName);

Teraz, gdy klient jest zdefiniowany, przekaż dokumenty do indeksu wyszukiwania. W takim przypadku używamy mergeOrUploadDocuments() metody , która przekazuje dokumenty lub scala je z istniejącym dokumentem, jeśli dokument o tym samym kluczu już istnieje.

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

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

Przeszukiwanie indeksu

Po przekazaniu indeksu i przekazaniu dokumentów możesz wysyłać zapytania do indeksu. W tej sekcji wysyłamy pięć różnych zapytań do indeksu wyszukiwania, aby zademonstrować różne dostępne funkcje zapytań.

Zapytania są zapisywane w sendQueries() funkcji, którą wywołujemy w funkcji main w następujący sposób:

await sendQueries(searchClient);

Zapytania są wysyłane przy użyciu search() metody searchClient. Pierwszy parametr to tekst wyszukiwania, a drugi parametr określa opcje wyszukiwania.

Przykład zapytania 1

Pierwsze zapytanie wyszukuje *element , który jest odpowiednikiem wyszukiwania wszystkiego i wybiera trzy pola w indeksie. Jest to najlepsze rozwiązanie tylko select dla potrzebnych pól, ponieważ ściąganie niepotrzebnych danych może powodować opóźnienie zapytań.

Dla searchOptions tego zapytania ustawiono includeTotalCount również wartość true, która zwraca liczbę znalezionych pasujących wyników.

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
}

Pozostałe zapytania opisane poniżej należy również dodać do sendQueries() funkcji. Są one tutaj oddzielone w celu czytelności.

Przykład zapytania 2

W następnym zapytaniu określamy termin "wifi" wyszukiwania, a także uwzględniamy filtr, aby zwracać wyniki tylko wtedy, gdy stan jest równy 'FL'. Wyniki są również uporządkowane przez hotel Rating.

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)}`);
}

Przykład zapytania 3

Następnie wyszukiwanie jest ograniczone do pojedynczego pola z możliwością wyszukiwania przy użyciu parametru searchFields . Takie podejście jest doskonałym rozwiązaniem, aby zwiększyć wydajność zapytania, jeśli wiesz, że interesuje Cię tylko dopasowanie w niektórych polach.

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();

Przykład zapytania 4

Inną typową opcją uwzględnienia w zapytaniu jest facets. Aspekty umożliwiają tworzenie filtrów w interfejsie użytkownika, aby ułatwić użytkownikom poznanie wartości, do których mogą filtrować.

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)}`);
}

Przykład zapytania 5

Ostatnie zapytanie używa getDocument() metody searchClient. Dzięki temu można efektywnie pobierać dokument przy użyciu jego klucza.

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

Podsumowanie zapytań

Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.

Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu searchClient.search metody . Zapytanie wyszukiwania można przekazać w searchText ciągu, podczas gdy wyrażenie filtru może zostać przekazane we filter właściwości SearchOptions klasy. Aby filtrować bez wyszukiwania, wystarczy przekazać wartość "*" dla searchText parametru search metody . Aby wyszukać bez filtrowania, pozostaw filter właściwość niezastawioną lub nie przekazuj SearchOptions w ogóle wystąpienia.

Dowiedz się, jak używać biblioteki klienta Azure.Search.Documents do tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania przy użyciu przykładowych danych na potrzeby wyszukiwania pełnotekstowego. Wyszukiwanie pełnotekstowe używa rozwiązania Apache Lucene do indeksowania i zapytań oraz algorytmu klasyfikacji BM25 na potrzeby oceniania wyników.

Ten przewodnik Szybki start tworzy i wykonuje zapytania dotyczące małego indeksu hotels-quickstart zawierającego dane dotyczące czterech hoteli.

Napiwek

Możesz pobrać i uruchomić gotowy notes.

Wymagania wstępne

Wymagania wstępne dotyczące identyfikatora entra firmy Microsoft

W przypadku zalecanego uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft należy wykonać następujące czynności:

  • Zainstaluj interfejs wiersza polecenia platformy Azure używany do uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft.
  • Przypisz zarówno role, jak Search Service Contributor i Search Index Data Contributor do konta użytkownika. Role można przypisać w witrynie Azure Portal w obszarze Kontrola dostępu (IAM)>Dodawanie przypisania roli. Aby uzyskać więcej informacji, zobacz Nawiązywanie połączenia z usługą Azure AI Search przy użyciu ról.

Pobieranie informacji o zasobie

Aby uwierzytelnić aplikację przy użyciu usługi Azure AI usługa wyszukiwania, należy pobrać następujące informacje:

Nazwa zmiennej Wartość
SEARCH_API_ENDPOINT Tę wartość można znaleźć w witrynie Azure Portal. Wybierz usługę wyszukiwania, a następnie z menu po lewej stronie wybierz pozycję Przegląd. Wartość adresu URL w obszarze Podstawy to potrzebny punkt końcowy. Przykładowy punkt końcowy może wyglądać podobnie jak https://mydemo.search.windows.net.

Dowiedz się więcej na temat uwierzytelniania bez klucza i ustawiania zmiennych środowiskowych.

Konfigurowanie środowiska

Przykładowy kod jest uruchamiany w notesie Jupyter. Dlatego należy skonfigurować środowisko do uruchamiania notesów Jupyter.

  1. Pobierz lub skopiuj przykładowy notes z usługi GitHub.

  2. Otwórz notes w programie Visual Studio Code.

  3. Utwórz nowe środowisko języka Python, które ma być używane do instalowania pakietów potrzebnych na potrzeby tego samouczka.

    Ważne

    Nie instaluj pakietów w globalnej instalacji języka Python. Zawsze należy używać środowiska wirtualnego lub conda podczas instalowania pakietów języka Python. W przeciwnym razie możesz przerwać globalną instalację języka Python.

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

    Skonfigurowanie może potrwać minutę. Jeśli wystąpią problemy, zobacz Środowiska języka Python w programie VS Code.

  4. Zainstaluj notesy Jupyter i jądro IPython dla notesów Jupyter, jeśli jeszcze ich nie masz.

    pip install jupyter
    pip install ipykernel
    python -m ipykernel install --user --name=.venv
    
  5. Wybierz jądro notesu.

    1. W prawym górnym rogu notesu wybierz pozycję Wybierz jądro.
    2. Jeśli widzisz .venv ją na liście, wybierz ją. Jeśli go nie widzisz, wybierz pozycję Wybierz inne środowiska>.venv języka Python jądra.>

Tworzenie, ładowanie i wykonywanie zapytań względem indeksu wyszukiwania

W tej sekcji dodasz kod, aby utworzyć indeks wyszukiwania, załadować go przy użyciu dokumentów i uruchomić zapytania. Uruchom program, aby wyświetlić wyniki w konsoli programu . Aby uzyskać szczegółowe wyjaśnienie kodu, zobacz sekcję wyjaśniającą kod .

  1. Upewnij się, że notes jest otwarty w .venv jądrze zgodnie z opisem w poprzedniej sekcji.

  2. Uruchom pierwszą komórkę kodu, aby zainstalować wymagane pakiety, w tym azure-search-documents.

    ! pip install azure-search-documents==11.6.0b1 --quiet
    ! pip install azure-identity --quiet
    ! pip install python-dotenv --quiet
    
  3. Zastąp zawartość drugiej komórki kodu następującym kodem w zależności od metody uwierzytelniania.

    Uwaga

    Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

    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. Usuń następujące dwa wiersze z komórki Tworzenie kodu indeksu . Poświadczenia są już ustawione w poprzedniej komórce kodu.

    from azure.core.credentials import AzureKeyCredential
    credential = AzureKeyCredential(search_api_key)
    
  5. Uruchom komórkę Tworzenie kodu indeksu , aby utworzyć indeks wyszukiwania.

  6. Uruchom pozostałe komórki kodu sekwencyjnie, aby załadować dokumenty i uruchomić zapytania.

Wyjaśnienie kodu

Tworzenie indeksu

SearchIndexClient służy do tworzenia indeksów usługi Azure AI Search i zarządzania nimi. Każde pole jest identyfikowane przez element name i ma określony element type.

Każde pole ma również szereg atrybutów indeksu, które określają, czy usługa Azure AI Search może wyszukiwać, filtrować, sortować i aspekty po polu. Większość pól to proste typy danych, ale niektóre, takie jak AddressType typy złożone, które umożliwiają tworzenie rozbudowanych struktur danych w indeksie. Więcej informacji na temat obsługiwanych typów danych i atrybutów indeksu opisano w artykule Create Index (REST) (Tworzenie indeksu (REST).

Tworzenie ładunku dokumentów i przekazywanie dokumentów

Użyj akcji indeksu dla typu operacji, takiej jak przekazywanie lub scalanie i przekazywanie. Dokumenty pochodzą z przykładu HotelsData w usłudze GitHub.

Przeszukiwanie indeksu

Wyniki zapytania można uzyskać zaraz po indeksowanym pierwszym dokumencie, ale rzeczywiste testowanie indeksu powinno poczekać, aż wszystkie dokumenty zostaną zindeksowane.

Użyj metody wyszukiwania klasy search.client.

Przykładowe zapytania w notesie to:

  • Zapytanie podstawowe: wykonuje puste wyszukiwanie (search=*), zwracając nies rankingową listę (wynik wyszukiwania = 1,0) dowolnych dokumentów. Ponieważ nie ma żadnych kryteriów, wszystkie dokumenty są uwzględniane w wynikach.
  • Zapytanie terminu: dodaje całe terminy do wyrażenia wyszukiwania ("wifi"). To zapytanie określa, że wyniki zawierają tylko te pola w instrukcji select . Ograniczenie pól, które wracają, minimalizuje ilość danych wysyłanych z powrotem przez sieć i zmniejsza opóźnienie wyszukiwania.
  • Zapytanie filtrowane: dodaj wyrażenie filtru, zwracając tylko te hotele z oceną większą niż cztery posortowane w kolejności malejącej.
  • Określanie zakresu pól: dodaj search_fields do zakresu wykonywania zapytań do określonych pól.
  • Aspekty: Generuj aspekty dla pozytywnych dopasowań znalezionych w wynikach wyszukiwania. Brak dopasowań zerowych. Jeśli wyniki wyszukiwania nie zawierają terminu wifi, sieć Wi-Fi nie jest wyświetlana w strukturze nawigacji aspektowej.
  • Wyszukaj dokument: Zwróć dokument na podstawie jego klucza. Ta operacja jest przydatna, jeśli chcesz podać przeglądanie szczegółowe, gdy użytkownik wybierze element w wynikach wyszukiwania.
  • Autouzupełnianie: podaj potencjalne dopasowania jako typy użytkowników w polu wyszukiwania. Autouzupełnianie używa sugestora (sg), aby wiedzieć, które pola zawierają potencjalne dopasowania do żądań sugestora. W tym przewodniku Szybki start te pola to Tags: , Address/CityAddress/Country. Aby symulować autouzupełnianie, przekaż litery sa jako ciąg częściowy. Metoda autouzupełniania elementu SearchClient wysyła potencjalne dopasowania terminów.

Usuwanie indeksu

Jeśli skończysz z tym indeksem, możesz go usunąć, uruchamiając komórkę Wyczyść kod. Usunięcie niepotrzebnych indeksów zwalnia miejsce na przechodzenie przez kolejne przewodniki Szybki start i samouczki.

Dowiedz się, jak używać biblioteki klienta Azure.Search.Documents do tworzenia, ładowania i wykonywania zapytań względem indeksu wyszukiwania przy użyciu przykładowych danych na potrzeby wyszukiwania pełnotekstowego. Wyszukiwanie pełnotekstowe używa rozwiązania Apache Lucene do indeksowania i zapytań oraz algorytmu klasyfikacji BM25 na potrzeby oceniania wyników.

Ten przewodnik Szybki start tworzy i wykonuje zapytania dotyczące małego indeksu hotels-quickstart zawierającego dane dotyczące czterech hoteli.

Napiwek

Możesz pobrać kod źródłowy, aby rozpocząć od ukończonego projektu lub wykonać następujące kroki, aby utworzyć własny.

Wymagania wstępne

  • Aktywna subskrypcja platformy Azure — utwórz jedną bezpłatnie
  • Usługa wyszukiwania sztucznej inteligencji platformy Azure. Utwórz usługę , jeśli jej nie masz. W tym przewodniku Szybki start możesz użyć warstwy Bezpłatna.

Wymagania wstępne dotyczące identyfikatora entra firmy Microsoft

W przypadku zalecanego uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft należy wykonać następujące czynności:

  • Zainstaluj interfejs wiersza polecenia platformy Azure używany do uwierzytelniania bez klucza za pomocą identyfikatora Entra firmy Microsoft.
  • Przypisz zarówno role, jak Search Service Contributor i Search Index Data Contributor do konta użytkownika. Role można przypisać w witrynie Azure Portal w obszarze Kontrola dostępu (IAM)>Dodawanie przypisania roli. Aby uzyskać więcej informacji, zobacz Nawiązywanie połączenia z usługą Azure AI Search przy użyciu ról.

Pobieranie informacji o zasobie

Aby uwierzytelnić aplikację przy użyciu usługi Azure AI usługa wyszukiwania, należy pobrać następujące informacje:

Nazwa zmiennej Wartość
SEARCH_API_ENDPOINT Tę wartość można znaleźć w witrynie Azure Portal. Wybierz usługę wyszukiwania, a następnie z menu po lewej stronie wybierz pozycję Przegląd. Wartość adresu URL w obszarze Podstawy to potrzebny punkt końcowy. Przykładowy punkt końcowy może wyglądać podobnie jak https://mydemo.search.windows.net.

Dowiedz się więcej na temat uwierzytelniania bez klucza i ustawiania zmiennych środowiskowych.

Konfiguruj

  1. Utwórz nowy folder full-text-quickstart zawierający aplikację i otwórz program Visual Studio Code w tym folderze za pomocą następującego polecenia:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Utwórz element package.json za pomocą następującego polecenia:

    npm init -y
    
  3. Zaktualizuj element do ecMAScript package.json za pomocą następującego polecenia:

    npm pkg set type=module
    
  4. Zainstaluj bibliotekę klienta usługi Azure AI Search (Azure.Search.Documents) dla języka JavaScript za pomocą następujących elementów:

    npm install @azure/search-documents
    
  5. W przypadku zalecanego uwierzytelniania bez hasła zainstaluj bibliotekę klienta tożsamości platformy Azure za pomocą polecenia:

    npm install @azure/identity
    

Tworzenie, ładowanie i wykonywanie zapytań względem indeksu wyszukiwania

W poprzedniej sekcji konfiguracji zainstalowano bibliotekę klienta usługi Azure AI Search i inne zależności.

W tej sekcji dodasz kod, aby utworzyć indeks wyszukiwania, załadować go przy użyciu dokumentów i uruchomić zapytania. Uruchom program, aby wyświetlić wyniki w konsoli programu . Aby uzyskać szczegółowe wyjaśnienie kodu, zobacz sekcję wyjaśniającą kod .

Przykładowy kod w tym przewodniku Szybki start używa identyfikatora Entra firmy Microsoft do zalecanego uwierzytelniania bez klucza. Jeśli wolisz użyć klucza interfejsu API, możesz zastąpić DefaultAzureCredential obiekt obiektem AzureKeyCredential .

const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
  1. Utwórz nowy plik o nazwie index.ts i wklej następujący kod do index.ts:

    // 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": "Description_fr",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": false,
    			"sortable": false,
    			"facetable": false,
    			"analyzerName": "fr.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 cliff", 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. Utwórz plik o nazwie hotels.json i wklej następujący kod do hotels.json:

    {
        "value": [
            {
                "HotelId": "1",
                "HotelName": "Secret Point Motel",
                "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's 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.",
                "Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
                "Category": "Boutique",
                "Tags": ["pool", "air conditioning", "concierge"],
                "ParkingIncluded": false,
                "LastRenovationDate": "1970-01-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "677 5th Ave",
                    "City": "New York",
                    "StateProvince": "NY",
                    "PostalCode": "10022"
                }
            },
            {
                "HotelId": "2",
                "HotelName": "Twin Dome Motel",
                "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.",
                "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                "Category": "Boutique",
                "Tags": ["pool", "free wifi", "concierge"],
                "ParkingIncluded": "false",
                "LastRenovationDate": "1979-02-18T00:00:00Z",
                "Rating": 3.6,
                "Address": {
                    "StreetAddress": "140 University Town Center Dr",
                    "City": "Sarasota",
                    "StateProvince": "FL",
                    "PostalCode": "34243"
                }
            },
            {
                "HotelId": "3",
                "HotelName": "Triple Landscape Hotel",
                "Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
                "Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
                "Category": "Resort and Spa",
                "Tags": ["air conditioning", "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 Cliff Hotel",
                "Description": "Sublime Cliff 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 1800 palace.",
                "Description_fr": "Le sublime Cliff Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Cliff fait partie d'un Palace 1800 restauré avec amour.",
                "Category": "Boutique",
                "Tags": ["concierge", "view", "24-hour front desk service"],
                "ParkingIncluded": true,
                "LastRenovationDate": "1960-02-06T00:00:00Z",
                "Rating": 4.6,
                "Address": {
                    "StreetAddress": "7400 San Pedro Ave",
                    "City": "San Antonio",
                    "StateProvince": "TX",
                    "PostalCode": "78216"
                }
            }
        ]
    }
    
  3. Utwórz plik do tsconfig.json transpilowania kodu TypeScript i skopiuj następujący kod dla języka 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. Transpiluj z języka TypeScript do języka JavaScript.

    tsc
    
  5. Zaloguj się do platformy Azure przy użyciu następującego polecenia:

    az login
    
  6. Uruchom kod JavaScript za pomocą następującego polecenia:

    node index.js
    

Wyjaśnienie kodu

tworzenie indeksu

Utwórz plik hotels_quickstart_index.json. Ten plik definiuje sposób działania usługi Azure AI Search z dokumentami ładowanymi w następnym kroku. Każde pole jest identyfikowane przez element name i ma określony typeelement . Każde pole ma również szereg atrybutów indeksu, które określają, czy usługa Azure AI Search może wyszukiwać, filtrować, sortować i aspekty po polu. Większość pól to proste typy danych, ale niektóre, takie jak AddressType typy złożone, które umożliwiają tworzenie rozbudowanych struktur danych w indeksie. Więcej informacji na temat obsługiwanych typów danych i atrybutów indeksu opisano w artykule Create Index (REST) (Tworzenie indeksu (REST).

Chcemy zaimportować hotels_quickstart_index.json , aby funkcja main mogła uzyskać dostęp do definicji indeksu.

import indexDefinition from './hotels_quickstart_index.json';

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

W ramach funkcji main utworzymy element SearchIndexClient, który służy do tworzenia indeksów usługi Azure AI Search i zarządzania nimi.

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

Następnie chcemy usunąć indeks, jeśli już istnieje. Ta operacja jest powszechną praktyką w przypadku kodu testowego/demonstracyjnego.

Robimy to, definiując prostą funkcję, która próbuje usunąć indeks.

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.');
    }
}

Aby uruchomić funkcję, wyodrębniamy nazwę indeksu z definicji indeksu i przekazujemy indexName element wraz z funkcją deleteIndexIfExists()indexClient .

// 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);

Następnie możemy utworzyć indeks za pomocą createIndex() metody .

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

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

Ładowanie dokumentów

W usłudze Azure AI Search dokumenty to struktury danych, które są danymi wejściowymi do indeksowania i danych wyjściowych z zapytań. Możesz wypchnąć takie dane do indeksu lub użyć indeksatora. W takim przypadku programowo wypchniemy dokumenty do indeksu.

Dane wejściowe dokumentu mogą być wierszami w bazie danych, obiektach blob w usłudze Blob Storage lub, jak w tym przykładzie, dokumentach JSON na dysku. Możesz pobrać hotels.json lub utworzyć własny plik hotels.json z następującą zawartością:

Podobnie jak w przypadku elementu indexDefinition, musimy również zaimportować hotels.json w górnej części index.ts , aby można było uzyskać dostęp do danych w naszej funkcji main.

import hotelData from './hotels.json';

interface Hotel {
    HotelId: string;
    HotelName: string;
    Description: string;
    Description_fr: 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"];

Aby zaindeksować dane do indeksu wyszukiwania, musimy teraz utworzyć element SearchClient. SearchIndexClient Element służy do tworzenia indeksu i zarządzania nim, SearchClient ale służy do przekazywania dokumentów i wykonywania zapytań względem indeksu.

Istnieją dwa sposoby tworzenia elementu SearchClient. Pierwszą opcją jest utworzenie od SearchClient podstaw:

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

Alternatywnie możesz użyć getSearchClient() metody , SearchIndexClient aby utworzyć element SearchClient:

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

Teraz, gdy klient jest zdefiniowany, przekaż dokumenty do indeksu wyszukiwania. W takim przypadku używamy mergeOrUploadDocuments() metody , która przekazuje dokumenty lub scala je z istniejącym dokumentem, jeśli dokument o tym samym kluczu już istnieje. Następnie sprawdź, czy operacja zakończyła się pomyślnie, ponieważ istnieje co najmniej pierwszy dokument.

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

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

Uruchom ponownie program za pomocą polecenia tsc && node index.ts. W kroku 1 powinien zostać wyświetlony nieco inny zestaw komunikatów. Tym razem indeks istnieje i powinien zostać wyświetlony komunikat o usunięciu go, zanim aplikacja utworzy nowy indeks i opublikuje do niego dane.

Przed uruchomieniem zapytań w następnym kroku zdefiniuj funkcję, aby program czekał na jedną sekundę. Odbywa się to tylko w celach testowych/demonstracyjnych, aby upewnić się, że indeksowanie zakończy się i że dokumenty są dostępne w indeksie dla naszych zapytań.

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

Aby program czekał na jedną sekundę, wywołaj sleep funkcję:

sleep(1000);

Przeszukiwanie indeksu

Po przekazaniu indeksu i przekazaniu dokumentów możesz wysyłać zapytania do indeksu. W tej sekcji wysyłamy pięć różnych zapytań do indeksu wyszukiwania, aby zademonstrować różne dostępne funkcje zapytań.

Zapytania są zapisywane w sendQueries() funkcji, którą wywołujemy w funkcji main w następujący sposób:

await sendQueries(searchClient);

Zapytania są wysyłane przy użyciu search() metody searchClient. Pierwszy parametr to tekst wyszukiwania, a drugi parametr określa opcje wyszukiwania.

Przykład zapytania 1

Pierwsze zapytanie wyszukuje *element , który jest odpowiednikiem wyszukiwania wszystkiego i wybiera trzy pola w indeksie. Jest to najlepsze rozwiązanie tylko select dla potrzebnych pól, ponieważ ściąganie niepotrzebnych danych może powodować opóźnienie zapytań.

Dla searchOptions tego zapytania ustawiono includeTotalCount również wartość true, która zwróci liczbę znalezionych pasujących wyników.

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
}

Pozostałe zapytania opisane poniżej należy również dodać do sendQueries() funkcji. Są one tutaj oddzielone w celu czytelności.

Przykład zapytania 2

W następnym zapytaniu określamy termin "wifi" wyszukiwania, a także uwzględniamy filtr, aby zwracać wyniki tylko wtedy, gdy stan jest równy 'FL'. Wyniki są również uporządkowane przez hotel Rating.

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)}`);
}

Przykład zapytania 3

Następnie wyszukiwanie jest ograniczone do pojedynczego pola z możliwością wyszukiwania przy użyciu parametru searchFields . Takie podejście jest doskonałym rozwiązaniem, aby zwiększyć wydajność zapytania, jeśli wiesz, że interesuje Cię tylko dopasowanie w niektórych polach.

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)}`);
}

Przykład zapytania 4

Inną typową opcją uwzględnienia w zapytaniu jest facets. Aspekty umożliwiają zapewnienie samodzielnego przechodzenia do szczegółów z wyników w interfejsie użytkownika. Wyniki aspektów można przekształcić w pola wyboru w okienku wyników.

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)}`);
}

Przykład zapytania 5

Ostatnie zapytanie używa getDocument() metody searchClient. Dzięki temu można efektywnie pobierać dokument przy użyciu jego klucza.

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

Podsumowanie zapytań

Poprzednie zapytania pokazują wiele sposobów dopasowywania terminów w zapytaniu: wyszukiwanie pełnotekstowe, filtry i autouzupełnianie.

Wyszukiwanie pełnotekstowe i filtry są wykonywane przy użyciu searchClient.search metody . Zapytanie wyszukiwania można przekazać w searchText ciągu, podczas gdy wyrażenie filtru może zostać przekazane we filter właściwości SearchOptions klasy. Aby filtrować bez wyszukiwania, wystarczy przekazać wartość "*" dla searchText parametru search metody . Aby wyszukać bez filtrowania, pozostaw filter właściwość niezastawioną lub nie przekazuj SearchOptions w ogóle wystąpienia.

Czyszczenie zasobów

Jeśli pracujesz w ramach własnej subskrypcji, dobrym pomysłem po zakończeniu projektu jest sprawdzenie, czy dalej potrzebujesz utworzonych zasobów. Uruchomione zasoby mogą generować koszty. Zasoby możesz usuwać pojedynczo lub jako grupę zasobów, usuwając cały zestaw zasobów.

Zasoby można znaleźć w witrynie Azure Portal i zarządzać nimi, korzystając z linku Wszystkie zasoby lub Grupy zasobów w okienku nawigacji po lewej stronie.

Jeśli używasz bezpłatnej usługi, pamiętaj, że masz ograniczenie do trzech indeksów, indeksatorów i źródeł danych. Aby utrzymać limit, możesz usunąć poszczególne elementy w witrynie Azure Portal.

Następny krok

W tym przewodniku Szybki start przepracowaliśmy zestaw zadań w celu utworzenia indeksu, załadowania go przy użyciu dokumentów i uruchamiania zapytań. Na różnych etapach zajęliśmy skróty, aby uprościć kod pod kątem czytelności i zrozumienia. Teraz, gdy znasz podstawowe pojęcia, wypróbuj samouczek, który wywołuje interfejsy API usługi Azure AI Search w aplikacji internetowej.