Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
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:
Installare l'interfaccia della riga di comando di Azure.
Assegna i ruoli
Search Service Contributor
eSearch Index Data Contributor
al tuo account utente. È possibile assegnare ruoli nel portale di Azure in Controllo di accesso (IAM)>Aggiungi un'assegnazione di ruolo. Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.
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
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
Creare una nuova applicazione console con il comando seguente:
dotnet new console
Installare la libreria client di Azure AI Search (Azure.Search.Documents) per .NET con:
dotnet add package Azure.Search.Documents
Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, installare il pacchetto Azure.Identity con:
dotnet add package Azure.Identity
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();
In Program.cs copiare e incollare il codice seguente. Modificare le variabili
serviceName
eapiKey
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(); } } }
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; } } }
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; } } }
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; } } }
Creare un nuovo file denominato Hotel.Methods.cs e incollare il codice seguente per definire un override
ToString()
per la classeHotel
.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(); } } }
Creare un nuovo file denominato Address.Methods.cs e incollare il codice seguente per definire un override
ToString()
per la classeAddress
.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); } }
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:
- 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
.
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 oggettoSimpleField
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 esempioIsKey = 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:
Installare l'interfaccia della riga di comando di Azure.
Assegna i ruoli
Search Service Contributor
eSearch Index Data Contributor
al tuo account utente. È possibile assegnare ruoli nel portale di Azure in Controllo di accesso (IAM)>Aggiungi un'assegnazione di ruolo. Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.
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.
Installare Apache Maven. Quindi eseguire
mvn -v
per confermare l'installazione corretta.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>
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
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();
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")); } }
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 ""; } } }
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; }
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:
Installare l'interfaccia della riga di comando di Azure.
Assegna il ruolo
Search Service Contributor
e il ruoloSearch Index Data Contributor
al tuo account utente. È possibile assegnare ruoli nel portale di Azure in Controllo di accesso (IAM)>Aggiungi un'assegnazione di ruolo. Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.
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
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
Creare
package.json
con il comando seguente:npm init -y
Installare la libreria client di Azure AI Search (Azure.Search.Documents) per JavaScript con:
npm install @azure/search-documents
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();
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); });
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" } } ] }
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" ] } ] }
Accedere ad Azure con il comando seguente:
az login
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
Un account Azure con una sottoscrizione attiva. Creare un account gratuito.
Un servizio di Azure AI Search. Creare un servizio o trovare un servizio esistente nella sottoscrizione corrente. Per questa guida introduttiva, è possibile usare un servizio gratuito.
Interfaccia della riga di comando di Azure per l'autenticazione senza chiave con MICROSOFT Entra ID.
PowerShell 7.3 o versione successiva. Questa guida introduttiva usa Invoke-RestMethod per effettuare chiamate API REST.
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:
Accedere al portale di Azure e selezionare il servizio di ricerca.
Nel riquadro sinistro selezionare Impostazioni>Chiavi.
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.
Nel riquadro sinistro selezionare Controllo di accesso (IAM).
Seleziona Aggiungi>Aggiungi assegnazione ruolo.
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:
Accedere al portale di Azure e selezionare il servizio di ricerca.
Nel riquadro sinistro selezionare Panoramica.
Prendere nota dell'URL, che dovrebbe essere simile a
https://my-service.search.windows.net
.
Connettersi ad Azure AI Search
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:
Nel sistema locale aprire PowerShell.
Accedere alla sottoscrizione di Azure. Se si hanno più sottoscrizioni, selezionare quella che contiene il servizio di ricerca.
az login
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
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.
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"
Eseguire
Invoke-RestMethod
per inviare una richiesta GET al servizio di ricerca. IncludereConvertTo-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:
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} ] } ] } "@
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"
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 esempioEdm.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:
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" } } ] } "@
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"
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:
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'
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
, HotelName
Tags
, 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
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.
Visual Studio Code con l'estensione Python o un IDE equivalente con Python 3.10 o versione successiva. Se non è stata installata una versione appropriata di Python, seguire le istruzioni riportate nell'esercitazione su Python per VS Code.
Prerequisiti di Microsoft Entra ID
Per l'autenticazione senza chiave consigliata con Microsoft Entra ID, è necessario:
Installare l'interfaccia della riga di comando di Azure.
Assegna i ruoli
Search Service Contributor
eSearch Index Data Contributor
all'account utente. È possibile assegnare ruoli nel portale di Azure in Controllo di accesso (IAM)>Aggiungi un'assegnazione di ruolo. Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.
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.
Scaricare o copiare il notebook di esempio da GitHub.
Aprire il notebook in Visual Studio Code.
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.
La configurazione può richiedere alcuni minuti. Se si verificano problemi, vedere Ambienti Python in VS Code.
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
Selezionare il kernel del notebook.
- Nell'angolo in alto a destra del notebook selezionare Seleziona kernel.
- 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.
Assicurarsi che il notebook sia aperto nel kernel
.venv
, come descritto nella sezione precedente.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
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 oggettoAzureKeyCredential
.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"
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)
Eseguire la cella di codice Create an index per creare un indice di ricerca.
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 sonoTags
,Address/City
eAddress/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
Un account Azure con una sottoscrizione attiva. Creare un account gratuito.
Un servizio di Azure AI Search. Creare un servizio o trovare un servizio esistente nella sottoscrizione corrente. Per questa guida introduttiva, è possibile usare un servizio gratuito.
Interfaccia della riga di comando di Azure per l'autenticazione senza chiave con MICROSOFT Entra ID.
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:
Accedere al portale di Azure e selezionare il servizio di ricerca.
Nel riquadro sinistro selezionare Impostazioni>Chiavi.
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.
Nel riquadro sinistro selezionare Controllo di accesso (IAM).
Seleziona Aggiungi>Aggiungi assegnazione ruolo.
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:
Accedere al portale di Azure e selezionare il servizio di ricerca.
Nel riquadro sinistro selezionare Panoramica.
Prendere nota dell'URL, che dovrebbe essere simile a
https://my-service.search.windows.net
.Nel sistema locale aprire un terminale.
Accedere alla sottoscrizione di Azure. Se si hanno più sottoscrizioni, selezionare quella che contiene il servizio di ricerca.
az login
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:
Nel sistema locale aprire Visual Studio Code.
Creare un file
.rest
o.http
.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}}
Sostituire i segnaposto
@baseUrl
e@token
con i valori ottenuti in Ottenere endpoint e token. Non includere virgolette.In
### List existing indexes by name
selezionare 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.
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:
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} ] } ] }
In
### Create a new index
selezionare 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 esempioEdm.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:
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" } } ] }
In
### Upload documents
selezionare 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:
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 }
In
### Run a query
selezionare 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
, HotelName
Tags
, 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:
Installare l'interfaccia della riga di comando di Azure.
Assegna i ruoli
Search Service Contributor
eSearch Index Data Contributor
all'account utente. È possibile assegnare ruoli nel portale di Azure in Controllo di accesso (IAM)>Aggiungi un'assegnazione di ruolo. Per altre informazioni, vedere Connettersi ad Azure AI Search usando i ruoli.
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
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
Creare
package.json
con il comando seguente:npm init -y
Aggiornare
package.json
in ECMAScript con il comando seguente:npm pkg set type=module
Installare la libreria client di Azure AI Search (Azure.Search.Documents) per JavaScript con:
npm install @azure/search-documents
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();
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); });
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" } } ] }
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"] }
Transpile da TypeScript a JavaScript.
tsc
Accedere ad Azure con il comando seguente:
az login
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.