Bagikan melalui


Membuat aplikasi pencarian di ASP.NET Core

Dalam tutorial ini, buat aplikasi dasar ASP.NET Core (Model-View-Controller) yang berjalan di localhost dan terhubung ke hotel-sample-index di layanan pencarian Anda. Dalam tutorial ini, Anda akan mempelajari cara:

  • Membuat halaman pencarian dasar
  • Memfilter hasil
  • Urukan hasil

Tutorial ini menempatkan fokus pada operasi sisi server yang dipanggil melalui API Pencarian. Meskipun umum untuk mengurutkan dan memfilter dalam skrip sisi klien, mengetahui cara memanggil operasi ini di server memberi Anda lebih banyak opsi saat merancang pengalaman pencarian.

Kode sampel untuk tutorial ini dapat ditemukan di repositori azure-search-dotnet-samples di GitHub.

Prasyarat

Telusuri wizard Impor data untuk membuat indeks sampel hotel di layanan pencarian Anda. Atau, ubah nama indeks dalam HomeController.cs file.

Membuat proyek

  1. Mulai Visual Studio dan pilih Buat proyek baru.

  2. Pilih ASP.NET Core Web App (Model-View-Controller), lalu pilih Berikutnya.

  3. Berikan nama proyek, lalu pilih Berikutnya.

  4. Pada halaman berikutnya, pilih .NET 6.0 atau .NET 7.0 atau .NET 8.0.

  5. Verifikasi bahwa Jangan gunakan pernyataan tingkat atas tidak dicentang.

  6. Pilih Buat.

Menambahkan paket NuGet

  1. Pada Alat, pilih Manajer>Paket NuGet Kelola Paket NuGet untuk solusinya.

  2. Telusuri Azure.Search.Documents dan instal versi stabil terbaru.

  3. Telusuri dan instal Microsoft.Spatial paket. Indeks sampel menyertakan jenis data GeographyPoint. Menginstal paket ini menghindari kesalahan run time. Atau, hapus bidang "Lokasi" dari kelas Hotel jika Anda tidak ingin menginstal paket. Bidang tersebut tidak digunakan dalam tutorial ini.

Menambahkan informasi layanan

Untuk koneksi, aplikasi menyajikan kunci API kueri ke URL pencarian Anda yang sepenuhnya memenuhi syarat. Keduanya ditentukan dalam appsettings.json file.

Ubah appsettings.json untuk menentukan layanan pencarian dan kunci API kueri Anda.

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

Anda bisa mendapatkan URL layanan dan kunci API dari portal. Karena kode ini mengkueri indeks dan tidak membuatnya, Anda bisa menggunakan kunci kueri alih-alih kunci admin.

Pastikan untuk menentukan layanan pencarian yang memiliki hotel-sample-index.

Menambahkan model

Dalam langkah ini, buat model yang mewakili skema hotel-sample-index.

  1. Di Penjelajah solusi, pilih kanan Model dan tambahkan kelas baru bernama "Hotel" untuk kode berikut:

     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. Tambahkan kelas bernama "Address" dan ganti dengan kode berikut:

     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. Tambahkan kelas bernama "Rooms" dan ganti dengan kode berikut:

     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. Tambahkan kelas bernama "SearchData" dan ganti dengan kode berikut:

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

Mengubah pengontrol

Untuk tutorial ini, ubah default HomeController untuk berisi metode yang dijalankan pada layanan pencarian Anda.

  1. Di Penjelajah solusi di bawah Model, buka HomeController.

  2. Ganti default dengan konten berikut:

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

Mengubah tampilan

  1. Di Penjelajah solusi di bawah Beranda Tampilan>, buka .index.cshtml

  2. Ganti default dengan konten berikut:

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

Jalankan sampel

  1. Tekan F5 untuk mengkompilasi dan menjalankan proyek. Aplikasi ini berjalan di host lokal dan terbuka di browser default Anda.

  2. Pilih Cari untuk mengembalikan semua hasil.

  3. Kode ini menggunakan konfigurasi pencarian default, mendukung sintaksis sederhana dan searchMode=Any. Anda dapat memasukkan kata kunci, menambah dengan operator Boolean, atau menjalankan pencarian awalan (pool*).

Di beberapa bagian berikutnya, ubah metode RunQueryAsync di HomeController untuk menambahkan filter dan pengurutan.

Memfilter hasil

Atribut bidang indeks menentukan bidang mana yang dapat dicari, dapat difilter, dapat diurutkan, dapat difaset, dan dapat diambil. Di indeks sampel hotel, bidang yang dapat difilter termasuk Kategori, Alamat/Kota, dan Alamat/StateProvince. Contoh ini menambahkan ekspresi $Filter pada Kategori.

Filter selalu dijalankan terlebih dahulu, diikuti oleh kueri dengan asumsi kueri ditentukan.

  1. HomeController Buka dan temukan metode RunQueryAsync. Tambahkan Filter ke 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. Jalankan aplikasi lagi.

  3. Pilih Cari untuk menjalankan kueri kosong. Filter mengembalikan 18 dokumen alih-alih 50 asli.

Untuk informasi selengkapnya tentang ekspresi filter, lihat Filter di Azure AI Search dan sintaks $filter OData di Azure AI Search.

Urukan hasil

Di indeks sampel hotel, bidang yang dapat diurutkan termasuk Peringkat dan LastRenovated. Contoh ini menambahkan ekspresi $OrderBy ke bidang Peringkat.

  1. HomeController Buka dan ganti metode RunQueryAsync dengan versi berikut:

     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. Jalankan aplikasi lagi. Hasil diurutkan menurut Peringkat dalam urutan turun.

Untuk informasi selengkapnya tentang pengurutan, lihat sintaks $orderby OData di Azure AI Search.

Langkah berikutnya

Dalam tutorial ini, Anda membuat proyek ASP.NET Core (MVC) yang tersambung ke layanan pencarian dan disebut API Pencarian untuk pemfilteran dan pengurutan sisi server.

Jika Anda ingin menjelajahi kode sisi klien yang merespons tindakan pengguna, pertimbangkan untuk menambahkan templat React ke solusi Anda: