Come usare Azure.Search.Documents in un'applicazione .NET C#
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 di Azure.Search.Documents del team di Azure SDK equivalente funzionalmente alla libreria client precedente, Microsoft.Azure.Search. La versione 11 è più coerente in termini di programmabilità di Azure. Alcuni esempi includono AzureKeyCredential
l'autenticazione della chiave 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 output
La libreria viene distribuita come singolo pacchetto NuGet Azure.Search.Documents, 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
Azure.Search.Documents (versione 11) è destinato alla specifica del servizio di ricerca 2020-06-30.
La libreria client non fornisce operazioni di gestione dei servizi, ad esempio la creazione e il ridimensionamento dei servizi di ricerca e la gestione delle chiavi API. Se è necessario gestire le risorse di ricerca da un'applicazione .NET, usare la libreria Microsoft.Azure.Management.Search in Azure SDK per .NET.
Eseguire l'aggiornamento 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.
I propri servizio di ricerca di intelligenza artificiale di Azure. 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 Azure.Search.Documents usando Strumenti>NuGet Gestione pacchetti> Gestisci pacchetti NuGet per la soluzione in Visual Studio. Cercare il nome
Azure.Search.Documents
del pacchetto .
Azure SDK per .NET è conforme a .NET Standard 2.0.
Applicazione di esempio
Questo articolo "illustra l'esempio", basandosi sull'esempio di codice DotNetHowTo su GitHub per illustrare i concetti fondamentali in Ricerca di intelligenza artificiale di Azure, in particolare su 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.
Di seguito è riportato il programma principale, che mostra 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 è incentrato sui primi due.
Come minimo, tutti i client richiedono il nome o l'endpoint del servizio e una chiave API. È comune fornire queste informazioni in un file di configurazione, analogamente a quanto trovato nel appsettings.json
file 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["SearchServiceEndPoint"];
string adminApiKey = configuration["SearchServiceAdminApiKey"];
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 di amministrazione, 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["SearchServiceEndPoint"];
string queryApiKey = configuration["SearchServiceQueryApiKey"];
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 è stata richiesta una chiave di amministrazione, verrà SearchClient
generata una CloudException
con il messaggio di errore "Accesso negato" la prima volta che si chiama un metodo di operazione. In questo caso, controllare la chiave API.
Eliminazione dell'indice
Nelle prime fasi di sviluppo potrebbe essere necessario includere un'istruzione DeleteIndex
per eliminare un indice in corso di lavoro in modo da poterlo ricreare con una definizione aggiornata. Il codice di esempio per Ricerca di intelligenza artificiale di Azure 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 usa l'oggetto specificato SearchIndexClient
per verificare se l'indice esiste e, in caso affermativo, lo elimina:
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. Ad esempio, nel metodo precedente è 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, è anche possibile aggiungere profili di punteggio, suggerimenti o opzioni CORS all'indice (questi parametri vengono omessi dall'esempio per brevità). È possibile trovare altre informazioni sull'oggetto SearchIndex e sulle relative parti costitutive nell'elenco delle proprietà, nonché nelle informazioni di riferimento sull'API SearchIndex
REST.
Nota
Se necessario, è inoltre possibile creare l'elenco di oggetti Field
direttamente anziché utilizzando FieldBuilder
. Ad esempio, è possibile evitare di utilizzare una classe modello o potrebbe essere necessario utilizzare una classe modello esistente che non si desidera 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 di dati Hotel, Address e Room . Hotel
fa Address
riferimento a , 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. Nell'esempio seguente vengono illustrati solo 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 possono 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; }
Scelta di una classe di campi
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 di base, con la maggior parte delle proprietà impostate su Null, ad eccezione del Name fatto che è obbligatorio e AnalyzerName che per impostazione predefinita è Lucene standard. |
SimpleField |
Modello helper. Può essere qualsiasi tipo di dati, è sempre non 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.
Aggiunta di attributi di campo
Si noti che ogni campo viene decorato con attributi come IsFilterable
, IsSortable
IsKey
, e AnalyzerName
. Questi attributi vengono mappati direttamente agli attributi di campo corrispondenti in un indice di Ricerca di intelligenza artificiale di Azure. La FieldBuilder
classe usa queste proprietà per costruire definizioni di campo per l'indice.
Mapping dei tipi di campo
I tipi .NET delle proprietà eseguono il mapping 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
così via.
Hai notato la SmokingAllowed
proprietà?
[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 serializzarlo 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 SmokingAllowed
proprietà riflette se una Room
qualsiasi nella Rooms
raccolta consente il fumo. Se tutti sono falsi, indica che l'intero hotel non consente il fumo.
Caricare un indice
Il passaggio successivo in Main
popola l'indice "hotels" 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 = "Secret Point Motel",
...
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 = "Twin Dome Motel",
...
{
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 = "Triple 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 fungeranno da dati di input da caricare nell'indice. Questi dati sono hardcoded per motivi di semplicità. In un'applicazione effettiva, è probabile che i dati provengano 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 Ricerca di intelligenza artificiale di Azure dal IndexDocuments
metodo .
Nota
In questo esempio, verranno semplicemente caricati i documenti. Se invece si vogliono unire le modifiche in documenti esistenti o eliminare documenti, creare batch chiamando IndexDocumentsAction.Merge
, IndexDocumentsAction.MergeOrUpload
o IndexDocumentsAction.Delete
. È anche possibile combinare operazioni diverse in un singolo batch chiamando IndexBatch.New
, che accetta una raccolta di IndexDocumentsAction
oggetti, ognuno dei quali indica a Ricerca di intelligenza artificiale di Azure di eseguire una determinata 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 generata un'eccezione RequestFailedException
. Un'eccezione può verificarsi se si esegue l'indicizzazione dei documenti mentre il servizio è sottoposto a un carico elevato. 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 sul contesto, vedere questo esempio .
Infine, il metodo UploadDocuments
ritarda per due secondi. L'indicizzazione avviene in modo asincrono nel servizio di ricerca, quindi l'applicazione di esempio deve attendere un breve periodo di tempo per assicurarsi 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 GetSearchClient
metodo 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
oggetto che legge l'endpoint di servizio e la chiave API di query da appsettings.json:
private static SearchClient CreateSearchClientForQueries(string indexName, IConfigurationRoot configuration)
{
string searchServiceEndPoint = configuration["SearchServiceEndPoint"];
string queryApiKey = configuration["SearchServiceQueryApiKey"];
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 SearchOptions
oggetto . Questo oggetto viene usato per specificare opzioni aggiuntive per la query, ad esempio l'ordinamento, il filtro, il paging e il faceting. In questo metodo si imposta la Filter
proprietà , Select
e OrderBy
per query diverse. Per altre informazioni sulla sintassi dell'espressione di query di ricerca, sintassi di query semplice.
Il passaggio successivo è l'esecuzione di query. L'esecuzione della ricerca viene eseguita usando il SearchClient.Search
metodo . Per ogni query, passare il testo di ricerca da usare come stringa (o "*"
se non è presente testo di ricerca), più le 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 scrive 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: Secret Point Motel
Name: Twin Dome Motel
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);
La query precedente usa un'espressione OData $filter
, Rooms/any(r: r/BaseRate lt 100)
, per filtrare i documenti nell'indice. In questo modo viene utilizzato l'operatore any per applicare "BaseRate lt 100" a ogni elemento dell'insieme Rooms. Per altre informazioni, vedere Sintassi del filtro OData.
Nella terza query trovare i primi due hotel che sono stati ristrutturati più di recente e visualizzare il nome dell'hotel e l'ultima data di 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 corrispondenti 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 Ricerca di intelligenza artificiale di Azure.
Passaggi successivi
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-getting-started-dotnet
Esaminare le convenzioni di denominazione per apprendere le regole per la denominazione di vari oggetti
Esaminare i tipi di dati supportati