Condividi tramite


Creare un'app di ricerca in ASP.NET Core

In questa esercitazione creare un'app di base ASP.NET Core (Model-View-Controller) che viene eseguita in localhost e si connette all'indice hotels-sample-index nel servizio di ricerca. In questa esercitazione, scopri come:

  • Creare una pagina di ricerca di base
  • Filtrare i risultati
  • Ordinare i risultati

Questa esercitazione mette a fuoco le operazioni sul lato server chiamate tramite le API di ricerca. Anche se è comune ordinare e filtrare nello script lato client, sapere come richiamare queste operazioni sul server offre più opzioni durante la progettazione dell'esperienza di ricerca.

Il codice di esempio per questa esercitazione è disponibile nel repository azure-search-dotnet-samples su GitHub.

Prerequisiti

Eseguire la procedura guidata Importa dati per creare l'indice hotels-sample-nel servizio di ricerca. In alternativa, modificare il nome dell'indice nel HomeController.cs file.

Creare il progetto

  1. Avviare Visual Studio e selezionare Crea un nuovo progetto.

  2. Selezionare ASP.NET Core Web App (Model-View-Controller) e quindi selezionare Avanti.

  3. Specificare un nome di progetto e quindi selezionare Avanti.

  4. Nella pagina successiva selezionare .NET 6.0 o .NET 7.0 o .NET 8.0.

  5. Verificare che non usare istruzioni di primo livello sia deselezionata.

  6. Seleziona Crea.

Aggiungere i pacchetti NuGet di

  1. In Strumenti selezionare NuGet Gestione pacchetti> Gestisci pacchetti NuGet per la soluzione.

  2. Azure.Search.Documents Cercare e installare la versione stabile più recente.

  3. Cercare e installare il Microsoft.Spatial pacchetto. L'indice di esempio include un tipo di dati GeographyPoint. L'installazione di questo pacchetto evita errori di runtime. In alternativa, rimuovere il campo "Location" dalla classe Hotels se non si vuole installare il pacchetto. Questo campo non viene usato in questa esercitazione.

Aggiungere informazioni sul servizio

Per la connessione, l'app presenta una chiave API di query all'URL di ricerca completo. Entrambi sono specificati nel appsettings.json file .

Modificare appsettings.json per specificare il servizio di ricerca e la chiave API di query.

{
    "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URL>",
    "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-QUERY-API-KEY>"
}

È possibile ottenere l'URL del servizio e la chiave API dal portale. Poiché questo codice esegue query su un indice e non ne crea uno, è possibile usare una chiave di query anziché una chiave di amministrazione.

Assicurarsi di specificare il servizio di ricerca con hotels-sample-index.

Aggiungi modelli

In questo passaggio creare modelli che rappresentano lo schema dell'indice hotels-sample-index.

  1. In Esplora soluzioni selezionare con il pulsante destro del codice Modelli e aggiungere una nuova classe denominata "Hotel" per il codice seguente:

     using Azure.Search.Documents.Indexes.Models;
     using Azure.Search.Documents.Indexes;
     using Microsoft.Spatial;
     using System.Text.Json.Serialization;
    
     namespace HotelDemoApp.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 Rooms[] Rooms { get; set; }
         }
     }
    
  2. Aggiungere una classe denominata "Address" e sostituirla con il codice seguente:

     using Azure.Search.Documents.Indexes;
    
     namespace HotelDemoApp.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; }
         }
     }
    
  3. Aggiungere una classe denominata "Rooms" e sostituirla con il codice seguente:

     using Azure.Search.Documents.Indexes.Models;
     using Azure.Search.Documents.Indexes;
     using System.Text.Json.Serialization;
    
     namespace HotelDemoApp.Models
     {
         public partial class Rooms
         {
             [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; }
         }
     }
    
  4. Aggiungere una classe denominata "SearchData" e sostituirla con il codice seguente:

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

Modificare il controller

Per questa esercitazione, modificare l'impostazione predefinita HomeController in modo che contenga i metodi eseguiti nel servizio di ricerca.

  1. In Esplora soluzioni in Modelli aprire HomeController.

  2. Sostituire il valore predefinito con il contenuto seguente:

    using Azure;
     using Azure.Search.Documents;
     using Azure.Search.Documents.Indexes;
     using HotelDemoApp.Models;
     using Microsoft.AspNetCore.Mvc;
     using System.Diagnostics;
    
     namespace HotelDemoApp.Controllers
     {
         public class HomeController : Controller
         {
             public IActionResult Index()
             {
                 return View();
             }
    
             [HttpPost]
             public async Task<ActionResult> Index(SearchData model)
             {
                 try
                 {
                     // Check for a search string
                     if (model.searchText == null)
                     {
                         model.searchText = "";
                     }
    
                     // Send the query to Search.
                     await RunQueryAsync(model);
                 }
    
                 catch
                 {
                     return View("Error", new ErrorViewModel { RequestId = "1" });
                 }
                 return View(model);
             }
    
             [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
             public IActionResult Error()
             {
                 return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
             }
    
             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-sample-index");
             }
    
             private async Task<ActionResult> RunQueryAsync(SearchData model)
             {
                 InitSearch();
    
                 var options = new SearchOptions()
                 {
                     IncludeTotalCount = true
                 };
    
                 // Enter Hotel property names to specify which fields are returned.
                 // If Select is empty, all "retrievable" fields are returned.
                 options.Select.Add("HotelName");
                 options.Select.Add("Category");
                 options.Select.Add("Rating");
                 options.Select.Add("Tags");
                 options.Select.Add("Address/City");
                 options.Select.Add("Address/StateProvince");
                 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);
             }
             public IActionResult Privacy()
             {
                 return View();
             }
         }
     }
    

Modificare la visualizzazione

  1. In Esplora soluzioni in Visualizzazioni>home aprire .index.cshtml

  2. Sostituire il valore predefinito con il contenuto seguente:

    @model HotelDemoApp.Models.SearchData;
    
    @{
        ViewData["Title"] = "Index";
    }
    
    <div>
        <h2>Search for Hotels</h2>
    
        <p>Use this demo app to test server-side sorting and filtering. Modify the RunQueryAsync method to change the operation. The app uses the default search configuration (simple search syntax, with searchMode=Any).</p>
    
        <form asp-controller="Home" asp-action="Index">
            <p>
                <input type="text" name="searchText" />
                <input type="submit" value="Search" />
            </p>
        </form>
    </div>
    
    <div>
        @using (Html.BeginForm("Index", "Home", FormMethod.Post))
        {
            @if (Model != null)
            {
                // Show the result count.
                <p>@Model.resultList.TotalCount Results</p>
    
                // Get search results.
                var results = Model.resultList.GetResults().ToList();
    
                {
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Category</th>
                                <th>Rating</th>
                                <th>Tags</th>
                                <th>City</th>
                                <th>State</th>
                                <th>Description</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var d in results)
                            {
                                <tr>
                                    <td>@d.Document.HotelName</td>
                                    <td>@d.Document.Category</td>
                                    <td>@d.Document.Rating</td>
                                    <td>@d.Document.Tags[0]</td>
                                    <td>@d.Document.Address.City</td>
                                    <td>@d.Document.Address.StateProvince</td>
                                    <td>@d.Document.Description</td>
                                </tr>
                            }
                        </tbody>
                      </table>
                }
            }
        }
    </div>
    

Eseguire l'esempio

  1. Premere F5 per compilare ed eseguire il progetto. L'app viene eseguita nell'host locale e si apre nel browser predefinito.

  2. Selezionare Cerca per restituire tutti i risultati.

  3. Questo codice usa la configurazione di ricerca predefinita, supportando la sintassi semplice e searchMode=Any. È possibile immettere parole chiave, aumentare con operatori booleani o eseguire una ricerca di prefisso (pool*).

Nelle sezioni successive modificare il metodo RunQueryAsync in HomeController per aggiungere filtri e ordinamento.

Filtrare i risultati

Gli attributi dei campi di indice determinano quali campi sono ricercabili, filtrabili, ordinabili, facetable e recuperabili. Nei campi hotels-sample-index i campi filtrabili includono Category, Address/City e Address/StateProvince. In questo esempio viene aggiunta un'espressione $Filter in Category.

Un filtro viene sempre eseguito per primo, seguito da una query presupponendo che ne venga specificata una.

  1. HomeController Aprire e trovare il metodo RunQueryAsync. Aggiungere un filtro a var options = new SearchOptions():

     private async Task<ActionResult> RunQueryAsync(SearchData model)
     {
         InitSearch();
    
         var options = new SearchOptions()
         {
             IncludeTotalCount = true,
             Filter = "search.in(Category,'Budget,Suite')"
         };
    
         options.Select.Add("HotelName");
         options.Select.Add("Category");
         options.Select.Add("Rating");
         options.Select.Add("Tags");
         options.Select.Add("Address/City");
         options.Select.Add("Address/StateProvince");
         options.Select.Add("Description");
    
         model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
    
         return View("Index", model);
     }
    
  2. Eseguire l'applicazione.

  3. Selezionare Cerca per eseguire una query vuota. Il filtro restituisce 18 documenti anziché l'originale 50.

Per altre informazioni sulle espressioni di filtro, vedere Filtri in Ricerca di intelligenza artificiale di Azure e sintassi di $filter OData in Ricerca di intelligenza artificiale di Azure.

Ordinare i risultati

Nei campi hotels-sample-index i campi ordinabili includono Rating e LastRenovated. In questo esempio viene aggiunta un'espressione $OrderBy al campo Rating .

  1. Aprire e sostituire il HomeController metodo RunQueryAsync con la versione seguente:

     private async Task<ActionResult> RunQueryAsync(SearchData model)
     {
         InitSearch();
    
         var options = new SearchOptions()
         {
             IncludeTotalCount = true,
         };
    
         options.OrderBy.Add("Rating desc");
    
         options.Select.Add("HotelName");
         options.Select.Add("Category");
         options.Select.Add("Rating");
         options.Select.Add("Tags");
         options.Select.Add("Address/City");
         options.Select.Add("Address/StateProvince");
         options.Select.Add("Description");
    
         model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
    
         return View("Index", model);
     }
    
  2. Eseguire l'applicazione. I risultati vengono ordinati in base alla classificazione in ordine decrescente.

Per altre informazioni sull'ordinamento, vedere Sintassi di $orderby OData in Ricerca di intelligenza artificiale di Azure.

Passaggi successivi

In questa esercitazione è stato creato un progetto ASP.NET Core (MVC) connesso a un servizio di ricerca e sono state chiamate API di ricerca per il filtro sul lato server e l'ordinamento.

Se si vuole esplorare il codice lato client che risponde alle azioni dell'utente, è consigliabile aggiungere un modello React alla soluzione: