Condividi tramite


Esercitazione: Creare la prima app di ricerca in Ricerca cognitiva di Azure usando .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
  • Modellare le strutture di dati
  • Creare un'app Web per raccogliere gli input delle 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 poche 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*

Panoramica

Questa esercitazione usa hotels-sample-index, che è possibile creare rapidamente nel proprio servizio di ricerca eseguendo l'avvio rapido Importa dati. L'indice contiene dati di hotel fittizi, disponibili come origine dati predefinita in ogni servizio di ricerca.

La prima lezione di questa esercitazione crea una struttura di query di base e una pagina di ricerca, che verrà migliorata nelle lezioni successive per includere paging, facet e un'esperienza di tipo-ahead.

Una versione completa del codice è disponibile nel progetto seguente:

Prerequisiti

Installare ed eseguire il progetto da GitHub

Se si vuole procedere direttamente a un'app funzionante, seguire la procedura seguente per scaricare ed eseguire il codice completo.

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

  2. Nella cartella radice selezionare Code (Codice), quindi Clone (Clona) o Download ZIP (Scarica ZIP) per creare una copia locale privata del progetto.

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

  4. Si tratta di un indice degli hotel, quindi digitare alcune parole che è possibile 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'app Web (Model-View-Controller) .

    Creazione di un progetto cloud

  2. Assegnare un nome al progetto, ad esempio "FirstSearchApp", e impostare la località. Selezionare Avanti.

  3. Accettare le impostazioni predefinite per il framework di destinazione, il tipo di autenticazione e HTTPS. Selezionare Crea.

  4. Installare la libreria client. In Strumenti>Gestione pacchetti>NuGet Gestisci pacchetti NuGet per la 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 un amministratore o una chiave API di 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, quindi in Proprietà impostare l'opzione Copia nella directory di output su Copia se più recente.

    Copia delle impostazioni dell'app nell'output

Modellare le strutture di dati

I modelli (classi C#) vengono usati per comunicare i dati tra il client (la vista), il server (il controller) e anche il cloud di Azure con l'architettura MVC (Model-View-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, oltre alla stringa di ricerca usata nelle comunicazioni tra visualizzazione e controller. Nell'indice di hotel, ogni hotel ha molte camere e un indirizzo in più parti. Nel complesso, la rappresentazione completa di un hotel è una struttura di dati gerarchici e annidati. Per creare ogni componente sono necessarie tre classi.

Le classi Hotel, Address e Room sono note come tipi complessi, una funzionalità importante di Ricerca cognitiva di Azure. I tipi complessi possono includere molti livelli al di sotto di classi e sottoclassi e consentire la rappresentazione di strutture di dati molto più complesse rispetto all'uso dei tipi semplici (una classe che contiene solo membri primitivi).

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

  2. Selezionare Classe e assegnare all'elemento il nome Hotel.cs. Sostituire il contenuto del file Hotel.cs con il codice seguente. Si noti che 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 la stessa procedura di creazione di un modello per la classe Address, assegnando al file il nome 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. Seguire di nuovo lo stesso processo per creare la classe Room, assegnando però al file il nome 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 che verrà creato in questa esercitazione è una classe denominata SearchData, che rappresenta l'input dell'utente (searchText) e l'output della ricerca (resultList). Il tipo di output, SearchResults<Hotel> , è importante poiché 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 includono diverse visualizzazioni client situate nella cartella Views. 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 l'intero contenuto del file Index.cshtml e ricreare il file con questa procedura.

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

  2. La prima riga del file Index.cshtml deve fare riferimento al modello che verrà usato per comunicare i dati tra il client (la visualizzazione) 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. Prima di tutto, 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 il codice seguente al file Index.cshtml e leggere con attenzione 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 l'opzione Generale evidenziata).

    Sostituire il codice predefinito con il codice seguente. Questo file non verrà inserito in altri dettagli, 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 con il nome hotels.css nella cartella wwwroot/css, insieme al file site.css predefinito.

In questo modo la vista è stata completata. A questo punto, i modelli e le visualizzazioni sono completati. Per concludere manca solo il controller.

Definire i metodi

In questo passaggio modificare il contenuto del controller home.

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

    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. In questa esercitazione il metodo predefinito, che non accetta parametri, viene usato per il caso d'uso di avvio dell'applicazione, ovvero il rendering di una pagina di ricerca vuota.

In questa sezione il metodo viene esteso per supportare un secondo caso d'uso, ovvero il rendering della pagina quando un utente immette il testo di ricerca. Per supportare questo caso, il metodo Index viene esteso in modo da 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);
        }
    

    Osservare la dichiarazione async del metodo e la chiamata await a RunQueryAsync. Queste parole chiave fanno sì che le chiamate siano asincrone, evitando così thread di blocco nel server.

    Il blocco catch usa il modello di errore predefinito creato.

Notare la gestione degli errori e altri metodi e viste 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 ed Errore. È possibile visualizzare queste pagine predefinite durante l'esecuzione dell'app ed esaminare come vengono gestite nel controller.

Verrà testata la visualizzazione Errore più avanti in questa esercitazione.

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

Aggiungere il metodo RunQueryAsync

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

  1. Prima di tutto aggiungere 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.

    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 si verifica prima di tutto che la configurazione di Azure sia stata avviata e quindi si impostano alcune opzioni di ricerca. L'opzione Select specifica quali campi restituire nei risultati per trovare una corrispondenza con i nomi delle proprietà nella classe hotel. Se si omette Select, vengono restituiti tutti i campi non gestiti, che possono essere 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 dovrebbe trovare 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 Debug>Avvia senza eseguire debug oppure premere F5. Se l'app viene eseguita come previsto, si dovrà ottenere la visualizzazione Indice iniziale.

    Apertura dell'app

  2. Immettere una stringa di query, ad esempio "beach" o un altro testo, quindi fare clic sull'icona di ricerca per inviare la richiesta.

    Ricerca di *beach*

  3. Provare a immettere "five star". Si noti che questa query non restituisce risultati. Una ricerca più sofisticata potrebbe considerare "five star" come un sinonimo di "luxury" e restituire 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 le voci con la parola "hotel" in loro. La ricerca individua solo parole intere, anche se vengono restituiti alcuni risultati.

  5. Provare con altre parole: "pool", "sunshine", "view" e così via. Vedrai Ricerca cognitiva di Azure lavorare con il suo livello più semplice, ma ancora convincente.

Testare errori e condizioni limite

È 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 forzerà un errore quando si esegue una ricerca nel testo.

  2. Eseguire l'app, immettere "bar" come testo di ricerca e fare clic sull'icona di ricerca. L'eccezione dovrebbe mostrare la vista di errore.

    Forzare un errore

    Importante

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

  3. Rimuovere genera nuove eccezioni() quando si è soddisfatti della gestione degli errori funziona come deve.

Risultati

Tenere conto delle considerazioni seguenti riguardo a questo progetto:

  • Una chiamata 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. Questa classe tuttavia può essere completata con molti membri che aggiungono complessità a una ricerca. Con un po' di lavoro, è possibile rendere questa app notevolmente più potente.

Passaggi successivi

Per migliorare l'esperienza utente, aggiungere più funzionalità, in particolare la paginazione (tramite numeri di pagina o scorrimento infinito) e il completamento automatico o i suggerimenti. È anche possibile prendere in considerazione altre opzioni di ricerca, ad esempio ricerche geografiche sugli alberghi all'interno di un raggio specificato di un determinato punto, nonché l'ordinamento dei risultati della ricerca.

Questi passaggi successivi sono illustrati nelle esercitazioni rimanenti. Si inizierà con l'impaginazione.