Tworzenie aplikacji wyszukiwania w programie ASP.NET Core

W tym samouczku utwórz podstawową aplikację ASP.NET Core (Model-View-Controller), która działa na hoście lokalnym i łączy się z indeksem hotels-sample-index w usłudze wyszukiwania. Z tego samouczka dowiesz się, jak wykonywać następujące czynności:

  • Tworzenie podstawowej strony wyszukiwania
  • Filtruj wyniki
  • Sortowanie wyników

Ten samouczek koncentruje się na operacjach po stronie serwera wywoływanych za pośrednictwem interfejsów API wyszukiwania. Chociaż często sortowanie i filtrowanie skryptu po stronie klienta, wiedza na temat wywoływania tych operacji na serwerze zapewnia więcej opcji podczas projektowania środowiska wyszukiwania.

Przykładowy kod tego samouczka można znaleźć w repozytorium azure-search-dotnet-samples w witrynie GitHub.

Wymagania wstępne

Wykonaj kroki kreatora Importowanie danych, aby utworzyć indeks hotels-sample-index w usłudze wyszukiwania. Możesz też zmienić nazwę indeksu HomeController.cs w pliku.

Tworzenie projektu

  1. Uruchom program Visual Studio i wybierz pozycję Utwórz nowy projekt.

  2. Wybierz pozycję ASP.NET Core Web App (Model-View-Controller), a następnie wybierz przycisk Dalej.

  3. Podaj nazwę projektu, a następnie wybierz pozycję Dalej.

  4. Na następnej stronie wybierz pozycję .NET 6.0 lub .NET 7.0 lub .NET 8.0.

  5. Sprawdź, czy nie używaj instrukcji najwyższego poziomu jest niezaznaczone.

  6. Wybierz pozycję Utwórz.

Dodawanie pakietów NuGet

  1. W obszarze Narzędzia wybierz pozycję NuGet Menedżer pakietów> Zarządzaj pakietami NuGet dla rozwiązania.

  2. Azure.Search.Documents Wyszukaj i zainstaluj najnowszą stabilną wersję.

  3. Wyszukaj i zainstaluj Microsoft.Spatial pakiet. Przykładowy indeks zawiera typ danych GeographyPoint. Instalowanie tego pakietu pozwala uniknąć błędów czasu wykonywania. Alternatywnie usuń pole "Lokalizacja" z klasy Hotels, jeśli nie chcesz instalować pakietu. To pole nie jest używane w tym samouczku.

Dodawanie informacji o usłudze

W przypadku połączenia aplikacja przedstawia klucz interfejsu API zapytania do w pełni kwalifikowanego adresu URL wyszukiwania. Oba są określone w appsettings.json pliku.

Zmodyfikuj appsettings.json , aby określić usługę wyszukiwania i klucz interfejsu API zapytań.

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

Adres URL usługi i klucz interfejsu API można pobrać z portalu. Ponieważ ten kod wykonuje zapytanie względem indeksu i nie tworzy go, możesz użyć klucza zapytania zamiast klucza administratora.

Upewnij się, że określono usługę wyszukiwania z indeksem hotels-sample-index.

Dodawanie modeli

W tym kroku utwórz modele reprezentujące schemat indeksu hotels-sample-index.

  1. W Eksploratorze rozwiązań wybierz prawym przyciskiem pozycję Modele i dodaj nową klasę o nazwie "Hotel" dla następującego kodu:

     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. Dodaj klasę o nazwie "Address" i zastąp ją następującym kodem:

     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. Dodaj klasę o nazwie "Rooms" i zastąp ją następującym kodem:

     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. Dodaj klasę o nazwie "SearchData" i zastąp ją następującym kodem:

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

Modyfikowanie kontrolera

Na potrzeby tego samouczka zmodyfikuj wartość domyślną HomeController tak, aby zawierała metody wykonywane w usłudze wyszukiwania.

  1. W Eksploratorze rozwiązań w obszarze Modele otwórz plik HomeController.

  2. Zastąp wartość domyślną następującą zawartością:

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

Modyfikowanie widoku

  1. W Eksploratorze rozwiązań w obszarze Widoki>główne otwórz plik .index.cshtml

  2. Zastąp wartość domyślną następującą zawartością:

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

Uruchamianie aplikacji przykładowej

  1. Naciśnij klawisz F5 , aby skompilować i uruchomić projekt. Aplikacja działa na hoście lokalnym i zostanie otwarta w domyślnej przeglądarce.

  2. Wybierz pozycję Wyszukaj , aby zwrócić wszystkie wyniki.

  3. Ten kod używa domyślnej konfiguracji wyszukiwania, obsługującej prostą składnię i searchMode=Any. Możesz wprowadzić słowa kluczowe, rozszerzyć za pomocą operatorów logicznych lub uruchomić wyszukiwanie prefiksu (pool*).

W kolejnych sekcjach zmodyfikuj metodę RunQueryAsync w pliku , HomeController aby dodać filtry i sortowanie.

Filtruj wyniki

Atrybuty pola indeksu określają, które pola można przeszukiwać, filtrować, sortować, aspektów i pobierać. W polach hotels-sample-index można filtrować, takie jak Kategoria, Adres/Miasto i Adres/StateProvince. W tym przykładzie dodano wyrażenie $Filter w kategorii.

Filtr zawsze wykonuje najpierw, a następnie zapytanie przy założeniu, że jest określony.

  1. Otwórz plik HomeController i znajdź metodę RunQueryAsync . Dodaj filtr do elementu 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. Uruchom aplikację.

  3. Wybierz pozycję Wyszukaj , aby uruchomić puste zapytanie. Filtr zwraca 18 dokumentów zamiast oryginalnego 50.

Aby uzyskać więcej informacji na temat wyrażeń filtrów, zobacz Filtry w usługach Azure AI Search i OData $filter składni w usłudze Azure AI Search.

Sortowanie wyników

W polach hotels-sample-index pola sortowalne obejmują pozycje Ocena i LastRenovated. W tym przykładzie dodano wyrażenie $OrderBy do pola Ocena.

  1. Otwórz i zastąp metodę HomeController RunQueryAsync następującą wersją:

     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. Uruchom aplikację. Wyniki są sortowane według klasyfikacji w kolejności malejącej.

Aby uzyskać więcej informacji na temat sortowania, zobacz OData $orderby składni w usłudze Azure AI Search.

Następne kroki

W tym samouczku utworzono projekt platformy ASP.NET Core (MVC), który połączył się z usługą wyszukiwania i nazwał interfejsy API wyszukiwania na potrzeby filtrowania i sortowania po stronie serwera.

Jeśli chcesz eksplorować kod po stronie klienta, który odpowiada na akcje użytkownika, rozważ dodanie szablonu React do rozwiązania: