Vytvoření vyhledávací aplikace v ASP.NET Core

V tomto kurzu vytvořte základní aplikaci ASP.NET Core (Model-View-Controller), která běží v místním hostiteli a připojuje se k indexu hotels-sample-index ve vyhledávací službě. V tomto kurzu se naučíte:

  • Vytvoření základní vyhledávací stránky
  • Filtrování výsledků
  • Řazení výsledků

V tomto kurzu se zaměříme na operace na straně serveru volané prostřednictvím rozhraní API vyhledávání. I když je běžné řadit a filtrovat ve skriptu na straně klienta, znalost toho, jak tyto operace vyvolat na serveru, nabízí při návrhu vyhledávacího prostředí více možností.

Vzorový kód pro tento kurz najdete v úložišti azure-search-dotnet-samples na GitHubu.

Požadavky

Projděte si průvodce importem dat a vytvořte ve vyhledávací službě index hotels-sample-index. Nebo změňte název indexu HomeController.cs v souboru.

Vytvoření projektu

  1. Spusťte Visual Studio a vyberte Vytvořit nový projekt.

  2. Vyberte ASP.NET Základní webová aplikace (Model-View-Controller) a pak vyberte Další.

  3. Zadejte název projektu a pak vyberte Další.

  4. Na další stránce vyberte .NET 6.0 nebo .NET 7.0 nebo .NET 8.0.

  5. Ověřte, že není zaškrtnuto políčko Nepoužívat příkazy nejvyšší úrovně.

  6. Vyberte Vytvořit.

Přidání balíčků NuGet

  1. V nástrojích vyberte NuGet Správce balíčků> Nabídky NuGet pro řešení.

  2. Azure.Search.Documents Vyhledejte a nainstalujte nejnovější stabilní verzi.

  3. Vyhledejte a nainstalujte Microsoft.Spatial balíček. Ukázkový index obsahuje datový typ GeographyPoint. Instalace tohoto balíčku zabraňuje chybám za běhu. Pokud nechcete balíček nainstalovat, odeberte pole Umístění z třídy Hotely. Toto pole se v tomto kurzu nepoužívá.

Přidání informací o službě

Pro připojení aplikace zobrazí klíč rozhraní API dotazu na plně kvalifikovanou adresu URL vyhledávání. Oba jsou v souboru zadány appsettings.json .

Upravte appsettings.json zadání vyhledávací služby a klíče rozhraní API pro dotazy.

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

Adresu URL služby a klíč rozhraní API můžete získat z portálu. Vzhledem k tomu, že tento kód dotazuje index a nevytvořuje ho, můžete místo klíče správce použít klíč dotazu.

Nezapomeňte zadat vyhledávací službu, která má index hotels-sample-index.

Přidání modelů

V tomto kroku vytvořte modely, které představují schéma hotels-sample-index.

  1. V Průzkumníku řešení vyberte pravým tlačítkem myši Modely a přidejte novou třídu s názvem "Hotel" pro následující kód:

     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. Přidejte třídu s názvem Address a nahraďte ji následujícím kódem:

     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. Přidejte třídu s názvem Rooms a nahraďte ji následujícím kódem:

     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. Přidejte třídu s názvem SearchData a nahraďte ji následujícím kódem:

     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;
         }
     }
    

Úprava kontroleru

V tomto kurzu upravte výchozí hodnotu HomeController tak, aby obsahovala metody spouštěné ve vyhledávací službě.

  1. V Průzkumníku řešení v části Modely otevřete HomeController.

  2. Nahraďte výchozí obsah následujícím obsahem:

    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();
             }
         }
     }
    

Úprava zobrazení

  1. V Průzkumníku řešení v části Domovská stránka zobrazení>otevřete index.cshtml.

  2. Nahraďte výchozí obsah následujícím obsahem:

    @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>
    

Spuštění ukázky

  1. Stisknutím klávesy F5 projekt zkompilujte a spusťte. Aplikace běží na místním hostiteli a otevře se ve výchozím prohlížeči.

  2. Výběrem možnosti Hledat vrátíte všechny výsledky.

  3. Tento kód používá výchozí konfiguraci vyhledávání, která podporuje jednoduchou syntaxi a searchMode=Any. Můžete zadat klíčová slova, rozšířit o logické operátory nebo spustit vyhledávání předpon (pool*).

V dalších několika částech upravte metodu RunQueryAsync v přidání HomeController filtrů a řazení.

Filtrování výsledků

Atributy pole indexu určují, která pole jsou prohledávatelná, filtrovatelná, řaditelná, fasetová a načístelná. V indexu hotels-sample-index jsou filtrovatelná pole Kategorie, Adresa/Město a Address/StateProvince. Tento příklad přidá výraz $Filter v kategorii.

Filtr se vždy spustí jako první, po kterém následuje dotaz za předpokladu, že je zadaný.

  1. Otevřete metodu HomeControllerRunQueryAsync a vyhledejte ji. Přidat filtr do 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. Aplikaci spusťte.

  3. Výběrem možnosti Hledat spustíte prázdný dotaz. Filtr vrátí místo původního čísla 50 18 dokumentů.

Další informace o výrazech filtru najdete v tématu Filtry ve službě Azure AI Search a syntaxi $filter OData ve službě Azure AI Search.

Řazení výsledků

V indexu hotels-sample-index jsou seřazená pole Hodnocení a LastRenovated. Tento příklad přidá výraz $OrderBy do pole Hodnocení.

  1. Otevřete a nahraďte metodu HomeController RunQueryAsync následující verzí:

     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. Aplikaci spusťte. Výsledky jsou seřazené podle hodnocení v sestupném pořadí.

Další informace o řazení najdete v tématu OData $orderby syntaxe ve službě Azure AI Search.

Další kroky

V tomto kurzu jste vytvořili projekt ASP.NET Core (MVC), který se připojil k vyhledávací službě a volal rozhraní API pro vyhledávání pro filtrování a řazení na straně serveru.

Pokud chcete prozkoumat kód na straně klienta, který reaguje na akce uživatelů, zvažte přidání šablony React do vašeho řešení: