Condividi tramite


Guida introduttiva: Ricerca a testo completo

In questa guida introduttiva si usa la libreria client Azure.Search.Documents per creare, caricare ed eseguire query su un indice di ricerca con dati di esempio per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questo quickstart utilizza dati fittizi sugli hotel dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

  • Un account Azure con una sottoscrizione attiva. Creare un account gratuito.

  • Un servizio di Azure AI Search. Creare un servizio se non ne è disponibile uno. Per questa guida introduttiva, è possibile usare un servizio gratuito.

Prerequisiti di Microsoft Entra ID

Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:

Recuperare informazioni sulla risorsa

Per autenticare l'applicazione con il servizio Azure AI Search, è necessario recuperare le informazioni seguenti:

Nome variabile valore
SEARCH_API_ENDPOINT Questo valore è disponibile nel portale di Azure. Selezionare il servizio di ricerca e quindi nel menu a sinistra selezionare Panoramica. Il valore URL in Informazioni di base è l'endpoint necessario. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

Altre informazioni sull'autenticazione senza chiave e sull'impostazione delle variabili di ambiente.

Configurare

  1. Creare una nuova cartella full-text-quickstart per contenere l'applicazione e aprire Visual Studio Code in tale cartella con il comando seguente:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Creare una nuova applicazione console con il comando seguente:

    dotnet new console
    
  3. Installare la libreria client di Azure AI Search (Azure.Search.Documents) per .NET con:

    dotnet add package Azure.Search.Documents
    
  4. Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, installare il pacchetto Azure.Identity con:

    dotnet add package Azure.Identity
    
  5. Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, accedere ad Azure con il comando seguente:

    az login
    

Creare, caricare ed eseguire query su un indice di ricerca

Nella sezione di configurazione precedente è stata creata una nuova applicazione console ed è stata installata la libreria client di Azure AI Search.

In questa sezione si aggiunge codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. Si esegue quindi il programma per visualizzare i risultati nella console. Per una spiegazione dettagliata del codice, vedere la sezione che illustra il codice.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto AzureKeyCredential.

Uri serviceEndpoint = new Uri($"https://<Put your search service NAME here>.search.windows.net/");
DefaultAzureCredential credential = new();
  1. In Program.cs copiare e incollare il codice seguente. Modificare le variabili serviceName e apiKey con il nome del servizio di ricerca e la chiave API dell'amministratore.

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

    using System;
    using System.Text.Json.Serialization;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            [SimpleField(IsKey = true, IsFilterable = true)]
            public string HotelId { get; set; }
    
            [SearchableField(IsSortable = true)]
            public string HotelName { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
            public string Description { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Category { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            [SearchableField]
            public Address Address { get; set; }
        }
    }
    
  3. Creare un nuovo file denominato Hotel.cs e incollare il codice seguente per definire la struttura di un documento relativo agli hotel. Gli attributi nel campo ne determinano in che modo è usato in un'applicazione. L'attributo IsFilterable, ad esempio, deve essere assegnato a ogni campo che supporta un'espressione filtro.

    using System;
    using System.Text.Json.Serialization;
    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            [SimpleField(IsKey = true, IsFilterable = true)]
            public string HotelId { get; set; }
    
            [SearchableField(IsSortable = true)]
            public string HotelName { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
            public string Description { get; set; }
    
            [SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public string Category { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public bool? ParkingIncluded { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public DateTimeOffset? LastRenovationDate { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
            public double? Rating { get; set; }
    
            [SearchableField]
            public Address Address { get; set; }
        }
    }
    
  4. Creare un nuovo file denominato Address.cs e incollare il codice seguente per definire la struttura di un documento relativo agli indirizzi.

    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. Creare un nuovo file denominato Hotel.Methods.cs e incollare il codice seguente per definire un override ToString() per la classe Hotel.

    using System;
    using System.Text;
    
    namespace AzureSearch.Quickstart
    {
        public partial class Hotel
        {
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                if (!String.IsNullOrEmpty(HotelId))
                {
                    builder.AppendFormat("HotelId: {0}\n", HotelId);
                }
    
                if (!String.IsNullOrEmpty(HotelName))
                {
                    builder.AppendFormat("Name: {0}\n", HotelName);
                }
    
                if (!String.IsNullOrEmpty(Description))
                {
                    builder.AppendFormat("Description: {0}\n", Description);
                }
    
                if (!String.IsNullOrEmpty(Category))
                {
                    builder.AppendFormat("Category: {0}\n", Category);
                }
    
                if (Tags != null && Tags.Length > 0)
                {
                    builder.AppendFormat("Tags: [ {0} ]\n", String.Join(", ", Tags));
                }
    
                if (ParkingIncluded.HasValue)
                {
                    builder.AppendFormat("Parking included: {0}\n", ParkingIncluded.Value ? "yes" : "no");
                }
    
                if (LastRenovationDate.HasValue)
                {
                    builder.AppendFormat("Last renovated on: {0}\n", LastRenovationDate);
                }
    
                if (Rating.HasValue)
                {
                    builder.AppendFormat("Rating: {0}\n", Rating);
                }
    
                if (Address != null && !Address.IsEmpty)
                {
                    builder.AppendFormat("Address: \n{0}\n", Address.ToString());
                }
    
                return builder.ToString();
            }
        }
    }
    
  6. Creare un nuovo file denominato Address.Methods.cs e incollare il codice seguente per definire un override ToString() per la classe 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. Compilare ed eseguire l'applicazione con il comando seguente:

    dotnet run
    

L'output include i messaggi restituiti da Console.WriteLine, con l'aggiunta di informazioni sulle query e i risultati.

Spiegazione del codice

Nelle sezioni precedenti è stata creata una nuova applicazione console ed è stata installata la libreria client di Azure AI Search. È stato aggiunto il codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. È stato eseguito il programma per visualizzare i risultati nella console.

In questa sezione viene illustrato il codice aggiunto all'applicazione console.

Creare un client di ricerca

In Program.cs sono stati creati due client:

Entrambi i client necessitano dell'endpoint del servizio di ricerca e delle credenziali descritte in precedenza nella sezione relativa alle informazioni sulla risorsa.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto 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 . . . 
}

Creare un indice

Questa guida di avvio rapido compila un indice di hotel in cui si caricano i dati relativi agli hotel su cui si eseguono query. In questo passaggio si definiscono i campi nell'indice. Ogni definizione di campo include un nome, un tipo di dati e gli attributi che determinano come viene usato il campo.

In questo esempio, per una maggiore leggibilità e semplicità vengono usati i metodi sincroni della libreria Azure.Search.Documents. Per scenari di produzione, tuttavia, è consigliabile usare metodi asincroni per mantenere la scalabilità e la reattività dell'app. Ad esempio, usare CreateIndexAsync invece di CreateIndex.

Definire le strutture

Sono state create due classi di helper, Hotel.cs e Address.cs, per definire la struttura di un documento relativo agli hotel e il rispettivo indirizzo. La classe Hotel include campi per ID, nome, descrizione, categoria, tag, parcheggio, data di ristrutturazione, valutazione e indirizzo di un hotel. La classe Address include campi per indirizzo, città, stato/provincia, codice postale e paese/area geografica.

Nella libreria client Azure.Search.Documents è possibile usare SearchableField e SimpleField per semplificare le definizioni dei campi. Entrambi derivano da un oggetto SearchField e possono semplificare il codice:

  • SimpleField può essere di qualsiasi tipo di dati, non è mai ricercabile (ignorato per le query di ricerca full-text) ed è recuperabile (non nascosto). Altri attributi sono disattivati per impostazione predefinita, ma possono essere attivati. Si potrebbe usare un oggetto SimpleField per gli ID di documento o i campi usati solo in filtri, facet o profili di punteggio. In tal caso, assicurarsi di applicare tutti gli attributi necessari per lo scenario, ad esempio IsKey = true per un ID documento. Per altre informazioni, vedere SimpleFieldAttribute.cs nel codice sorgente.

  • SearchableField deve essere una stringa ed è sempre ricercabile e recuperabile. Altri attributi sono disattivati per impostazione predefinita, ma possono essere attivati. Poiché questo tipo di campo è ricercabile, supporta i sinonimi e tutte le proprietà dell'analizzatore. Per altre informazioni, vedere SearchableFieldAttribute.cs nel codice sorgente.

Sia che si usi l'API SearchField di base o uno dei modelli di supporto, è necessario abilitare in modo esplicito gli attributi di filtro, facet e ordinamento. Ad esempio, IsFilterable, IsSortablee IsFacetable devono essere attribuiti in modo esplicito, come illustrato nell'esempio precedente.

Creare l'indice di ricerca

In Program.cs si crea un oggetto SearchIndex e quindi si chiama il metodo CreateIndex per esprimere l'indice nel servizio di ricerca. L'indice include anche un oggetto SearchSuggester per abilitare il completamento automatico nei campi specificati.

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

Caricare i documenti

Azure AI Search esegue ricerche sul contenuto archiviato nel servizio. In questo passaggio si caricano documenti JSON conformi all'indice di hotel creato.

In Azure AI Search i documenti di ricerca sono strutture di dati che costituiscono sia l'input per l'indicizzazione che l'output restituito dalle query. In quanto ottenuti da un'origine dati esterna, gli input dei documenti possono essere righe in un database, BLOB nell'archiviazione BLOB o documenti JSON nel disco. Per brevità, in questo esempio i documenti JSON per i quattro alberghi verranno incorporati nel codice stesso.

Quando si caricano i documenti, è necessario usare un oggetto IndexDocumentsBatch. Un oggetto IndexDocumentsBatch contiene una raccolta di Azioni, ognuna delle quali contiene un documento e una proprietà che indicano ad Azure AI Search quale azione eseguire (caricare, unire, eliminare e mergeOrUpload).

In Program.cs si crea una matrice di documenti e azioni di indice e quindi la si passa a IndexDocumentsBatch. I documenti seguenti sono conformi all'indice hotels-quickstart, come definito dalla classe hotel.

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

Dopo aver inizializzato l'oggetto IndexDocumentsBatch, è possibile inviarlo all'indice chiamando IndexDocuments nell'oggetto SearchClient.

I documenti vengono caricati usando SearchClient in Main(), ma l'operazione richiede anche diritti di amministratore per il servizio, che è in genere associato a SearchIndexClient. Un modo per configurare questa operazione consiste nell'ottenere SearchClient tramite SearchIndexClient (searchIndexClient in questo esempio).

SearchClient ingesterClient = searchIndexClient.GetSearchClient(indexName);

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

Poiché è disponibile un'app console che esegue tutti i comandi in sequenza, viene aggiunto un tempo di attesa di 2 secondi tra indicizzazione e query.

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

Il ritardo di due secondi compensa l'indicizzazione, che è asincrona, in modo che tutti i documenti possano essere indicizzati prima dell'esecuzione delle query. La scrittura di codice in un ritardo è in genere necessaria solo in applicazioni di esempio, test e demo.

Eseguire la ricerca in un indice

È possibile ottenere risultati della query subito dopo l'indicizzazione del primo documento, ma per il test effettivo dell'indice è necessario attendere il completamento dell'indicizzazione di tutti i documenti.

In questa sezione vengono aggiunte elementi di funzionalità, ovvero la logica di query e i risultati. Per le query usare il metodo Search. Questo metodo accetta il testo da cercare (stringa di query) e altre opzioni.

La classe SearchResults rappresenta i risultati.

In Program.cs il metodo WriteDocuments stampa i risultati della ricerca nella console.

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

Esempio di query 1

Il metodo RunQueries esegue query e restituisce i risultati. I risultati sono oggetti Hotel. Questo esempio mostra la firma del metodo e la prima query. Questa query illustra il parametro Select e consente di comporre il risultato usando i campi selezionati dal documento.

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

Esempio di query 2

Nella seconda query cercare un termine, aggiungere un filtro che seleziona i documenti con Valutazione maggiore di 4, quindi ordinare in base a Valutazione, in ordine decrescente. Un filtro è un'espressione booleana che viene valutata sui campi IsFilterable di un indice. Le query di filtro includono o escludono valori. Di conseguenza, a una query di filtro non è associato alcun punteggio della rilevanza.

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

Esempio di query 3

La terza query illustra searchFields, che si usa per impostare l'ambito di un'operazione di ricerca full-text su specifici campi.

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

Esempio di query 4

La quarta query illustra i facets, che possono essere usati per creare una struttura di esplorazione in base a facet.

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

Esempio di query 5

La quinta query restituisce un documento specifico. Una ricerca di documenti è una tipica risposta a un evento OnClick in un set di risultati.

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

Esempio di query 6

L'ultima query mostra la sintassi del completamento automatico, simulando l'input utente parziale sa che si risolve in due possibili corrispondenze nei campi sourceFields associati allo strumento suggerimenti definito nell'indice.

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

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

Riepilogo delle query

Le query precedenti mostrano più modi per trovare termini corrispondenti in una query: ricerca full-text, filtri e completamento automatico.

La ricerca full-text e i filtri vengono eseguiti con il metodo SearchClient.Search. Una query di ricerca può essere passata nella stringa searchText, mentre un'espressione filtro può essere passata nella proprietà Filter della classe SearchOptions. Per filtrare senza eseguire ricerche, passare soltanto "*" per il parametro searchText del metodo Search. Per eseguire una ricerca senza filtrare, lasciare la proprietà Filter non impostata oppure non passare un'istanza di SearchOptions.

In questa guida introduttiva si usa la libreria client Azure.Search.Documents per creare, caricare ed eseguire query su un indice di ricerca con dati di esempio per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa guida di avvio rapido utilizza i dati di hotel fittizi dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

  • Un account Azure con una sottoscrizione attiva. Creare un account gratuito.

  • Un servizio di Azure AI Search. Creare un servizio se non ne è disponibile uno. Per questa guida introduttiva, è possibile usare un servizio gratuito.

Prerequisiti di Microsoft Entra ID

Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:

Recuperare informazioni sulla risorsa

Per autenticare l'applicazione con il servizio Azure AI Search, è necessario recuperare le informazioni seguenti:

Nome variabile valore
SEARCH_API_ENDPOINT Questo valore è disponibile nel portale di Azure. Selezionare il servizio di ricerca e quindi nel menu a sinistra selezionare Panoramica. Il valore URL in Informazioni di base è l'endpoint necessario. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

Altre informazioni sull'autenticazione senza chiave e sull'impostazione delle variabili di ambiente.

Configurare

L'esempio in questa guida introduttiva funziona con Java Runtime. Installare un Development Kit di Java, come Azul Zulu OpenJDK. Microsoft Build di OpenJDK o il proprio JDK di scelta dovrebbero funzionare allo stesso modo.

  1. Installare Apache Maven. Quindi eseguire mvn -v per confermare l'installazione corretta.

  2. Creare un nuovo file pom.xml nella radice del progetto copiando il codice seguente:

    <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. Installare le dipendenze, inclusa la libreria client di Azure AI Search (Azure.Search.Documents) per Java e la libreria client di Identità di Azure per Java con:

    mvn clean dependency:copy-dependencies
    
  4. Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, accedere ad Azure con il comando seguente:

    az login
    

Creare, caricare ed eseguire query su un indice di ricerca

Nella sezione di configurazione precedente è stata eseguita l'installazione della libreria client di Azure AI Search e di altre dipendenze.

In questa sezione si aggiunge codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. Si esegue quindi il programma per visualizzare i risultati nella console. Per una spiegazione dettagliata del codice, vedere la sezione che illustra il codice.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto AzureKeyCredential.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Creare un nuovo file denominato App.java e incollare il codice seguente in 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 = "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "view", "air conditioning", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2022, 1, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.6;
            hotel.address = new Address();
            hotel.address.streetAddress = "677 5th Ave";
            hotel.address.city = "New York";
            hotel.address.stateProvince = "NY";
            hotel.address.postalCode = "10022";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "2";
            hotel.hotelName = "Old Century Hotel";
            hotel.description = "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
            hotel.category = "Boutique";
            hotel.tags = new String[] { "pool", "free wifi", "concierge" };
            hotel.parkingIncluded = false;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2019, 2, 18), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 3.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "140 University Town Center Dr";
            hotel.address.city = "Sarasota";
            hotel.address.stateProvince = "FL";
            hotel.address.postalCode = "34243";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "3";
            hotel.hotelName = "Gastronomic Landscape Hotel";
            hotel.description = "The Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.";
            hotel.category = "Suite";
            hotel.tags = new String[] { "restaurant", "bar", "continental breakfast" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2015, 9, 20), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.80;
            hotel.address = new Address();
            hotel.address.streetAddress = "3393 Peachtree Rd";
            hotel.address.city = "Atlanta";
            hotel.address.stateProvince = "GA";
            hotel.address.postalCode = "30326";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            hotel = new Hotel();
            hotel.hotelId = "4";
            hotel.hotelName = "Sublime Palace Hotel";
            hotel.description = "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.";
            hotel.category = "Boutique";
            hotel.tags = new String[] { "concierge", "view", "air conditioning" };
            hotel.parkingIncluded = true;
            hotel.lastRenovationDate = OffsetDateTime.of(LocalDateTime.of(LocalDate.of(2020, 2, 06), LocalTime.of(0, 0)), ZoneOffset.UTC);
            hotel.rating = 4.60;
            hotel.address = new Address();
            hotel.address.streetAddress = "7400 San Pedro Ave";
            hotel.address.city = "San Antonio";
            hotel.address.stateProvince = "TX";
            hotel.address.postalCode = "78216";
            hotel.address.country = "USA";
            hotelList.add(hotel);
    
            var batch = new IndexDocumentsBatch<Hotel>();
            batch.addMergeOrUploadActions(hotelList);
            try
            {
                searchClient.indexDocuments(batch);
            }
            catch (Exception e)
            {
                e.printStackTrace();
                // If for some reason any documents are dropped during indexing, you can compensate by delaying and
                // retrying. This simple demo just logs failure and continues
                System.err.println("Failed to index some of the documents");
            }
        }
    
        // Write search results to console
        private static void WriteSearchResults(SearchPagedIterable searchResults)
        {
            searchResults.iterator().forEachRemaining(result ->
            {
                Hotel hotel = result.getDocument(Hotel.class);
                System.out.println(hotel);
            });
    
            System.out.println();
        }
    
        // Write autocomplete results to console
        private static void WriteAutocompleteResults(AutocompletePagedIterable autocompleteResults)
        {
            autocompleteResults.iterator().forEachRemaining(result ->
            {
                String text = result.getText();
                System.out.println(text);
            });
    
            System.out.println();
        }
    
        // Run queries, use WriteDocuments to print output
        private static void RunQueries(SearchClient searchClient)
        {
            // Query 1
            System.out.println("Query #1: Search on empty term '*' to return all documents, showing a subset of fields...\n");
    
            SearchOptions options = new SearchOptions();
            options.setIncludeTotalCount(true);
            options.setFilter("");
            options.setOrderBy("");
            options.setSelect("HotelId", "HotelName", "Address/City");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 2
            System.out.println("Query #2: Search on 'hotels', filter on 'Rating gt 4', sort by Rating in descending order...\n");
    
            options = new SearchOptions();
            options.setFilter("Rating gt 4");
            options.setOrderBy("Rating desc");
            options.setSelect("HotelId", "HotelName", "Rating");
    
            WriteSearchResults(searchClient.search("hotels", options, Context.NONE));
    
            // Query 3
            System.out.println("Query #3: Limit search to specific fields (pool in Tags field)...\n");
    
            options = new SearchOptions();
            options.setSearchFields("Tags");
    
            options.setSelect("HotelId", "HotelName", "Tags");
    
            WriteSearchResults(searchClient.search("pool", options, Context.NONE));
    
            // Query 4
            System.out.println("Query #4: Facet on 'Category'...\n");
    
            options = new SearchOptions();
            options.setFilter("");
            options.setFacets("Category");
            options.setSelect("HotelId", "HotelName", "Category");
    
            WriteSearchResults(searchClient.search("*", options, Context.NONE));
    
            // Query 5
            System.out.println("Query #5: Look up a specific document...\n");
    
            Hotel lookupResponse = searchClient.getDocument("3", Hotel.class);
            System.out.println(lookupResponse.hotelId);
            System.out.println();
    
             // Query 6
            System.out.println("Query #6: Call Autocomplete on HotelName that starts with 's'...\n");
    
            WriteAutocompleteResults(searchClient.autocomplete("s", "sg"));
        }
    }
    
  2. Creare un nuovo file denominato Hotel.java e incollare il codice seguente in 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;
    
        /**
         * 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. Creare un nuovo file denominato Address.java e incollare il codice seguente in 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. Eseguire la nuova applicazione console:

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

Spiegazione del codice

Nelle sezioni precedenti è stata creata una nuova applicazione console ed è stata installata la libreria client di Azure AI Search. È stato aggiunto il codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. È stato eseguito il programma per visualizzare i risultati nella console.

In questa sezione viene illustrato il codice aggiunto all'applicazione console.

Creare un client di ricerca

In App.java sono stati creati due client:

  • SearchIndexClient crea l'indice.
  • SearchClient carica un indice esistente ed esegue query su tale indice.

Entrambi i client necessitano dell'endpoint del servizio di ricerca e delle credenziali descritte in precedenza nella sezione relativa alle informazioni sulla risorsa.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto 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 . . . 
}

Creare un indice

Questa guida di avvio rapido compila un indice di hotel in cui si caricano i dati relativi agli hotel su cui si eseguono query. In questo passaggio si definiscono i campi nell'indice. Ogni definizione di campo include un nome, un tipo di dati e gli attributi che determinano come viene usato il campo.

In questo esempio, per una maggiore leggibilità e semplicità vengono usati i metodi sincroni della libreria Azure.Search.Documents. Per scenari di produzione, tuttavia, è consigliabile usare metodi asincroni per mantenere la scalabilità e la reattività dell'app. Ad esempio, usare CreateIndexAsync invece di CreateIndex.

Definire le strutture

Sono state create due classi di helper, Hotel.java e Address.java, per definire la struttura di un documento relativo agli hotel e il rispettivo indirizzo. La classe Hotel include campi per ID, nome, descrizione, categoria, tag, parcheggio, data di ristrutturazione, valutazione e indirizzo di un hotel. La classe Address include campi per indirizzo, città, stato/provincia, codice postale e paese/area geografica.

Nella libreria client Azure.Search.Documents è possibile usare SearchableField e SimpleField per semplificare le definizioni dei campi.

  • SimpleField può essere di qualsiasi tipo di dati, non è mai ricercabile (ignorato per le query di ricerca full-text) ed è recuperabile (non nascosto). Altri attributi sono disattivati per impostazione predefinita, ma possono essere attivati. Si potrebbe usare un oggetto SimpleField per gli ID di documento o i campi usati solo in filtri, facet o profili di punteggio. In tal caso, assicurarsi di applicare gli attributi necessari per lo scenario, ad esempio IsKey = true per un ID documento.
  • SearchableField deve essere una stringa ed è sempre ricercabile e recuperabile. Altri attributi sono disattivati per impostazione predefinita, ma possono essere attivati. Poiché questo tipo di campo è ricercabile, supporta i sinonimi e tutte le proprietà dell'analizzatore.

Sia che si usi l'API SearchField di base o uno dei modelli di supporto, è necessario abilitare in modo esplicito gli attributi di filtro, facet e ordinamento. Ad esempio, isFilterable, isSortable e isFacetable devono essere attribuiti in modo esplicito, come illustrato nell'esempio precedente.

Creare l'indice di ricerca

In App.java si crea un oggetto SearchIndex nel metodo main e quindi si chiama il metodo createOrUpdateIndex per creare l'indice nel servizio di ricerca. L'indice include anche un SearchSuggester per abilitare il completamento automatico nei campi specificati.

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

Caricare i documenti

Azure AI Search esegue ricerche sul contenuto archiviato nel servizio. In questo passaggio si caricano documenti JSON conformi all'indice di hotel creato.

In Azure AI Search i documenti di ricerca sono strutture di dati che costituiscono sia l'input per l'indicizzazione che l'output restituito dalle query. In quanto ottenuti da un'origine dati esterna, gli input dei documenti possono essere righe in un database, BLOB nell'archiviazione BLOB o documenti JSON nel disco. Per brevità, in questo esempio i documenti JSON per i quattro alberghi verranno incorporati nel codice stesso.

Quando si caricano i documenti, è necessario usare un oggetto IndexDocumentsBatch. Un oggetto IndexDocumentsBatch contiene una raccolta di IndexActions, ognuna delle quali contiene un documento e una proprietà che indicano ad Azure AI Search quale azione eseguire (caricare, unire, eliminare e mergeOrUpload).

In App.java si creano documenti e azioni di indice e quindi li si passa a IndexDocumentsBatch. I documenti seguenti sono conformi all'indice hotels-quickstart, come definito dalla classe hotel.

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

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

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

Dopo aver inizializzato l'oggetto IndexDocumentsBatch, è possibile inviarlo all'indice chiamando indexDocuments sull'oggetto SearchClient.

I documenti vengono caricati usando SearchClient in main(), ma l'operazione richiede anche diritti di amministratore per il servizio, che è in genere associato a SearchIndexClient. Un modo per configurare questa operazione consiste nell'ottenere SearchClient tramite SearchIndexClient (searchIndexClient in questo esempio).

uploadDocuments(searchClient);

Poiché è disponibile un'app console che esegue tutti i comandi in sequenza, viene aggiunto un tempo di attesa di 2 secondi tra indicizzazione e query.

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

Il ritardo di due secondi compensa l'indicizzazione, che è asincrona, in modo che tutti i documenti possano essere indicizzati prima dell'esecuzione delle query. La scrittura di codice in un ritardo è in genere necessaria solo in applicazioni di esempio, test e demo.

Eseguire la ricerca in un indice

È possibile ottenere risultati della query subito dopo l'indicizzazione del primo documento, ma per il test effettivo dell'indice è necessario attendere il completamento dell'indicizzazione di tutti i documenti.

In questa sezione vengono aggiunte elementi di funzionalità, ovvero la logica di query e i risultati. Per le query, usare il metodo Search. Questo metodo accetta il testo da cercare (stringa di query) e altre opzioni.

In App.java il metodo WriteDocuments stampa i risultati della ricerca nella console.

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

Esempio di query 1

Il metodo RunQueries esegue query e restituisce i risultati. I risultati sono oggetti Hotel. Questo esempio mostra la firma del metodo e la prima query. Questa query illustra il parametro Select e consente di comporre il risultato usando i campi selezionati dal documento.

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

Esempio di query 2

Nella seconda query cercare un termine, aggiungere un filtro che seleziona i documenti con Valutazione maggiore di 4, quindi ordinare in base a Valutazione, in ordine decrescente. Filtro è un'espressione booleana che viene valutata in base ai campi isFilterable in un indice. Le query di filtro includono o escludono valori. Di conseguenza, a una query di filtro non è associato alcun punteggio della rilevanza.

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

Esempio di query 3

La terza query illustra searchFields, che si usa per impostare l'ambito di un'operazione di ricerca full-text su specifici campi.

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

Esempio di query 4

La quarta query illustra i facets, che possono essere usati per creare una struttura di esplorazione in base a facet.

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

Esempio di query 5

La quinta query restituisce un documento specifico.

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

Esempio di query 6

L'ultima query mostra la sintassi per il completamento automatico, simulando un input utente parziale di s che risolve due possibili corrispondenze nel sourceFields associato allo strumento suggerimenti definito nell'indice.

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

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

Riepilogo delle query

Le query precedenti mostrano più modi per trovare termini corrispondenti in una query: ricerca full-text, filtri e completamento automatico.

La ricerca full-text e i filtri vengono eseguiti con il metodo SearchClient.search. Una query di ricerca può essere passata nella stringa searchText, mentre un'espressione di filtro può essere passata nella proprietà filter della classe SearchOptions. Per filtrare senza eseguire ricerche, passare semplicemente "*" per il parametro searchText del metodo search. Per eseguire una ricerca senza filtrare, lasciare la proprietà filter non impostata oppure non passare un'istanza di SearchOptions.

In questa guida introduttiva si usa la libreria client Azure.Search.Documents per creare, caricare ed eseguire query su un indice di ricerca con dati di esempio per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa guida introduttiva utilizza dati fittizi su hotel provenienti dal repository azure-search-sample-data per riempire l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

  • Un account Azure con una sottoscrizione attiva. Creare un account gratuito.

  • Un servizio di Azure AI Search. Creare un servizio se non ne è disponibile uno. Per questa guida introduttiva, è possibile usare un servizio gratuito.

Prerequisiti di Microsoft Entra ID

Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:

Recuperare informazioni sulla risorsa

Per autenticare l'applicazione con il servizio Azure AI Search, è necessario recuperare le informazioni seguenti:

Nome variabile valore
SEARCH_API_ENDPOINT Questo valore è disponibile nel portale di Azure. Selezionare il servizio di ricerca e quindi nel menu a sinistra selezionare Panoramica. Il valore URL in Informazioni di base è l'endpoint necessario. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

Altre informazioni sull'autenticazione senza chiave e sull'impostazione delle variabili di ambiente.

Configurare

  1. Creare una nuova cartella full-text-quickstart per contenere l'applicazione e aprire Visual Studio Code in tale cartella con il comando seguente:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Creare package.json con il comando seguente:

    npm init -y
    
  3. Installare la libreria client di Azure AI Search (Azure.Search.Documents) per JavaScript con:

    npm install @azure/search-documents
    
  4. Per l'autenticazione senza password consigliata, installare la libreria client di Identità di Azure con:

    npm install @azure/identity
    

Creare, caricare ed eseguire query su un indice di ricerca

Nella sezione di configurazione precedente è stata eseguita l'installazione della libreria client di Azure AI Search e di altre dipendenze.

In questa sezione si aggiunge codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. Si esegue quindi il programma per visualizzare i risultati nella console. Per una spiegazione dettagliata del codice, vedere la sezione che illustra il codice.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto AzureKeyCredential.

String searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
  1. Creare un nuovo file denominato index.js e incollare il codice seguente in 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 palace", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Creare un file denominato hotels.json e incollare il codice seguente in hotels.json:

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

    az login
    
  5. Eseguire il codice JavaScript con il comando seguente:

    node index.js
    

Spiegazione del codice

Creare un indice

Il file hotels_quickstart_index.json definisce il funzionamento di Azure AI Search con i documenti caricati nel passaggio successivo. Ogni campo è identificato da un name e ha un type specificato. Ogni campo dispone anche di una serie di attributi di indice che specificano se Azure AI Search può eseguire ricerche, applicare filtri, eseguire l'ordinamento e applicare facet nel campo. Quasi tutti i campi sono tipi di dati semplici, ma alcuni come AddressType sono tipi complessi che consentono di creare strutture di dati avanzate nell'indice. Per altre informazioni sui tipi di dati supportati e sugli attributi di indice descritti, vedere Creare un indice (REST).

Dopo avere configurato la definizione dell'indice, si vuole importare hotels_quickstart_index.json all'inizio di index.js in modo che la funzione main possa accedere alla definizione dell'indice.

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

All'interno della funzione main creare quindi un SearchIndexClient, che viene usato per creare e gestire gli indici per Azure AI Search.

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

A questo punto eliminare l'indice, se esiste già. Questa operazione è una procedura comune per il codice di test/demo.

A questo scopo occorre definire una funzione semplice che tenta di eliminare l'indice.

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

Per eseguire la funzione, estrarre il nome dell'indice dalla definizione dell'indice e quindi passare indexName insieme a indexClient alla funzione deleteIndexIfExists().

const indexName = indexDefinition["name"];

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

A questo punto è possibile creare l'indice con il metodo createIndex().

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

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

Caricare i documenti

In Azure AI Search i documenti sono strutture dei dati che costituiscono sia l'input per l'indicizzazione che l'output restituito dalle query. È possibile eseguire il push di questi dati nell'indice oppure usare un indicizzatore. In questo caso, i documenti verranno inseriti nell'indice tramite programmazione.

Gli input dei documenti possono essere righe in un database, BLOB nell'archiviazione BLOB o, come in questo esempio, documenti JSON nel disco. Analogamente a quanto è stato fatto con indexDefinition, occorre anche importare hotels.json all'inizio del file index.js in modo che i dati siano accessibili nella funzione main.

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

Per indicizzare i dati nell'indice di ricerca, è ora necessario creare un SearchClient. Mentre SearchIndexClient viene usato per creare e gestire un indice, SearchClient viene usato per caricare i documenti ed eseguire query sull'indice.

Esistono due modi per creare un oggetto SearchClient. La prima opzione consiste nel creare un SearchClient da zero:

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

In alternativa, è possibile usare il metodo getSearchClient() di SearchIndexClient per creare SearchClient:

const searchClient = indexClient.getSearchClient(indexName);

Una volta definito il client, caricare i documenti nell'indice di ricerca. In questo caso si usa il metodo mergeOrUploadDocuments(), che carica i documenti o li unisce a un documento esistente, se esiste già un documento con la stessa chiave.

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

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

Eseguire la ricerca in un indice

Dopo aver creato un indice e aver caricato i documenti, è possibile iniziare a inviare query all'indice. In questa sezione si inviano cinque diverse query all'indice di ricerca per illustrare altrettante funzionalità di query disponibili.

Le query sono scritte in una funzione sendQueries() che viene chiamata nella funzione main come indicato di seguito:

await sendQueries(searchClient);

Le query vengono inviate usando il metodo search() di searchClient. Il primo parametro è il testo della ricerca, mentre il secondo parametro specifica le opzioni di ricerca.

Esempio di query 1

La prima query cerca *, che equivale alla ricerca di tutti gli elementi, e seleziona tre dei campi nell'indice. È consigliabile select solo i campi necessari, in quanto il pull di dati non necessari può aggiungere latenza alle query.

searchOptions per questa query contiene anche includeTotalCount impostato su true, che restituisce il numero di risultati corrispondenti trovati.

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
}

Anche le query rimanenti riportate di seguito dovrebbero essere aggiunte alla funzione sendQueries(). Qui sono separate per migliorare la leggibilità.

Esempio di query 2

Nella query successiva viene specificato il termine di ricerca "wifi" e viene incluso anche un filtro per restituire solo i risultati in cui lo stato è uguale a 'FL'. I risultati sono inoltre ordinati in base al Rating dell'hotel.

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

Esempio di query 3

La ricerca viene quindi limitata a un singolo campo ricercabile con il parametro searchFields. Questo approccio è un'ottima opzione per rendere la query più efficiente, se si è interessati solo alle corrispondenze in determinati campi.

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

Esempio di query 4

Un'altra opzione comune da includere in una query è facets. I facet consentono di creare filtri nell'interfaccia utente per consentire agli utenti di individuare più facilmente i valori che possono filtrare.

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

Esempio di query 5

La query finale usa il metodo getDocument() di searchClient. Questo consente di recuperare in modo efficiente un documento in base alla relativa chiave.

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

Riepilogo delle query

Le query precedenti mostrano più modi per trovare termini corrispondenti in una query: ricerca full-text, filtri e completamento automatico.

La ricerca full-text e i filtri vengono eseguiti usando il metodo searchClient.search. Una query di ricerca può essere passata nella stringa searchText, mentre un'espressione di filtro può essere passata nella proprietà filter della classe SearchOptions. Per filtrare senza eseguire ricerche, passare semplicemente "*" per il parametro searchText del metodo search. Per eseguire una ricerca senza filtrare, lasciare la proprietà filter non impostata oppure non passare un'istanza di SearchOptions.

In questa guida operativa rapida si usano PowerShell e le API REST di Ricerca AI di Azure per creare, caricare e interrogare un indice di ricerca per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa guida rapida utilizza dati di hotel fittizi dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

Configurare l'accesso

È possibile connettersi al servizio Ricerca intelligenza artificiale di Azure usando le chiavi API o Microsoft Entra ID con assegnazioni di ruolo. Le chiavi sono più facili da iniziare, ma i ruoli sono più sicuri.

Per configurare l'accesso basato sui ruoli consigliato:

  1. Accedere al portale di Azure e selezionare il servizio di ricerca.

  2. Nel riquadro sinistro selezionare Impostazioni>Chiavi.

  3. In Controllo di accesso API selezionare Entrambi.

    Questa opzione abilita sia l'autenticazione basata su chiave che l'autenticazione senza chiave. Dopo aver assegnato i ruoli, è possibile tornare a questo passaggio e selezionare Controllo degli accessi in base al ruolo.

  4. Nel riquadro sinistro selezionare Controllo di accesso (IAM).

  5. Seleziona Aggiungi>Aggiungi assegnazione ruolo.

  6. Assegnare i ruoli Collaboratore servizio di ricerca e Collaboratore ai dati dell'indice di ricerca all'account utente.

Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.

Ottenere l'endpoint

Nella sezione successiva si specifica l'endpoint seguente per stabilire una connessione al servizio Ricerca intelligenza artificiale di Azure. Questi passaggi presuppongono che sia stato configurato l'accesso in base al ruolo.

Per ottenere l'endpoint di servizio:

  1. Accedere al portale di Azure e selezionare il servizio di ricerca.

  2. Nel riquadro sinistro selezionare Panoramica.

  3. Prendere nota dell'URL, che dovrebbe essere simile a https://my-service.search.windows.net.

Prima di poter effettuare chiamate API REST al servizio Ricerca intelligenza artificiale di Azure, è necessario eseguire l'autenticazione e la connessione al servizio. Eseguire i passaggi seguenti in PowerShell, che supporta i comandi dell'interfaccia della riga di comando di Azure usati nei passaggi due e tre.

Per connettersi al servizio di ricerca:

  1. Nel sistema locale aprire PowerShell.

  2. Accedere alla sottoscrizione di Azure. Se si hanno più sottoscrizioni, selezionare quella che contiene il servizio di ricerca.

    az login
    
  3. Creare un $token oggetto per archiviare il token di accesso.

    $token = az account get-access-token --resource https://search.azure.com/ --query accessToken --output tsv
    
  4. Creare un $headers oggetto per archiviare il token e il tipo di contenuto.

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

    È necessario impostare l'intestazione una sola volta per sessione, ma è necessario aggiungerla a ogni richiesta.

  5. Creare un $url oggetto destinato alla collezione di indici nel servizio di ricerca. Sostituire <YOUR-SEARCH-SERVICE> con il valore ottenuto in Ottenere l'endpoint.

    $url = "<YOUR-SEARCH-SERVICE>/indexes?api-version=2024-07-01&`$select=name"
    
  6. Eseguire Invoke-RestMethod per inviare una richiesta GET al servizio di ricerca. Includere ConvertTo-Json per visualizzare le risposte dal servizio.

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

    Se il servizio è vuoto e non ha indici, la risposta è simile all'esempio seguente. In caso contrario, viene visualizzata una rappresentazione JSON delle definizioni degli indici.

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

Creare un indice di ricerca

Prima di aggiungere contenuto a Ricerca di intelligenza artificiale di Azure, è necessario creare un indice per definire la modalità di archiviazione e strutturazione del contenuto. Un indice è concettualmente simile a una tabella in un database relazionale, ma è progettato appositamente per le operazioni di ricerca, ad esempio la ricerca full-text.

Eseguire i comandi seguenti nella stessa sessione di PowerShell avviata nella sezione precedente.

Per creare un indice:

  1. Creare un $body oggetto per definire lo schema dell'indice.

    $body = @"
    {
        "name": "hotels-quickstart",  
        "fields": [
            {"name": "HotelId", "type": "Edm.String", "key": true, "filterable": true},
            {"name": "HotelName", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": true, "facetable": false},
            {"name": "Description", "type": "Edm.String", "searchable": true, "filterable": false, "sortable": false, "facetable": false, "analyzer": "en.lucene"},
            {"name": "Category", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
            {"name": "Tags", "type": "Collection(Edm.String)", "searchable": true, "filterable": true, "sortable": false, "facetable": true},
            {"name": "ParkingIncluded", "type": "Edm.Boolean", "filterable": true, "sortable": true, "facetable": true},
            {"name": "LastRenovationDate", "type": "Edm.DateTimeOffset", "filterable": true, "sortable": true, "facetable": true},
            {"name": "Rating", "type": "Edm.Double", "filterable": true, "sortable": true, "facetable": true},
            {"name": "Address", "type": "Edm.ComplexType", 
                "fields": [
                {"name": "StreetAddress", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "searchable": true},
                {"name": "City", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "StateProvince", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "PostalCode", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true},
                {"name": "Country", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "facetable": true}
            ]
         }
      ]
    }
    "@
    
  2. Aggiornare l'oggetto $url per specificare come destinazione il nuovo indice. Sostituire <YOUR-SEARCH-SERVICE> con il valore ottenuto in Ottenere l'endpoint.

    $url = "<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart?api-version=2024-07-01"
    
  3. Eseguire Invoke-RestMethod per creare l'indice nel servizio di ricerca.

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

    La risposta deve contenere la rappresentazione JSON dello schema dell'indice.

Informazioni sulla richiesta di creazione dell'indice

Questa guida introduttiva chiama Indexes - Create (API REST) per creare un indice di ricerca denominato hotels-quickstart e le relative strutture di dati fisiche nel servizio di ricerca.

All'interno dello schema dell'indice, la collezione fields definisce la struttura dei documenti dell'hotel. Ogni campo ha un name, dati type e attributi che ne determinano il comportamento durante l'indicizzazione e le query. Il HotelId campo è contrassegnato come chiave, che Ricerca di intelligenza artificiale di Azure richiede per identificare in modo univoco ogni documento in un indice.

Punti chiave sullo schema dell'indice:

  • Usare i campi stringa (Edm.String) per rendere i dati numerici ricercabili nel testo completo. Altri tipi di dati supportati, ad esempio Edm.Int32, sono filtrabili, ordinabili, visualizzabili e recuperabili, ma non sono disponibili per la ricerca.

  • La maggior parte dei campi sono tipi di dati semplici, ma è possibile definire tipi complessi per rappresentare dati annidati, ad esempio il Address campo.

  • Gli attributi di campo determinano le azioni consentite. Le API REST consentono numerose azioni per impostazione predefinita. Ad esempio, tutte le stringhe sono ricercabili e recuperabili. Con le API REST, è possibile usare attributi solo se è necessario disabilitare un comportamento.

Caricare l'indice

Gli indici appena creati sono vuoti. Per popolare un indice e renderlo ricercabile, è necessario caricare documenti JSON conformi allo schema dell'indice.

In Ricerca di intelligenza artificiale di Azure i documenti fungono da input per l'indicizzazione e gli output per le query. Per semplicità, questa guida introduttiva fornisce documenti di esempio relativi agli hotel come JSON in linea. Negli scenari di produzione, tuttavia, il contenuto viene spesso estratto da origini dati connesse e trasformato in JSON usando indicizzatori.

Per caricare documenti nell'indice:

  1. Creare un $body oggetto per archiviare il payload JSON di quattro documenti di esempio.

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

    $url = "<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs/index?api-version=2024-07-01"
    
  3. Eseguire Invoke-RestMethod per inviare la richiesta di caricamento al servizio di ricerca.

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

    La risposta deve contenere la chiave e lo stato di ogni documento caricato.

Informazioni sulla richiesta di caricamento

Questo avvio rapido richiama Documents - Index (API REST) per aggiungere quattro documenti di hotel di esempio all'indice. Rispetto alla richiesta precedente, l'URI viene esteso per includere la raccolta docs e l'operazione index.

Ogni documento nella value matrice rappresenta un hotel e contiene campi che corrispondono allo schema dell'indice. Il @search.action parametro specifica l'operazione da eseguire per ogni documento. Nell'esempio viene usato upload, che aggiunge il documento se non è presente o lo aggiorna se invece esiste.

Eseguire una query sull'indice

Ora che i documenti vengono caricati nell'indice, è possibile usare la ricerca full-text per trovare termini o frasi specifici all'interno dei relativi campi.

Per eseguire una query full-text sul tuo indice:

  1. Aggiornare l'oggetto $url per specificare i parametri di ricerca. Sostituire <YOUR-SEARCH-SERVICE> con il valore ottenuto in Ottenere l'endpoint.

    $url = '<YOUR-SEARCH-SERVICE>/indexes/hotels-quickstart/docs?api-version=2024-07-01&search=attached restaurant&searchFields=Description,Tags&$select=HotelId,HotelName,Tags,Description&$count=true'
    
  2. Eseguire Invoke-RestMethod per inviare la richiesta di query al servizio di ricerca.

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

    La risposta dovrebbe essere simile all'esempio seguente, che mostra un documento di hotel corrispondente, il relativo punteggio di pertinenza e i relativi campi selezionati.

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

Informazioni sulla richiesta di query

Questo quickstart chiama Documents - Search Post (API REST) per trovare documenti di hotel che soddisfano i tuoi criteri di ricerca. L'URI fa ancora riferimento alla raccolta docs, ma non include più l'operazione index.

Le richieste di ricerca full-text includono sempre un search parametro contenente il testo della query. Il testo della query può includere uno o più termini, frasi o operatori. Oltre a search, è possibile specificare altri parametri per perfezionare il comportamento e i risultati della ricerca.

La query cerca i termini "ristorante collegato" nei campi Description e Tags di ogni documento dell'hotel. Il $select parametro limita i campi restituiti nella risposta a HotelId, HotelNameTags, e Description. Il $count parametro richiede il numero totale di documenti corrispondenti.

Altri esempi di query

Eseguire i comandi seguenti per esplorare la sintassi della query. È possibile eseguire ricerche in stringhe, usare $filter espressioni, limitare i set di risultati, selezionare campi specifici e altro ancora. Ricordarsi di sostituire <YOUR-SEARCH-SERVICE> con il valore ottenuto in Ottenere l'endpoint.

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

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

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

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

In questa guida introduttiva si usa la libreria client Azure.Search.Documents per creare, caricare ed eseguire query su un indice di ricerca con dati di esempio per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa guida introduttiva usa i dati di hotel fittizi dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare ed eseguire un notebook completato.

Prerequisiti

Prerequisiti di Microsoft Entra ID

Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:

Recuperare informazioni sulla risorsa

Per autenticare l'applicazione con il servizio Azure AI Search, è necessario recuperare le informazioni seguenti:

Nome variabile valore
SEARCH_API_ENDPOINT Questo valore è disponibile nel portale di Azure. Selezionare il servizio di ricerca e quindi nel menu a sinistra selezionare Panoramica. Il valore URL in Informazioni di base è l'endpoint necessario. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

Altre informazioni sull'autenticazione senza chiave e sull'impostazione delle variabili di ambiente.

Configurare l'ambiente

Il codice di esempio viene eseguito in un notebook Jupyter. È quindi necessario configurare l'ambiente per eseguire notebook Jupyter.

  1. Scaricare o copiare il notebook di esempio da GitHub.

  2. Aprire il notebook in Visual Studio Code.

  3. Creare un nuovo ambiente Python da usare per installare i pacchetti necessari per questa esercitazione.

    Importante

    Non installare pacchetti nell'installazione globale di Python. Quando si installano i pacchetti Python, è necessario usare sempre un ambiente virtuale o Conda, altrimenti si rischia di interrompere l'installazione di Python nel sistema.

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

    La configurazione può richiedere alcuni minuti. Se si verificano problemi, vedere Ambienti Python in VS Code.

  4. Installare i notebook Jupyter e il kernel IPython per i notebook Jupyter, se non sono già disponibili.

    pip install jupyter
    pip install ipykernel
    python -m ipykernel install --user --name=.venv
    
  5. Selezionare il kernel del notebook.

    1. Nell'angolo in alto a destra del notebook selezionare Seleziona kernel.
    2. Se viene visualizzato .venv nell'elenco, selezionarlo. Se non viene visualizzato, selezionare Seleziona un altro kernel>Ambienti Python>.venv.

Creare, caricare ed eseguire query su un indice di ricerca

In questa sezione si aggiunge codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. Si esegue quindi il programma per visualizzare i risultati nella console. Per una spiegazione dettagliata del codice, vedere la sezione che illustra il codice.

  1. Assicurarsi che il notebook sia aperto nel kernel .venv, come descritto nella sezione precedente.

  2. Eseguire la prima cella di codice per installare i pacchetti necessari, tra cui azure-search-documents.

    ! pip install azure-search-documents==11.6.0b1 --quiet
    ! pip install azure-identity --quiet
    ! pip install python-dotenv --quiet
    
  3. Sostituire il contenuto della seconda cella di codice con il codice seguente a seconda del metodo di autenticazione.

    Note

    Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto 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. Rimuovere le due righe seguenti dalla cella di codice Create an index. Le credenziali sono già impostate nella cella di codice precedente.

    from azure.core.credentials import AzureKeyCredential
    credential = AzureKeyCredential(search_api_key)
    
  5. Eseguire la cella di codice Create an index per creare un indice di ricerca.

  6. Eseguire le celle di codice rimanenti in sequenza per caricare i documenti ed eseguire query.

Spiegazione del codice

Creare un indice

SearchIndexClient viene usato per creare e gestire indici per Azure AI Search. Ogni campo è identificato da un name e ha un type specificato.

Ogni campo dispone anche di una serie di attributi di indice che specificano se Azure AI Search può eseguire ricerche, applicare filtri, eseguire l'ordinamento e applicare facet nel campo. Quasi tutti i campi sono tipi di dati semplici, ma alcuni come AddressType sono tipi complessi che consentono di creare strutture di dati avanzate nell'indice. Per altre informazioni sui tipi di dati supportati e sugli attributi di indice descritti, vedere Creare un indice (REST).

Creare un payload di documenti e caricare documenti

Usare un'azione di indice per il tipo di operazione, ad esempio caricamento o merge e caricamento. I documenti provengono dall'esempio HotelsData su GitHub.

Eseguire la ricerca in un indice

È possibile ottenere risultati della query subito dopo l'indicizzazione del primo documento, ma per il test effettivo dell'indice è necessario attendere il completamento dell'indicizzazione di tutti i documenti.

Usare il metodo di ricerca della classe search.client.

Le query di esempio nel notebook sono:

  • Query di base: esegue una ricerca vuota (search=*), restituendo un elenco non classificato (punteggio di ricerca = 1,0) di documenti arbitrari. Non essendoci criteri, nei risultati vengono inclusi tutti i documenti.
  • Query di termine: aggiunge parole intere all'espressione di ricerca ("wifi"). Questa query specifica che i risultati devono contenere solo i campi inclusi nell'istruzione select. Limitando i campi restituiti, è possibile ridurre la quantità di dati inviati tramite rete e di conseguenza la latenza della ricerca.
  • Query filtrata: aggiunge un'espressione di filtro, in modo da restituire solo gli hotel con una valutazione maggiore di quattro, ordinati in ordine decrescente.
  • Ambito con campo: aggiunge search_fields per definire l’ambito di esecuzione di query in campi specifici.
  • Facet: genera facet per le corrispondenze positive trovate nei risultati della ricerca. Non esistono corrispondenze pari a zero. Se i risultati della ricerca non includono il termine wifi, wifi non viene visualizzato nella struttura di esplorazione in base a facet.
  • Ricerca in un documento: restituisce un documento in base alla relativa chiave. Questa operazione è utile se si vuole fornire il drill-through quando un utente seleziona un elemento in un risultato della ricerca.
  • Completamento automatico: fornisce potenziali corrispondenze quando l'utente digita nella casella di ricerca. Completamento automatico usa uno strumento suggerimenti (sg) per sapere quali campi contengono potenziali corrispondenze alle richieste dello strumento suggerimenti. In questo Avvio rapido, questi campi sono Tags, Address/City e Address/Country. Per simulare Completamento automatico passare le lettere sa come stringa parziale. Il metodo di completamento automatico di SearchClient restituisce le possibile corrispondenze del termine.

Rimuovere l'indice

Se l'indice non è più necessario, è possibile eliminarlo eseguendo la cella di codice Clean up. L'eliminazione di indici non necessari libera spazio per eseguire altre guide introduttive ed esercitazioni.

In questa guida introduttiva si usano le API REST di Azure AI Search per creare, caricare ed eseguire query su un indice di ricerca per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa Quickstart usa i dati di hotel fittizi dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

Configurare l'accesso

È possibile connettersi al servizio Ricerca intelligenza artificiale di Azure usando le chiavi API o Microsoft Entra ID con assegnazioni di ruolo. Le chiavi sono più facili da iniziare, ma i ruoli sono più sicuri.

Per configurare l'accesso basato sui ruoli consigliato:

  1. Accedere al portale di Azure e selezionare il servizio di ricerca.

  2. Nel riquadro sinistro selezionare Impostazioni>Chiavi.

  3. In Controllo di accesso API selezionare Entrambi.

    Questa opzione abilita sia l'autenticazione basata su chiave che l'autenticazione senza chiave. Dopo aver assegnato i ruoli, è possibile tornare a questo passaggio e selezionare Controllo degli accessi in base al ruolo.

  4. Nel riquadro sinistro selezionare Controllo di accesso (IAM).

  5. Seleziona Aggiungi>Aggiungi assegnazione ruolo.

  6. Assegnare i ruoli Collaboratore servizio di ricerca e Collaboratore ai dati dell'indice di ricerca all'account utente.

Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.

Ottenere endpoint e token

Nella sezione successiva specificare l'endpoint e il token seguenti per stabilire una connessione al servizio Ricerca intelligenza artificiale di Azure. Questi passaggi presuppongono che sia stato configurato l'accesso in base al ruolo.

Per ottenere l'endpoint di servizio e il token:

  1. Accedere al portale di Azure e selezionare il servizio di ricerca.

  2. Nel riquadro sinistro selezionare Panoramica.

  3. Prendere nota dell'URL, che dovrebbe essere simile a https://my-service.search.windows.net.

  4. Nel sistema locale aprire un terminale.

  5. Accedere alla sottoscrizione di Azure. Se si hanno più sottoscrizioni, selezionare quella che contiene il servizio di ricerca.

    az login
    
  6. Prendere nota del token Microsoft Entra.

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

Configura il tuo file

Prima di poter effettuare chiamate API REST al servizio Ricerca intelligenza artificiale di Azure, è necessario creare un file per archiviare l'endpoint del servizio, il token di autenticazione e le richieste finali. L'estensione client REST in Visual Studio Code supporta questa attività.

Per configurare il file di richiesta:

  1. Nel sistema locale aprire Visual Studio Code.

  2. Creare un file .rest o .http.

  3. Incollare i segnaposto e la richiesta seguenti nel file.

    @baseUrl = PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE
    @token = PUT-YOUR-PERSONAL-IDENTITY-TOKEN-HERE
    
    ### List existing indexes by name
    GET {{baseUrl}}/indexes?api-version=2024-07-01  HTTP/1.1
        Authorization: Bearer {{token}}
    
  4. Sostituire i segnaposto @baseUrl e @token con i valori ottenuti in Ottenere endpoint e token. Non includere virgolette.

  5. In ### List existing indexes by nameselezionare Invia richiesta.

    Una risposta dovrebbe essere visualizzata in un riquadro adiacente. Se sono presenti indici esistenti, vengono elencati. In caso contrario, l'elenco è vuoto. Se il codice HTTP è 200 OK, si è pronti per i passaggi successivi.

    Screenshot che mostra un client REST configurato per una richiesta del servizio di ricerca.

Creare un indice di ricerca

Prima di aggiungere contenuto a Ricerca di intelligenza artificiale di Azure, è necessario creare un indice per definire la modalità di archiviazione e strutturazione del contenuto. Un indice è concettualmente simile a una tabella in un database relazionale, ma è progettato appositamente per le operazioni di ricerca, ad esempio la ricerca full-text.

Per creare un indice:

  1. Incollare la richiesta seguente nel file.

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

    Si dovrebbe ricevere una HTTP/1.1 201 Created risposta il cui corpo contiene la rappresentazione JSON dello schema dell'indice.

Informazioni sulla richiesta di creazione dell'indice

Questa guida introduttiva chiama Indexes - Create (API REST) per creare un indice di ricerca denominato hotels-quickstart e le relative strutture di dati fisiche nel servizio di ricerca.

All'interno dello schema dell'indice, la raccolta fields definisce la struttura dei documenti relativi agli hotel. Ogni campo ha un name, dati type e degli attributi che ne determinano il comportamento durante l'indicizzazione e le query. Il HotelId campo è contrassegnato come chiave, che Ricerca di intelligenza artificiale di Azure richiede per identificare in modo univoco ogni documento in un indice.

Punti chiave sullo schema dell'indice:

  • Usare i campi stringa (Edm.String) per rendere i dati numerici ricercabili in modalità full-text. Altri tipi di dati supportati, ad esempio Edm.Int32, sono filtrabili, ordinabili, visualizzabili e recuperabili, ma non sono disponibili per la ricerca.

  • La maggior parte dei campi sono tipi di dati semplici, ma è possibile definire tipi complessi per rappresentare dati annidati, ad esempio il Address campo.

  • Gli attributi di campo determinano le azioni consentite. Le API REST consentono numerose azioni per impostazione predefinita. Ad esempio, tutte le stringhe sono ricercabili e recuperabili. Con le API REST, è possibile usare attributi solo se è necessario disabilitare un comportamento.

Caricare l'indice

Gli indici appena creati sono vuoti. Per popolare un indice e renderlo ricercabile, è necessario caricare documenti JSON conformi allo schema dell'indice.

In Ricerca di intelligenza artificiale di Azure i documenti fungono da input per l'indicizzazione e gli output per le query. Per semplicità, questa guida introduttiva fornisce documenti relativi agli hotel di esempio come JSON inline. Negli scenari di produzione, tuttavia, il contenuto viene spesso estratto da origini dati connesse e trasformato in JSON usando indicizzatori.

Per caricare documenti nell'indice:

  1. Incollare la richiesta seguente nel file.

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

    Si dovrebbe ricevere una HTTP/1.1 200 OK risposta il cui corpo contiene la chiave e lo stato di ogni documento caricato.

Informazioni sulla richiesta di caricamento

Questa guida introduttiva chiama Documents - Index (API REST) per aggiungere quattro documenti relativi agli hotel di esempio all'indice. Rispetto alla richiesta precedente, l'URI viene esteso per includere la raccolta docs e l'operazione index.

Ogni documento nella value matrice rappresenta un hotel e contiene campi che corrispondono allo schema dell'indice. Il @search.action parametro specifica l'operazione da eseguire per ogni documento. Nel nostro esempio viene utilizzato upload, che aggiunge il documento se non esiste o aggiorna il documento se esiste.

Eseguire una query sull'indice

Ora che i documenti vengono caricati nell'indice, è possibile usare la ricerca full-text per trovare termini o frasi specifici all'interno dei relativi campi.

Per eseguire una query full-text sul tuo indice:

  1. Incollare la richiesta seguente nel file.

    ### Run a query
    POST {{baseUrl}}/indexes/hotels-quickstart/docs/search?api-version=2024-07-01  HTTP/1.1
      Content-Type: application/json
      Authorization: Bearer {{token}}
    
      {
          "search": "attached restaurant",
          "select": "HotelId, HotelName, Tags, Description",
          "searchFields": "Description, Tags",
          "count": true
      }
    
  2. In ### Run a queryselezionare Invia richiesta.

    Si dovrebbe ricevere una HTTP/1.1 200 OK risposta simile all'esempio seguente, che mostra un documento di hotel corrispondente, il relativo punteggio di pertinenza e i relativi campi selezionati.

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

Informazioni sulla richiesta di query

Questa procedura rapida chiama Documents - Search Post (API REST) per trovare documenti degli hotel che soddisfano i criteri di ricerca. L'URI ora è destinato all'operazione /docs/search .

Le richieste di ricerca full-text includono sempre un search parametro contenente il testo della query. Il testo della query può includere uno o più termini, frasi o operatori. Oltre a search, è possibile specificare altri parametri per perfezionare il comportamento e i risultati della ricerca.

La query cerca i termini "ristorante collegato" nei campi Description e Tags di ogni documento dell'hotel. Il select parametro limita i campi restituiti nella risposta a HotelId, HotelNameTags, e Description. Il count parametro richiede il numero totale di documenti corrispondenti.

In questa guida introduttiva si usa la libreria client Azure.Search.Documents per creare, caricare ed eseguire query su un indice di ricerca con dati di esempio per la ricerca full-text. La ricerca full-text usa Apache Lucene per l'indicizzazione e le query e l'algoritmo di classificazione BM25 per l'assegnazione dei punteggi.

Questa Quickstart usa i dati di hotel fittizi dal repository azure-search-sample-data per popolare l'indice.

Suggerimento

È possibile scaricare il codice sorgente per iniziare con un progetto completato o seguire questa procedura per creare un progetto personalizzato.

Prerequisiti

  • Un account Azure con una sottoscrizione attiva. Creare un account gratuito.
  • Un servizio di Azure AI Search. Creare un servizio se non ne è disponibile uno. Per questa guida introduttiva, è possibile usare un servizio gratuito.

Prerequisiti di Microsoft Entra ID

Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:

Recuperare informazioni sulla risorsa

Per autenticare l'applicazione con il servizio Azure AI Search, è necessario recuperare le informazioni seguenti:

Nome variabile valore
SEARCH_API_ENDPOINT Questo valore è disponibile nel portale di Azure. Selezionare il servizio di ricerca e quindi nel menu a sinistra selezionare Panoramica. Il valore URL in Informazioni di base è l'endpoint necessario. Un endpoint di esempio potrebbe essere simile a https://mydemo.search.windows.net.

Altre informazioni sull'autenticazione senza chiave e sull'impostazione delle variabili di ambiente.

Configurare

  1. Creare una nuova cartella full-text-quickstart per contenere l'applicazione e aprire Visual Studio Code in tale cartella con il comando seguente:

    mkdir full-text-quickstart && cd full-text-quickstart
    
  2. Creare package.json con il comando seguente:

    npm init -y
    
  3. Aggiornare package.json in ECMAScript con il comando seguente:

    npm pkg set type=module
    
  4. Installare la libreria client di Azure AI Search (Azure.Search.Documents) per JavaScript con:

    npm install @azure/search-documents
    
  5. Per l'autenticazione senza password consigliata, installare la libreria client di Identità di Azure con:

    npm install @azure/identity
    

Creare, caricare ed eseguire query su un indice di ricerca

Nella sezione di configurazione precedente è stata eseguita l'installazione della libreria client di Azure AI Search e di altre dipendenze.

In questa sezione si aggiunge codice per creare un indice di ricerca, caricarlo con documenti ed eseguire query. Si esegue quindi il programma per visualizzare i risultati nella console. Per una spiegazione dettagliata del codice, vedere la sezione che illustra il codice.

Il codice di esempio in questa guida introduttiva usa Microsoft Entra ID per l'autenticazione senza chiave consigliata. Se si preferisce usare una chiave API, è possibile sostituire l'oggetto DefaultAzureCredential con un oggetto AzureKeyCredential.

const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
const credential = new DefaultAzureCredential();
  1. Creare un nuovo file denominato index.ts e incollare il codice seguente in 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": "Category",
    			"type": "Edm.String" as SearchFieldDataType,
    			"searchable": true,
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Tags",
    			"type": "Collection(Edm.String)",
    			"searchable": true,
    			"filterable": true,
    			"sortable": false,
    			"facetable": true
    		},
    		{
    			"name": "ParkingIncluded",
    			"type": "Edm.Boolean",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "LastRenovationDate",
    			"type": "Edm.DateTimeOffset",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Rating",
    			"type": "Edm.Double",
    			"filterable": true,
    			"sortable": true,
    			"facetable": true
    		},
    		{
    			"name": "Address",
    			"type": "Edm.ComplexType",
    			"fields": [
    				{
    					"name": "StreetAddress",
    					"type": "Edm.String" as SearchFieldDataType,
    					"filterable": false,
    					"sortable": false,
    					"facetable": false,
    					"searchable": true
    				},
    				{
    					"name": "City",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "StateProvince",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "PostalCode",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				},
    				{
    					"name": "Country",
    					"type": "Edm.String" as SearchFieldDataType,
    					"searchable": true,
    					"filterable": true,
    					"sortable": true,
    					"facetable": true
    				}
    			]
    		}
    	],
    	"suggesters": [
    		{
    			"name": "sg",
    			"searchMode": "analyzingInfixMatching",
    			"sourceFields": [
    				"HotelName"
    			]
    		}
    	]
    };
    
    async function main() {
    
    	// Your search service endpoint
    	const searchServiceEndpoint = "https://<Put your search service NAME here>.search.windows.net/";
    
    	// Use the recommended keyless credential instead of the AzureKeyCredential credential.
    	const credential = new DefaultAzureCredential();
    	//const credential = new AzureKeyCredential(Your search service admin key);
    
    	// Create a SearchIndexClient to send create/delete index commands
    	const searchIndexClient: SearchIndexClient = new SearchIndexClient(
    		searchServiceEndpoint,
    		credential
    	);
    
    	// Creating a search client to upload documents and issue queries
    	const indexName: string  = "hotels-quickstart";
        const searchClient: SearchClient<any> = searchIndexClient.getSearchClient(indexName);
    
        console.log('Checking if index exists...');
        await deleteIndexIfExists(searchIndexClient, indexName);
    
        console.log('Creating index...');
        let index: SearchIndex = await searchIndexClient.createIndex(indexDefinition);
        console.log(`Index named ${index.name} has been created.`);
    
        console.log('Uploading documents...');
        let indexDocumentsResult = await searchClient.mergeOrUploadDocuments(hotelData['value']);
        console.log(`Index operations succeeded: ${JSON.stringify(indexDocumentsResult.results[0].succeeded)} `);
    
        // waiting one second for indexing to complete (for demo purposes only)
        sleep(1000);
    
        console.log('Querying the index...');
        console.log();
        await sendQueries(searchClient);
    }
    
    async function deleteIndexIfExists(searchIndexClient: SearchIndexClient, indexName: string) {
        try {
            await searchIndexClient.deleteIndex(indexName);
            console.log('Deleting index...');
        } catch {
            console.log('Index does not exist yet.');
        }
    }
    
    async function sendQueries(searchClient: SearchClient<any>) {
        // Query 1
        console.log('Query #1 - search everything:');
        let searchOptions: any = {
            includeTotalCount: true,
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        let searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log(`Result count: ${searchResults.count}`);
        console.log();
    
    
        // Query 2
        console.log('Query #2 - search with filter, orderBy, and select:');
        let state = 'FL';
        searchOptions = {
            filter: odata`Address/StateProvince eq ${state}`,
            orderBy: ["Rating desc"],
            select: ["HotelId", "HotelName", "Rating"]
        };
    
        searchResults = await searchClient.search("wifi", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 3
        console.log('Query #3 - limit searchFields:');
        searchOptions = {
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("sublime palace", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 4
        console.log('Query #4 - limit searchFields and use facets:');
        searchOptions = {
            facets: ["Category"],
            select: ["HotelId", "HotelName", "Rating"],
            searchFields: ["HotelName"]
        };
    
        searchResults = await searchClient.search("*", searchOptions);
        for await (const result of searchResults.results) {
            console.log(`${JSON.stringify(result.document)}`);
        }
        console.log();
    
        // Query 5
        console.log('Query #5 - Lookup document:');
        let documentResult = await searchClient.getDocument('3');
        console.log(`HotelId: ${documentResult.HotelId}; HotelName: ${documentResult.HotelName}`);
        console.log();
    }
    
    function sleep(ms: number) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    main().catch((err) => {
        console.error("The sample encountered an error:", err);
    });
    
  2. Creare un file denominato hotels.json e incollare il codice seguente in hotels.json:

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

    tsc
    
  5. Accedere ad Azure con il comando seguente:

    az login
    
  6. Eseguire il codice JavaScript con il comando seguente:

    node index.js
    

Spiegazione del codice

Creare un indice

Creare un file hotels_quickstart_index.json. Questo file definisce il funzionamento di Azure AI Search con i documenti che caricati nel passaggio successivo. Ogni campo è identificato da un name e ha un type specificato. Ogni campo dispone anche di una serie di attributi di indice che specificano se Azure AI Search può eseguire ricerche, applicare filtri, eseguire l'ordinamento e applicare facet nel campo. Quasi tutti i campi sono tipi di dati semplici, ma alcuni come AddressType sono tipi complessi che consentono di creare strutture di dati avanzate nell'indice. Per altre informazioni sui tipi di dati supportati e sugli attributi di indice descritti, vedere Creare un indice (REST).

Si vuole importare hotels_quickstart_index.json in modo che la funzione main possa accedere alla definizione dell'indice.

import indexDefinition from './hotels_quickstart_index.json';

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

All'interno della funzione main creare quindi un SearchIndexClient, che viene usato per creare e gestire gli indici per Azure AI Search.

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

A questo punto eliminare l'indice, se esiste già. Questa operazione è una procedura comune per il codice di test/demo.

A questo scopo occorre definire una funzione semplice che tenta di eliminare l'indice.

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

Per eseguire la funzione, estrarre il nome dell'indice dalla definizione dell'indice e quindi passare indexName insieme a indexClient alla funzione deleteIndexIfExists().

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

A questo punto è possibile creare l'indice con il metodo createIndex().

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

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

Caricare i documenti

In Azure AI Search i documenti sono strutture dei dati che costituiscono sia l'input per l'indicizzazione che l'output restituito dalle query. È possibile eseguire il push di questi dati nell'indice oppure usare un indicizzatore. In questo caso, i documenti verranno inseriti nell'indice tramite programmazione.

Gli input dei documenti possono essere righe in un database, BLOB nell'archiviazione BLOB o, come in questo esempio, documenti JSON nel disco. È possibile scaricare hotels.json o creare il proprio file hotels.json con il contenuto seguente:

Analogamente a quanto è stato fatto con indexDefinition, occorre anche importare hotels.json all'inizio del file index.ts in modo che i dati siano accessibili nella funzione main.

import hotelData from './hotels.json';

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

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

Per indicizzare i dati nell'indice di ricerca, è ora necessario creare un SearchClient. Mentre SearchIndexClient viene usato per creare e gestire un indice, SearchClient viene usato per caricare i documenti ed eseguire query sull'indice.

Esistono due modi per creare un oggetto SearchClient. La prima opzione consiste nel creare un SearchClient da zero:

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

In alternativa, è possibile usare il metodo getSearchClient() di SearchIndexClient per creare SearchClient:

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

Una volta definito il client, caricare i documenti nell'indice di ricerca. In questo caso si usa il metodo mergeOrUploadDocuments(), che carica i documenti o li unisce a un documento esistente, se esiste già un documento con la stessa chiave. Verificare quindi che l'operazione sia andata a buon fine perché almeno il primo documento esiste.

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

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

Eseguire di nuovo il programma con tsc && node index.ts. Verrà visualizzato un set di messaggi leggermente diverso rispetto a quanto visualizzato nel passaggio 1. Questa volta l'indice esiste e dovrebbe quindi essere visualizzato un messaggio relativo alla sua eliminazione prima che l'app crei il nuovo indice e vi inserisca i dati.

Prima di eseguire le query nel passaggio successivo, occorre definire una funzione che indichi al programma di attendere un secondo. Questa operazione viene eseguita solo a scopo di test/dimostrativo per assicurarsi che l'indicizzazione venga completata e che i documenti siano disponibili nell'indice per le query.

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

Per indicare al programma di attendere un secondo, chiamare la funzione sleep:

sleep(1000);

Eseguire la ricerca in un indice

Dopo aver creato un indice e aver caricato i documenti, è possibile iniziare a inviare query all'indice. In questa sezione si inviano cinque diverse query all'indice di ricerca per illustrare altrettante funzionalità di query disponibili.

Le query sono scritte in una funzione sendQueries() che viene chiamata nella funzione main come indicato di seguito:

await sendQueries(searchClient);

Le query vengono inviate usando il metodo search() di searchClient. Il primo parametro è il testo della ricerca, mentre il secondo parametro specifica le opzioni di ricerca.

Esempio di query 1

La prima query cerca *, che equivale alla ricerca di tutti gli elementi, e seleziona tre dei campi nell'indice. È consigliabile select solo i campi necessari, in quanto il pull di dati non necessari può aggiungere latenza alle query.

searchOptions per questa query contiene anche includeTotalCount impostato su true, che restituirà il numero di risultati corrispondenti trovati.

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
}

Anche le query rimanenti riportate di seguito dovrebbero essere aggiunte alla funzione sendQueries(). Qui sono separate per migliorare la leggibilità.

Esempio di query 2

Nella query successiva viene specificato il termine di ricerca "wifi" e viene incluso anche un filtro per restituire solo i risultati in cui lo stato è uguale a 'FL'. I risultati sono inoltre ordinati in base al Rating dell'hotel.

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

Esempio di query 3

La ricerca viene quindi limitata a un singolo campo ricercabile con il parametro searchFields. Questo approccio è un'ottima opzione per rendere la query più efficiente, se si è interessati solo alle corrispondenze in determinati campi.

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

Esempio di query 4

Un'altra opzione comune da includere in una query è facets. I facet consentono di fornire il drill-down autoindirizzato dai risultati nella propria interfaccia utente. I risultati dei facet possono essere trasformati in caselle di controllo nel riquadro dei risultati.

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

Esempio di query 5

La query finale usa il metodo getDocument() di searchClient. Questo consente di recuperare in modo efficiente un documento in base alla relativa chiave.

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

Riepilogo delle query

Le query precedenti mostrano più modi per trovare termini corrispondenti in una query: ricerca full-text, filtri e completamento automatico.

La ricerca full-text e i filtri vengono eseguiti usando il metodo searchClient.search. Una query di ricerca può essere passata nella stringa searchText, mentre un'espressione di filtro può essere passata nella proprietà filter della classe SearchOptions. Per filtrare senza eseguire ricerche, passare semplicemente "*" per il parametro searchText del metodo search. Per eseguire una ricerca senza filtrare, lasciare la proprietà filter non impostata oppure non passare un'istanza di SearchOptions.

Pulire le risorse

Quando si lavora nel proprio abbonamento, è consigliabile completare un progetto determinando se sono ancora necessarie le risorse create. Le risorse lasciate in esecuzione possono avere un costo. È possibile eliminare le risorse singolarmente oppure eliminare il gruppo di risorse per eliminare l'intero set di risorse.

Nel portale di Azure è possibile trovare e gestire le risorse selezionando Tutte le risorse o Gruppi di risorse nel riquadro sinistro.

Se si usa un servizio gratuito, tenere presente che il numero di indicizzatori e origini dati è limitato a tre. È possibile eliminare singoli elementi nel portale di Azure per rimanere al di sotto del limite.