Condividi tramite


Esercitazione: Creare la prima app di ricerca in Ricerca cognitiva di Azure con .NET SDK

Questa esercitazione illustra come creare un'app Web che esegue query e restituisce risultati da un indice di ricerca usando Ricerca cognitiva di Azure e Visual Studio.

In questa esercitazione si apprenderà come:

  • Configurare un ambiente di sviluppo
  • Strutture di dati del modello
  • Creare una pagina Web per raccogliere input di query e visualizzare i risultati
  • Definire un metodo di ricerca
  • Testare l'app

Si apprenderà anche quanto sia semplice una chiamata di ricerca. Le istruzioni chiave nel codice vengono incapsulate nelle righe seguenti:

var options = new SearchOptions()
{
    // The Select option specifies fields for the result set
    options.Select.Add("HotelName");
    options.Select.Add("Description");
};

var searchResult = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
model.resultList = searchResult.Value.GetResults().ToList();

Una sola chiamata esegue una query sull'indice di ricerca e restituisce i risultati.

Ricerca di *pool*

Informazioni generali

Questa esercitazione utilizza "hotels-sample-index," che è possibile creare rapidamente nel proprio servizio di ricerca seguendo la guida introduttiva Importa dati. L'indice contiene dati di hotel fittizi, disponibili come origine dati predefinita in ogni servizio di ricerca.

La prima lezione di questo tutorial crea una struttura di base della query e una pagina di esplorazione, che verranno migliorate nella lezione successiva per includere il paging, le facette e un'esperienza di completamento automatico.

Una versione completata del codice è disponibile nel progetto seguente:

Prerequisiti

Installare ed eseguire il progetto da GitHub

Se si vuole passare a un'app funzionante, seguire questa procedura per scaricare ed eseguire il codice completato.

  1. Individuare l'esempio in GitHub: Creare la prima app.

  2. Nella cartella radice selezionare Codice, quindi Clona o Scarica ZIP per creare la copia locale privata del progetto.

  3. Usando Visual Studio, passare a e aprire la soluzione per la pagina di ricerca di base ("1-basic-search-page") e selezionare Avvia senza eseguire il debug (o premere F5) per compilare ed eseguire il programma.

  4. Si tratta di un indice di hotel, quindi digitare alcune parole che potresti usare per cercare hotel (ad esempio, "wifi", "view", "bar", "parking"). Esaminare i risultati.

    Ricerca di *wifi*

I componenti essenziali per le ricerche più sofisticate sono inclusi in questa unica app. Se non si ha familiarità con lo sviluppo di ricerche, è possibile ricreare questa app in modo dettagliato per apprendere il flusso di lavoro. Le sezioni seguenti illustrano come.

Configurare un ambiente di sviluppo

Per creare questo progetto da zero e quindi rafforzare i concetti di Ricerca cognitiva di Azure, iniziare con un progetto di Visual Studio.

  1. In Visual Studio selezionare Nuovo>progetto e quindi ASP.NET Core Web App (Modello-View-Controller).

    Creazione di un progetto cloud

  2. Assegna un nome al tuo progetto, ad esempio "FirstSearchApp", e imposta il percorso. Seleziona Avanti.

  3. Accettare le impostazioni predefinite per framework di destinazione, tipo di autenticazione e HTTPS. Fare clic su Crea.

  4. Installare la libreria client. In Strumenti>Gestione pacchetti>NuGet Gestisci pacchetti NuGet per soluzione... selezionare Sfoglia e quindi cercare "azure.search.documents". Installare Azure.Search.Documents (versione 11 o successiva), accettando i contratti di licenza e le dipendenze.

    Uso di NuGet per aggiungere librerie di Azure

In questo passaggio impostare l'endpoint e la chiave di accesso per la connessione al servizio di ricerca che fornisce l'indice di esempio degli hotel.

  1. Aprire appsettings.json e sostituire le righe predefinite con l'URL del servizio di ricerca (nel formato https://<service-name>.search.windows.net) e una chiave API di amministrazione o query del servizio di ricerca. Poiché non è necessario creare o aggiornare un indice, è possibile usare la chiave di query per questa esercitazione.

    {
        "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URI>",
        "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-API-KEY>"
    }
    
  2. In Esplora soluzioni selezionare il file e in Proprietà modificare l'impostazione Copia nella directory di output su Copia se più recente.

    Copia delle impostazioni dell'app nell'output

