Condividi tramite


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:

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

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:

Screenshot dell'output Console.WriteLine del programma di esempio.

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.