Come usare Azure.Search.Documents in un'applicazione .NET
Questo articolo illustra come creare e gestire oggetti di ricerca usando C# e la libreria client Azure.Search.Documents (versione 11) in Azure SDK per .NET.
Informazioni sulla versione 11
Azure SDK per .NET include una libreria client Azure.Search.Documents del team di Azure SDK equivalente funzionalmente alla libreria client precedente, Microsoft.Azure.Search. La versione 11 è più coerente in merito alla programmabilità di Azure. Alcuni esempi includono l'autenticazione della chiave AzureKeyCredential
e System.Text.Json.Serialization per la serializzazione JSON.
Come per le versioni precedenti, è possibile usare questa libreria per:
- Creare e gestire indici di ricerca, origini dati, indicizzatori, set di competenze e mappe sinonimiche
- Caricare e gestire i documenti di ricerca in un indice
- Eseguire query, senza dover gestire i dettagli di HTTP e JSON
- Richiamare e gestire l'arricchimento tramite intelligenza artificiale (set di competenze) e gli output
La libreria viene distribuita come singolo pacchetto NuGet che include tutte le API usate per l'accesso a livello di codice a un servizio di ricerca.
La libreria client definisce classi come SearchIndex
, SearchField
, e SearchDocument
, nonché operazioni quali SearchIndexClient.CreateIndex
e SearchClient.Search
sulle classi SearchIndexClient
e SearchClient
. Le classi sono organizzate negli spazi dei nomi seguenti:
Azure.Search.Documents
Azure.Search.Documents.Indexes
Azure.Search.Documents.Indexes.Models
Azure.Search.Documents.Models
La versione 11 è destinata alla specifica del servizio di ricerca 2020-06-30.
La libreria client non fornisce operazioni di gestione dei servizi, come la creazione e la scalabilità di servizi di ricerca e la gestione delle chiavi API. Se è necessario gestire le risorse di ricerca da un'applicazione .NET, utilizzare la libreria Microsoft.Azure.Management.Search in Azure SDK per .NET.
Eseguire l'aggiornamento passando alla versione 11
Se si usa la versione precedente di .NET SDK e si vuole eseguire l'aggiornamento alla versione corrente disponibile a livello generale, vedere Eseguire l'aggiornamento a .NET SDK di Ricerca di intelligenza artificiale di Azure versione 11.
Requisiti dell'SDK
Visual Studio 2019 o versione successiva.
Un servizio di Azure AI Search. Per usare l'SDK, è necessario il nome del servizio e una o più chiavi API. Creare un servizio nel portale, se non ne è disponibile uno.
Scaricare il pacchetto NuGet usando Strumenti>NuGet Gestione pacchetti> Gestisci pacchetti NuGet per la soluzione in Visual Studio. Cercare il nome del pacchetto
Azure.Search.Documents
.
Azure SDK per .NET è conforme a .NET Standard 2.0.
Applicazione di esempio
Questo articolo illustra, ad esempio, l'esempio di codice DotNetHowTo in GitHub per illustrare i concetti fondamentali in Ricerca di intelligenza artificiale di Azure e come creare, caricare ed eseguire query su un indice di ricerca.
Per il resto di questo articolo, si supponga che un nuovo indice denominato hotels, popolato con alcuni documenti, con diverse query corrispondenti ai risultati.
L'esempio seguente illustra il programma principale, con il flusso complessivo:
// This sample shows how to delete, create, upload documents and query an index
static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
IConfigurationRoot configuration = builder.Build();
SearchIndexClient indexClient = CreateSearchIndexClient(configuration);
string indexName = configuration["SearchIndexName"];
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, indexClient);
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, indexClient);
SearchClient searchClient = indexClient.GetSearchClient(indexName);
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(searchClient);
SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);
Console.WriteLine("{0}", "Run queries...\n");
RunQueries(indexClientForQueries);
Console.WriteLine("{0}", "Complete. Press any key to end application...\n");
Console.ReadKey();
}
Di seguito è riportato uno screenshot parziale dell'output, presupponendo che l'applicazione venga eseguita con un nome di servizio e chiavi API validi:
Tipi di client
La libreria client usa tre tipi di client per diverse operazioni: SearchIndexClient
per creare, aggiornare o eliminare indici, SearchClient
per caricare o eseguire query su un indice e SearchIndexerClient
per lavorare con indicizzatori e set di competenze. Questo articolo si concentra sulle prime due.
Come minimo, tutti i client richiedono il nome del servizio o l'endpoint e una chiave API. È comune fornire queste informazioni in un file di configurazione, in modo simile a quello presente nel file di appsettings.json dell'applicazione di esempio DotNetHowTo. Per leggere dal file di configurazione, aggiungere using Microsoft.Extensions.Configuration;
al programma.
L'istruzione seguente crea il client di indice usato per creare, aggiornare o eliminare indici. Accetta un endpoint di servizio e una chiave API di amministrazione.
private static SearchIndexClient CreateSearchIndexClient(IConfigurationRoot configuration)
{
string searchServiceEndPoint = configuration["YourSearchServiceEndPoint"];
string adminApiKey = configuration["YourSearchServiceAdminApiKey"];
SearchIndexClient indexClient = new SearchIndexClient(new Uri(searchServiceEndPoint), new AzureKeyCredential(adminApiKey));
return indexClient;
}
L'istruzione successiva crea il client di ricerca usato per caricare documenti o eseguire query. SearchClient
richiede un indice. Per caricare i documenti è necessaria una chiave API amministratore, ma è possibile usare una chiave API di query per eseguire query.
string indexName = configuration["SearchIndexName"];
private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
string searchServiceEndPoint = configuration["YourSearchServiceEndPoint"];
string queryApiKey = configuration["YourSearchServiceQueryApiKey"];
SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
return searchClient;
}
Nota
Se si specifica una chiave non valida per l'operazione di importazione, ad esempio una chiave di query in cui è necessaria una chiave di amministrazione, viene generata un'eccezione SearchClient
CloudException
con il messaggio di errore Non consentito la prima volta che si chiama un metodo di operazione. In questo caso, eseguire una doppia verifica della chiave API.
Eliminare l'indice
Nelle prime fasi di sviluppo potrebbe essere necessario includere un'istruzione DeleteIndex
per eliminare un indice in fase di creazione, in modo da poterlo ricreare con una definizione aggiornata. Il codice di esempio per Azure AI Search spesso include un passaggio di eliminazione in modo da poter rieseguire l'esempio.
La riga seguente chiama DeleteIndexIfExists
:
Console.WriteLine("{0}", "Deleting index...\n");
DeleteIndexIfExists(indexName, indexClient);
Questo metodo utilizza il SearchIndexClient
fornito per verificare l'esistenza dell'indice ed eventualmente eliminarlo:
private static void DeleteIndexIfExists(string indexName, SearchIndexClient indexClient)
{
try
{
if (indexClient.GetIndex(indexName) != null)
{
indexClient.DeleteIndex(indexName);
}
}
catch (RequestFailedException e) when (e.Status == 404)
{
// Throw an exception if the index name isn't found
Console.WriteLine("The index doesn't exist. No deletion occurred.");
Nota
Il codice di esempio in questo articolo usa i metodi sincroni per semplicità, ma è consigliabile usare i metodi asincroni nelle proprie applicazioni per mantenerli scalabili e reattivi. Nel metodo precedente, ad esempio, è possibile usare DeleteIndexAsync
anziché DeleteIndex
.
Creare un indice
È possibile usare SearchIndexClient
per creare un indice.
Il metodo seguente crea un nuovo SearchIndex
oggetto con un elenco di SearchField
oggetti che definiscono lo schema del nuovo indice. Ogni campo ha un nome, un tipo di dati e diversi attributi che definiscono il comportamento della ricerca.
I campi possono essere definiti da una classe modello usando FieldBuilder
. La classe FieldBuilder
utilizza la reflection per creare un elenco di oggetti SearchField
per l'indice esaminando le proprietà pubbliche e gli attributi della classe modello Hotel
specificata. Esamineremo la classe Hotel
più da vicino in un secondo momento.
private static void CreateIndex(string indexName, SearchIndexClient indexClient)
{
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));
var definition = new SearchIndex(indexName, searchFields);
indexClient.CreateOrUpdateIndex(definition);
}
Oltre ai campi, è possibile aggiungere anche profili di punteggio, suggerimenti di alternative oppure opzioni CORS per l'indice. Per motivi di brevità, nell’esempio questi elementi sono stati omessi. È possibile trovare altre informazioni sull'oggetto SearchIndex
e sulle relative parti costitutive nell'elenco delle proprietà SearchIndex, nonché nelle informazioni di riferimento sull'API REST.
Nota
Se necessario, è inoltre possibile creare l'elenco di oggetti Field
direttamente anziché utilizzando FieldBuilder
. Ad esempio, potrebbe non essere necessario usare una classe modello oppure potrebbe essere necessario usare una classe di modello esistente che non si vuole modificare aggiungendo attributi.
Chiamare CreateIndex in Main()
Main
crea un nuovo indice hotels chiamando il metodo precedente:
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, indexClient);
Usare una classe modello per la rappresentazione dei dati
L'esempio DotNetHowTo usa classi di modello per le strutture dati Hotel, Indirizzo e Camera . Hotel
fa riferimento a Address
, un tipo complesso a livello singolo (un campo in più parti) e Room
(una raccolta di campi in più parti).
È possibile usare questi tipi per creare e caricare l'indice e per strutturare la risposta da una query:
// Use-case: <Hotel> in a field definition
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));
// Use-case: <Hotel> in a response
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.GetResults())
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
Un approccio alternativo consiste nell'aggiungere direttamente campi a un indice. L'esempio seguente riporta alcuni campi.
SearchIndex index = new SearchIndex(indexName)
{
Fields =
{
new SimpleField("hotelId", SearchFieldDataType.String) { IsKey = true, IsFilterable = true, IsSortable = true },
new SearchableField("hotelName") { IsFilterable = true, IsSortable = true },
new SearchableField("hotelCategory") { IsFilterable = true, IsSortable = true },
new SimpleField("baseRate", SearchFieldDataType.Int32) { IsFilterable = true, IsSortable = true },
new SimpleField("lastRenovationDate", SearchFieldDataType.DateTimeOffset) { IsFilterable = true, IsSortable = true }
}
};
Definizioni dei campi
Il modello di dati in .NET e lo schema di indice corrispondente devono supportare l'esperienza di ricerca che si vuole fornire all'utente finale. Ogni oggetto di primo livello in .NET, ad esempio un documento di ricerca in un indice di ricerca, corrisponde a un risultato di ricerca presente nell'interfaccia utente. Ad esempio, in un'applicazione di ricerca hotel, gli utenti finali potrebbero voler cercare in base al nome dell'hotel, alle caratteristiche dell'hotel o alle caratteristiche di una determinata stanza.
All'interno di ogni classe, un campo viene definito con un tipo di dati e attributi che determinano la modalità di utilizzo. Il nome di ogni proprietà pubblica in ogni classe viene mappato a un campo con lo stesso nome nella definizione dell'indice.
Esaminare il frammento di codice seguente che esegue il pull di diverse definizioni di campo dalla classe Hotel. Si noti che Address
e Rooms
sono tipi C# con definizioni di classe personalizzate (fare riferimento al codice di esempio se si vuole visualizzarli). Entrambi sono tipi complessi. Per altre informazioni, vedere Come modellare tipi complessi.
public partial class Hotel
{
[SimpleField(IsKey = true, IsFilterable = true)]
public string HotelId { get; set; }
[SearchableField(IsSortable = true)]
public string HotelName { get; set; }
[SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnLucene)]
public string Description { get; set; }
[SearchableField(IsFilterable = true, IsSortable = true, IsFacetable = true)]
public string Category { get; set; }
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
[SearchableField]
public Address Address { get; set; }
public Room[] Rooms { get; set; }
Scegliere una classe campo
Quando si definiscono i campi, è possibile usare la classe base SearchField
oppure è possibile usare modelli helper derivati che fungono da modelli, con proprietà preconfigurati.
Esattamente un campo nell'indice deve fungere da chiave del documento (IsKey = true
). Deve essere una stringa e deve identificare in modo univoco ogni documento. È anche necessario avere IsHidden = true
, il che significa che non può essere visibile nei risultati della ricerca.
Tipo di campo | Descrizione e utilizzo |
---|---|
SearchField |
Classe base, con la maggior parte delle proprietà impostate su Null, ad eccezione Name del fatto che è obbligatorio e AnalyzerName che per impostazione predefinita è Lucene standard. |
SimpleField |
Modello helper. Può essere di qualsiasi tipo di dati, non è mai ricercabile (viene ignorato per le query di ricerca full-text) ed è recuperabile (non è nascosto). Altri attributi sono disattivati per impostazione predefinita, ma possono essere attivati. Si potrebbe usare un oggetto SimpleField per gli ID di documento o i campi usati solo in filtri, facet o profili di punteggio. In tal caso, assicurarsi di applicare tutti gli attributi necessari per lo scenario, ad esempio IsKey = true per un ID documento. Per altre informazioni, vedere SimpleFieldAttribute.cs nel codice sorgente. |
SearchableField |
Modello helper. 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, IsSortable e IsFacetable devono essere attribuiti in modo esplicito, come illustrato nell'esempio precedente.
Aggiungere attributi di campo
Si noti che ogni campo viene decorato con attributi come IsFilterable
, IsSortable
, IsKey
e AnalyzerName
. Questi attributi eseguono il mapping direttamente agli attributi di campo corrispondenti nell'indice di Azure AI Search. La classe FieldBuilder
utilizza queste proprietà per creare definizioni di campo per l'indice.
Mapping dei tipi di campo
Viene eseguito il mapping dei tipi .NET delle proprietà ai tipi di campo equivalenti nella definizione dell'indice. Ad esempio, viene eseguito il mapping della proprietà stringa Category
al campo category
, che è di tipo Edm.String
. Esistono mapping di tipi simili tra bool?
, Edm.Boolean
, DateTimeOffset?
e Edm.DateTimeOffset
e così via.
È stata notata la proprietà SmokingAllowed
?
[JsonIgnore]
public bool? SmokingAllowed => (Rooms != null) ? Array.Exists(Rooms, element => element.SmokingAllowed == true) : (bool?)null;
L'attributo JsonIgnore
in questa proprietà indica all'oggetto FieldBuilder
di non serializzarla nell'indice come campo. Si tratta di un ottimo modo per creare proprietà calcolate lato client che è possibile usare come helper nell'applicazione. In questo caso, la proprietà SmokingAllowed
riflette se una Room
qualsiasi nella raccolta Rooms
consente di fumare. Se sono tutte false, significa che l'intero hotel non consente di fumare.
Caricare un indice
Il passaggio successivo in Main
popola l'indice degli hotel appena creato. Questo popolamento di indici viene eseguito nel metodo seguente: (alcuni codici sostituiti con ...
a scopo illustrativo. Vedere la soluzione di esempio completa per il codice di popolamento completo dei dati.
private static void UploadDocuments(SearchClient searchClient)
{
IndexDocumentsBatch<Hotel> batch = IndexDocumentsBatch.Create(
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "1",
HotelName = "Stay-Kay City Hotel",
...
Address = new Address()
{
StreetAddress = "677 5th Ave",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Budget Room, 1 Queen Bed (Cityside)",
...
},
new Room()
{
Description = "Budget Room, 1 King Bed (Mountain View)",
...
},
new Room()
{
Description = "Deluxe Room, 2 Double Beds (City View)",
...
}
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "2",
HotelName = "Old Century Hotel",
...
{
StreetAddress = "140 University Town Center Dr",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Suite, 2 Double Beds (Mountain View)",
...
},
new Room()
{
Description = "Standard Room, 1 Queen Bed (City View)",
...
},
new Room()
{
Description = "Budget Room, 1 King Bed (Waterfront View)",
...
}
}
}),
IndexDocumentsAction.Upload(
new Hotel()
{
HotelId = "3",
HotelName = "Gastronomic Landscape Hotel",
...
Address = new Address()
{
StreetAddress = "3393 Peachtree Rd",
...
},
Rooms = new Room[]
{
new Room()
{
Description = "Standard Room, 2 Queen Beds (Amenities)",
...
},
new Room ()
{
Description = "Standard Room, 2 Double Beds (Waterfront View)",
...
},
new Room()
{
Description = "Deluxe Room, 2 Double Beds (Cityside)",
...
}
}
}
};
try
{
IndexDocumentsResult result = searchClient.IndexDocuments(batch);
}
catch (Exception)
{
// Sometimes when your Search service is under load, indexing will fail for some of the documents in
// the batch. Depending on your application, you can take compensating actions like delaying and
// retrying. For this simple demo, we just log the failed document keys and continue.
Console.WriteLine("Failed to index some of the documents: {0}");
}
Console.WriteLine("Waiting for documents to be indexed...\n");
Thread.Sleep(2000);
Questo metodo è costituito da quattro parti. Il primo crea una matrice di tre Hotel
oggetti ognuno con tre Room
oggetti che fungono da dati di input da caricare nell'indice. Questi dati sono hardcoded per motivi di semplicità. In un'applicazione effettiva, i dati provengono probabilmente da un'origine dati esterna, ad esempio un database SQL.
La seconda parte crea un oggetto IndexDocumentsBatch
contenente i documenti. Specificare l'operazione che si vuole applicare al batch al momento della creazione, in questo caso chiamando IndexDocumentsAction.Upload
. Il batch viene quindi caricato nell'indice di Azure AI Search dal metodo IndexDocuments
.
Nota
In questo esempio si stanno semplicemente caricando documenti. Se invece si vogliono unire le modifiche in documenti esistenti o eliminare documenti, creare batch chiamando IndexDocumentsAction.Merge
, IndexDocumentsAction.MergeOrUpload
o IndexDocumentsAction.Delete
. È inoltre possibile combinare diverse operazioni in un singolo batch chiamando IndexBatch.New
, che accetta una raccolta di oggetti IndexDocumentsAction
, ognuno dei quali indica ad Azure AI Search di eseguire una particolare operazione su un documento. È possibile creare ogni oggetto IndexDocumentsAction
con la propria operazione chiamando il metodo corrispondente, ad esempio IndexDocumentsAction.Merge
, IndexAction.Upload
e così via.
La terza parte di questo metodo è un blocco catch che gestisce un caso di errore importante per l'indicizzazione. Se il servizio di ricerca non riesce a indicizzare alcuni dei documenti nel batch, viene generato un RequestFailedException
. Un'eccezione può verificarsi se l'indicizzazione dei documenti avviene mentre il servizio è sovraccarico. Si consiglia di gestire in modo esplicito questo caso nel codice. È possibile ritardare e quindi ritentare l'indicizzazione di documenti, accedere e continuare come nell'esempio, oppure eseguire altre attività a seconda dei requisiti di coerenza di dati dell'applicazione. Un'alternativa consiste nell'usare SearchIndexingBufferedSender per l'invio in batch intelligente, lo scaricamento automatico e i tentativi per le azioni di indicizzazione non riuscite. Per altre informazioni di contesto, vedere l'esempio searchIndexingBufferedSender.
Infine, il metodo UploadDocuments
ritarda per due secondi. L'indicizzazione avviene in modo asincrono nel servizio di ricerca, pertanto l'applicazione di esempio deve attendere un breve periodo per garantire che i documenti siano disponibili per la ricerca. Ritardi come questi in genere sono necessari solo in applicazioni di esempio, test e demo.
Chiamare UploadDocuments in Main()
Il frammento di codice seguente configura un'istanza di SearchClient
usando il metodo GetSearchClient
di indexClient. IndexClient usa una chiave API di amministrazione nelle richieste, necessaria per il caricamento o l'aggiornamento dei documenti.
Un approccio alternativo consiste nel chiamare SearchClient
direttamente, passando una chiave API di amministrazione in AzureKeyCredential
.
SearchClient searchClient = indexClient.GetSearchClient(indexName);
Console.WriteLine("{0}", "Uploading documents...\n");
UploadDocuments(searchClient);
Esegui query
Innanzitutto, configurare un SearchClient
che legga l'endpoint di servizio e la chiave API di query da appsettings.json:
private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
string searchServiceEndPoint = configuration["YourSearchServiceEndPoint"];
string queryApiKey = configuration["YourSearchServiceQueryApiKey"];
SearchClient searchClient = new SearchClient(new Uri(searchServiceEndPoint), indexName, new AzureKeyCredential(queryApiKey));
return searchClient;
}
In secondo luogo, definire un metodo che invia una richiesta di query.
Ogni volta che il metodo esegue una query, crea un nuovo oggetto SearchOptions
. L'oggetto consente di specificare opzioni aggiuntive per la query di ordinamento, filtro, impaginazione e faceting. In questo metodo, impostiamo le proprietà Filter
, Select
e OrderBy
per diverse query. Per ulteriori informazioni sulla sintassi delle espressioni di query di ricerca, Sintassi delle query semplici.
Il passaggio successivo è l'esecuzione di query. L'esecuzione della ricerca avviene usando il metodo SearchClient.Search
. Per ogni query, passare il testo di ricerca da utilizzare come stringa (o "*"
se non è presente il testo di ricerca), oltre alle opzioni di ricerca create in precedenza. Viene inoltre specificato Hotel
come parametro di tipo per SearchClient.Search
, che indica all'SDK di deserializzare i documenti nei risultati della ricerca in oggetti di tipo Hotel
.
private static void RunQueries(SearchClient searchClient)
{
SearchOptions options;
SearchResults<Hotel> results;
Console.WriteLine("Query 1: Search for 'motel'. Return only the HotelName in results:\n");
options = new SearchOptions();
options.Select.Add("HotelName");
results = searchClient.Search<Hotel>("motel", options);
WriteDocuments(results);
Console.Write("Query 2: Apply a filter to find hotels with rooms cheaper than $100 per night, ");
Console.WriteLine("returning the HotelId and Description:\n");
options = new SearchOptions()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)"
};
options.Select.Add("HotelId");
options.Select.Add("Description");
results = searchClient.Search<Hotel>("*", options);
WriteDocuments(results);
Console.Write("Query 3: Search the entire index, order by a specific field (lastRenovationDate) ");
Console.Write("in descending order, take the top two results, and show only hotelName and ");
Console.WriteLine("lastRenovationDate:\n");
options =
new SearchOptions()
{
Size = 2
};
options.OrderBy.Add("LastRenovationDate desc");
options.Select.Add("HotelName");
options.Select.Add("LastRenovationDate");
results = searchClient.Search<Hotel>("*", options);
WriteDocuments(results);
Console.WriteLine("Query 4: Search the HotelName field for the term 'hotel':\n");
options = new SearchOptions();
options.SearchFields.Add("HotelName");
//Adding details to select, because "Location" isn't supported yet when deserializing search result to "Hotel"
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Description");
options.Select.Add("Category");
options.Select.Add("Tags");
options.Select.Add("ParkingIncluded");
options.Select.Add("LastRenovationDate");
options.Select.Add("Rating");
options.Select.Add("Address");
options.Select.Add("Rooms");
results = searchClient.Search<Hotel>("hotel", options);
WriteDocuments(results);
}
In terzo luogo, definire un metodo che scriva la risposta, stampando ogni documento nella console:
private static void WriteDocuments(SearchResults<Hotel> searchResults)
{
foreach (SearchResult<Hotel> result in searchResults.GetResults())
{
Console.WriteLine(result.Document);
}
Console.WriteLine();
}
Chiamare RunQueries in Main()
SearchClient indexClientForQueries = CreateSearchClientForQueries(indexName, configuration);
Console.WriteLine("{0}", "Running queries...\n");
RunQueries(indexClientForQueries);
Esplorare i costrutti di query
Queste query saranno ora esaminate in maggiore dettaglio. Ecco il codice per eseguire la prima query:
options = new SearchOptions();
options.Select.Add("HotelName");
results = searchClient.Search<Hotel>("motel", options);
WriteDocuments(results);
In questo caso, si sta cercando l'intero indice per la parola motel in qualsiasi campo ricercabile e si vogliono recuperare solo i nomi degli hotel, come specificato dall'opzione Select
. Ecco i risultati:
Name: Stay-Kay City Hotel
Name: Old Century Hotel
Nella seconda query usare un filtro per selezionare le camere con una tariffa notturna inferiore a $ 100. Restituisce solo l'ID e la descrizione dell'hotel nei risultati:
options = new SearchOptions()
{
Filter = "Rooms/any(r: r/BaseRate lt 100)"
};
options.Select.Add("HotelId");
options.Select.Add("Description");
results = searchClient.Search<Hotel>("*", options);
Questa query utilizza un'espressione $filter
OData, Rooms/any(r: r/BaseRate lt 100)
, per filtrare i documenti nell'indice. Usa l'operatore any per applicare "BaseRate lt 100" a ogni elemento dell'insieme Rooms. Per ulteriori informazioni, vedere Sintassi del filtro OData .
Nella terza query, individuare i due hotel ristrutturati più di recente e visualizzare il nome dell'hotel e la data dell'ultima ristrutturazione. Ecco il codice:
options =
new SearchOptions()
{
Size = 2
};
options.OrderBy.Add("LastRenovationDate desc");
options.Select.Add("HotelName");
options.Select.Add("LastRenovationDate");
results = searchClient.Search<Hotel>("*", options);
WriteDocuments(results);
Nell'ultima query trovare tutti i nomi di hotel che corrispondono alla parola hotel:
options.Select.Add("HotelId");
options.Select.Add("HotelName");
options.Select.Add("Description");
options.Select.Add("Category");
options.Select.Add("Tags");
options.Select.Add("ParkingIncluded");
options.Select.Add("LastRenovationDate");
options.Select.Add("Rating");
options.Select.Add("Address");
options.Select.Add("Rooms");
results = searchClient.Search<Hotel>("hotel", options);
WriteDocuments(results);
Questa sezione conclude questa introduzione a .NET SDK, ma non si arresta qui. La sezione successiva suggerisce altre risorse per altre informazioni sulla programmazione con Azure AI Search.
Contenuto correlato
Esplorare la documentazione di riferimento dell'API per Azure.Search.Documents e l'API REST
Esplorare altri esempi di codice basati su Azure.Search.Documents in azure-search-dotnet-samples e search-dotnet-getting-started
Rivedere le convenzioni di denominazione per informazioni sulle regole per la denominazione dei vari oggetti
Rivedere i tipi di dati supportati