Strutture di dati del modello

I modelli (classi C#) vengono usati per comunicare i dati tra il client (la visualizzazione), il server (il controller) e anche il cloud di Azure usando l'architettura MVC (modello, visualizzazione, controller). In genere, questi modelli riflettono la struttura dei dati a cui si accede.

In questo passaggio verranno modellate le strutture di dati dell'indice di ricerca, nonché la stringa di ricerca usata nelle comunicazioni di visualizzazione/controller. Nell'indice degli hotel ogni hotel ha molte camere e ogni hotel ha un indirizzo in più parti. Complessivamente, la rappresentazione completa di un hotel è una struttura di dati gerarchica e annidata. Sono necessarie tre classi per creare ogni componente.

Il set di classi Hotel, Address e Room è noto come tipi complessi, una funzionalità importante di Ricerca cognitiva di Azure. I tipi complessi possono essere molti livelli profondi di classi e sottoclassi e consentono di rappresentare strutture di dati molto più complesse rispetto all'uso di tipi semplici (una classe contenente solo membri primitivi).

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse su Modelli>Aggiungi>nuovo elemento.

  2. Selezionare Classe e denominare l'elemento Hotel.cs. Sostituire tutto il contenuto di Hotel.cs con il codice seguente. Si notino i membri Address e Room della classe, questi campi sono classi stesse, quindi saranno necessari anche i modelli.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using Microsoft.Spatial;
    using System;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Hotel
        {
            [SimpleField(IsFilterable = true, IsKey = 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(AnalyzerName = LexicalAnalyzerName.Values.FrLucene)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { 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; }
    
            public Address Address { get; set; }
    
            [SimpleField(IsFilterable = true, IsSortable = true)]
            public GeographyPoint Location { get; set; }
    
            public Room[] Rooms { get; set; }
        }
    }
    
  3. Ripetere lo stesso processo di creazione di un modello per la classe Address , assegnando un nome al file Address.cs. Sostituire il contenuto con quanto segue.

    using Azure.Search.Documents.Indexes;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Address
        {
            [SearchableField]
            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; }
        }
    }
    
  4. E ancora, seguire lo stesso processo per creare la classe Room , denominando il file Room.cs.

    using Azure.Search.Documents.Indexes;
    using Azure.Search.Documents.Indexes.Models;
    using System.Text.Json.Serialization;
    
    namespace FirstAzureSearchApp.Models
    {
        public partial class Room
        {
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.EnMicrosoft)]
            public string Description { get; set; }
    
            [SearchableField(AnalyzerName = LexicalAnalyzerName.Values.FrMicrosoft)]
            [JsonPropertyName("Description_fr")]
            public string DescriptionFr { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string Type { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public double? BaseRate { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string BedOptions { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public int SleepsCount { get; set; }
    
            [SimpleField(IsFilterable = true, IsFacetable = true)]
            public bool? SmokingAllowed { get; set; }
    
            [SearchableField(IsFilterable = true, IsFacetable = true)]
            public string[] Tags { get; set; }
        }
    }
    
  5. L'ultimo modello creato in questa esercitazione è una classe denominata SearchData e rappresenta l'input dell'utente (searchText) e l'output della ricerca (resultList). Il tipo di output è critico, SearchResults<Hotel>, in quanto questo tipo corrisponde esattamente ai risultati della ricerca ed è necessario passare questo riferimento alla visualizzazione. Sostituire il modello predefinito con il codice seguente.

    using Azure.Search.Documents.Models;
    
    namespace FirstAzureSearchApp.Models
    {
        public class SearchData
        {
            // The text to search for.
            public string searchText { get; set; }
    
            // The list of results.
            public SearchResults<Hotel> resultList;
        }
    }
    

Creare una pagina Web

I modelli di progetto sono dotati di una serie di visualizzazioni client che si trovano nella cartella Visualizzazioni . Le visualizzazioni esatte dipendono dalla versione di Core .NET in uso (3.1 viene usata in questo esempio). Per questa esercitazione si modificherà Index.cshtml per includere gli elementi di una pagina di ricerca.

Eliminare il contenuto di Index.cshtml nell'intero file e ricompilare il file nei passaggi seguenti.

  1. L'esercitazione usa due immagini di piccole dimensioni nella visualizzazione: un logo di Azure e un'icona di lente di ingrandimento di ricerca (azure-logo.png e search.png). Copiare le immagini dal progetto GitHub nella cartella wwwroot/images del progetto.

  2. La prima riga di Index.cshtml deve fare riferimento al modello usato per comunicare i dati tra il client (la vista) e il server (il controller), ovvero il modello SearchData creato in precedenza. Aggiungere questa riga al file Index.cshtml.

    @model FirstAzureSearchApp.Models.SearchData
    
  3. È prassi standard immettere un titolo per la visualizzazione, quindi le righe successive devono essere:

    @{
        ViewData["Title"] = "Home Page";
    }
    
  4. Dopo il titolo immettere un riferimento a un foglio di stile HTML, che verrà creato a breve.

    <head>
        <link rel="stylesheet" href="~/css/hotels.css" />
    </head>
    
  5. Il corpo della visualizzazione gestisce due casi d'uso. Innanzitutto, deve fornire una pagina vuota al primo utilizzo, prima che venga immesso qualsiasi testo di ricerca. In secondo luogo, deve gestire i risultati, oltre alla casella di testo di ricerca, per le query ripetute.

    Per gestire entrambi i casi, è necessario verificare se il modello fornito alla visualizzazione è Null. Un modello Null indica il primo caso d'uso (l'esecuzione iniziale dell'app). Aggiungere quanto segue al file Index.cshtml e leggere i commenti.

    <body>
    <h1 class="sampleTitle">
        <img src="~/images/azure-logo.png" width="80" />
        Hotels Search
    </h1>
    
    @using (Html.BeginForm("Index", "Home", FormMethod.Post))
    {
        // Display the search text box, with the search icon to the right of it.
        <div class="searchBoxForm">
            @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox" }) <input class="searchBoxSubmit" type="submit" value="">
        </div>
    
        @if (Model != null)
        {
            // Show the result count.
            <p class="sampleText">
                @Model.resultList.TotalCount Results
            </p>
    
            var results = Model.resultList.GetResults().ToList();
    
            @for (var i = 0; i < results.Count; i++)
            {
                // Display the hotel name and description.
                @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" })
                @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" })
            }
        }
    }
    </body>
    
  6. Aggiungere il foglio di stile. In Visual Studio, in File>nuovo>file selezionare Foglio di stile (con Generale evidenziato).

    Sostituire il codice predefinito con il codice seguente. Questo file non verrà illustrato in modo più dettagliato, gli stili sono HTML standard.

    textarea.box1 {
        width: 648px;
        height: 30px;
        border: none;
        background-color: azure;
        font-size: 14pt;
        color: blue;
        padding-left: 5px;
    }
    
    textarea.box2 {
        width: 648px;
        height: 100px;
        border: none;
        background-color: azure;
        font-size: 12pt;
        padding-left: 5px;
        margin-bottom: 24px;
    }
    
    .sampleTitle {
        font: 32px/normal 'Segoe UI Light',Arial,Helvetica,Sans-Serif;
        margin: 20px 0;
        font-size: 32px;
        text-align: left;
    }
    
    .sampleText {
        font: 16px/bold 'Segoe UI Light',Arial,Helvetica,Sans-Serif;
        margin: 20px 0;
        font-size: 14px;
        text-align: left;
        height: 30px;
    }
    
    .searchBoxForm {
        width: 648px;
        box-shadow: 0 0 0 1px rgba(0,0,0,.1), 0 2px 4px 0 rgba(0,0,0,.16);
        background-color: #fff;
        display: inline-block;
        border-collapse: collapse;
        border-spacing: 0;
        list-style: none;
        color: #666;
    }
    
    .searchBox {
        width: 568px;
        font-size: 16px;
        margin: 5px 0 1px 20px;
        padding: 0 10px 0 0;
        border: 0;
        max-height: 30px;
        outline: none;
        box-sizing: content-box;
        height: 35px;
        vertical-align: top;
    }
    
    .searchBoxSubmit {
        background-color: #fff;
        border-color: #fff;
        background-image: url(/images/search.png);
        background-repeat: no-repeat;
        height: 20px;
        width: 20px;
        text-indent: -99em;
        border-width: 0;
        border-style: solid;
        margin: 10px;
        outline: 0;
    }
    
  7. Salvare il file del foglio di stile come hotels.css, nella cartella wwwroot/css , insieme al file site.css predefinito.

Questo conclude la nostra esposizione. A questo punto, i modelli e le visualizzazioni vengono completati. Solo il controller viene lasciato per legare tutto insieme.

Definire i metodi

In questo passaggio modificare il contenuto di Home Controller.

  1. Aprire il file HomeController.cs e sostituire le istruzioni using con le istruzioni seguenti.

    using Azure;
    using Azure.Search.Documents;
    using Azure.Search.Documents.Indexes;
    using FirstAzureSearchApp.Models;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    

Aggiungere metodi Index

In un'app MVC, il metodo Index() è un metodo di azione predefinito per qualsiasi controller. Apre la pagina HTML dell'indice. Il metodo predefinito, che non accetta parametri, viene usato in questa esercitazione per il caso d'uso di avvio dell'applicazione: rendering di una pagina di ricerca vuota.

In questa sezione viene esteso il metodo per supportare un secondo caso d'uso: rendering della pagina quando un utente ha immesso testo di ricerca. Per supportare questo caso, il metodo index viene esteso per accettare un modello come parametro.

  1. Aggiungere il metodo seguente, dopo il metodo Index() predefinito.

        [HttpPost]
        public async Task<ActionResult> Index(SearchData model)
        {
            try
            {
                // Ensure the search string is valid.
                if (model.searchText == null)
                {
                    model.searchText = "";
                }
    
                // Make the Azure Cognitive Search call.
                await RunQueryAsync(model);
            }
    
            catch
            {
                return View("Error", new ErrorViewModel { RequestId = "1" });
            }
            return View(model);
        }
    

    Si noti la dichiarazione async del metodo e la chiamata await a RunQueryAsync. Queste parole chiave si occupano di effettuare chiamate asincrone ed evitare quindi di bloccare i thread nel server.

    Il blocco catch usa il modello di errore predefinito creato.

Prendere nota della gestione degli errori e di altre visualizzazioni e metodi predefiniti

A seconda della versione di .NET Core in uso, viene creato un set leggermente diverso di visualizzazioni predefinite. Per .NET Core 3.1 le visualizzazioni predefinite sono Indice, Privacy e Errore. È possibile visualizzare queste pagine predefinite durante l'esecuzione dell'app ed esaminare il modo in cui vengono gestite nel controller.

La visualizzazione Degli errori verrà testata più avanti in questa esercitazione.

Nell'esempio di GitHub le visualizzazioni inutilizzate e le azioni associate vengono eliminate.

Aggiungere il metodo RunQueryAsync

La chiamata di Ricerca cognitiva di Azure viene incapsulata nel metodo RunQueryAsync .

  1. Aggiungere prima alcune variabili statiche per configurare il servizio di Azure e una chiamata per avviarle.

        private static SearchClient _searchClient;
        private static SearchIndexClient _indexClient;
        private static IConfigurationBuilder _builder;
        private static IConfigurationRoot _configuration;
    
        private void InitSearch()
        {
            // Create a configuration using appsettings.json
            _builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
            _configuration = _builder.Build();
    
            // Read the values from appsettings.json
            string searchServiceUri = _configuration["SearchServiceUri"];
            string queryApiKey = _configuration["SearchServiceQueryApiKey"];
    
            // Create a service and index client.
            _indexClient = new SearchIndexClient(new Uri(searchServiceUri), new AzureKeyCredential(queryApiKey));
            _searchClient = _indexClient.GetSearchClient("hotels");
        }
    
  2. Aggiungere ora il metodo RunQueryAsync stesso.

    private async Task<ActionResult> RunQueryAsync(SearchData model)
    {
        InitSearch();
    
        var options = new SearchOptions() 
        { 
            IncludeTotalCount = true
        };
    
        // Enter Hotel property names into this list so only these values will be returned.
        // If Select is empty, all values will be returned, which can be inefficient.
        options.Select.Add("HotelName");
        options.Select.Add("Description");
    
        // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search.
        model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);          
    
        // Display the results.
        return View("Index", model);
    }
    

    In questo metodo verificare prima di tutto che la configurazione di Azure sia avviata, quindi impostare alcune opzioni di ricerca. L'opzione Select specifica i campi da restituire nei risultati e quindi corrispondono ai nomi delle proprietà nella classe hotel . Se si omette Select, vengono restituiti tutti i campi non nascosti, che possono risultare inefficienti se si è interessati solo a un subset di tutti i campi possibili.

    La chiamata asincrona alla ricerca formula la richiesta (modellata come searchText) e la risposta (modellata come searchResult). Se si esegue il debug di questo codice, la classe SearchResult è un buon candidato per impostare un punto di interruzione se è necessario esaminare il contenuto di model.resultList. Si noterebbe che è intuitivo, fornendo solo i dati richiesti e non molto altro.

Testare l'app

A questo punto, verificare se l'app viene eseguita correttamente.

  1. Selezionare Avvia debug>senza eseguire debug o premere F5. Se l'app viene eseguita come previsto, si dovrebbe ottenere la visualizzazione Indice iniziale.

    Apertura dell'app

  2. Immettere una stringa di query, ad esempio "beach" (o qualsiasi testo in mente) e fare clic sull'icona di ricerca per inviare la richiesta.

    Ricerca di *spiaggia*

  3. Provare a inserire "cinque stelle". Si noti che questa query non restituisce risultati. Una ricerca più sofisticata considera "cinque stelle" come sinonimo di "lusso" e restituisce tali risultati. Il supporto per i sinonimi è disponibile in Ricerca cognitiva di Azure, ma non è trattato in questa serie di esercitazioni .

  4. Provare a immettere "hot" come testo di ricerca. Non restituisce voci che contengono la parola "hotel". La ricerca individua solo parole intere, anche se vengono restituiti alcuni risultati.

  5. Prova altre parole: "pool", "sole", "vista" e qualsiasi altra parola. Si noterà che Ricerca cognitiva di Azure funziona al suo livello più semplice, ma ancora convincente.

Testare le condizioni e gli errori dei bordi

È importante verificare che le funzionalità di gestione degli errori funzionino come dovrebbero, anche quando le cose funzionano perfettamente.

  1. Nel metodo Index , dopo la chiamata try { immettere la riga Throw new Exception(). Questa eccezione forza un errore quando si esegue la ricerca nel testo.

  2. Eseguire l'app, immettere "bar" come testo di ricerca e fare clic sull'icona di ricerca. L'eccezione dovrebbe comportare la visualizzazione degli errori.

    Forzare un errore

    Importante

    È considerato un rischio per la sicurezza restituire i numeri di errore interni nelle pagine di errore. Se l'app è destinata all'uso generale, seguire le procedure consigliate per la sicurezza di cosa restituire quando si verifica un errore.

  3. Rimuovere Throw new Exception() quando si è soddisfatti della gestione degli errori funziona come dovrebbe.

Risultati

Si considerino le considerazioni seguenti di questo progetto:

  • Una chiamata di Ricerca cognitiva di Azure è concisa ed è facile interpretare i risultati.
  • Le chiamate asincrone aggiungono una piccola quantità di complessità al controller, ma sono una procedura consigliata che migliora le prestazioni.
  • Questa app ha eseguito una ricerca di testo semplice, definita da ciò che è configurato in searchOptions. Tuttavia, questa classe può essere popolata con molti membri che aggiungono complessità a una ricerca. Con un po' di lavoro, puoi rendere questa app notevolmente più potente.

Passaggi successivi

Per migliorare l'esperienza utente, aggiungere altre funzionalità, in particolare il paging (usando numeri di pagina o scorrimento infinito) e il completamento automatico/suggerimenti. È anche possibile prendere in considerazione altre opzioni di ricerca (ad esempio, ricerche geografiche sugli hotel entro un raggio specificato di un determinato punto) e l'ordinamento dei risultati della ricerca.

Questi passaggi successivi vengono affrontati nelle esercitazioni rimanenti. Iniziamo con il paging.