ASP.NET Core'da arama uygulaması oluşturma

Bu öğreticide, localhost'ta çalışan ve arama hizmetinizdeki hotels-sample-index öğesine bağlanan temel bir ASP.NET Core (Model-View-Controller) uygulaması oluşturun. Bu öğreticide aşağıdakilerin nasıl yapılacağını öğreneceksiniz:

  • Temel arama sayfası oluşturma
  • Sonuçları filtrele
  • Sonuçları sıralama

Bu öğretici, odağı Arama API'leri aracılığıyla çağrılan sunucu tarafı işlemlerine getirir. İstemci tarafı betiğinde sıralamak ve filtrelemek yaygın olsa da, bu işlemlerin sunucuda nasıl çağrılılacağını bilmek, arama deneyimini tasarlarken size daha fazla seçenek sunar.

Bu öğreticinin örnek kodu GitHub'daki azure-search-dotnet-samples deposunda bulunabilir.

Önkoşullar

Arama hizmetinizde hotels-sample-index oluşturmak için Verileri içeri aktarma sihirbazında ilerleyin. Alternatif olarak, dosyadaki dizin adını da HomeController.cs değiştirebilirsiniz.

Proje oluşturma

  1. Visual Studio'yu başlatın ve Yeni proje oluştur'u seçin.

  2. ASP.NET Core Web App (Model-View-Controller) öğesini ve ardından İleri'yi seçin.

  3. Bir proje adı girin ve İleri'yi seçin.

  4. Sonraki sayfada .NET 6.0 veya .NET 7.0 veya .NET 8.0'ı seçin.

  5. Üst düzey deyimleri kullanma seçeneğinin işaretli olmadığını doğrulayın.

  6. Oluştur'u belirleyin.

NuGet paketlerini ekleme

  1. Araçlar'da Çözüm için NuGet Paket Yöneticisi> Yönetim NuGet Paketlerini Yönet'i seçin.

  2. En son kararlı sürümü bulun Azure.Search.Documents ve yükleyin.

  3. Paketi bulun ve yükleyin Microsoft.Spatial . Örnek dizin bir GeographyPoint veri türü içerir. Bu paketin yüklenmesi çalışma zamanı hatalarını önler. Alternatif olarak, paketi yüklemek istemiyorsanız Hotels sınıfından "Konum" alanını kaldırın. Bu alan bu öğreticide kullanılmaz.

Hizmet bilgileri ekleme

Bağlantı için uygulama, tam arama URL'nize bir sorgu API'si anahtarı sunar. Her ikisi de dosyada appsettings.json belirtilir.

Arama hizmetinizi ve sorgu API anahtarınızı belirtmek için değiştirinappsettings.json.

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

Hizmet URL'sini ve API anahtarını portaldan alabilirsiniz. Bu kod bir dizini sorguladığı ve oluşturmadığı için yönetici anahtarı yerine sorgu anahtarı kullanabilirsiniz.

hotels-sample-index içeren arama hizmetini belirttiğinizden emin olun.

Model ekleme

Bu adımda hotels-sample-index şemasını temsil eden modeller oluşturun.

  1. Çözüm gezgininde Modeller'i sağ seçin ve aşağıdaki kod için "Hotel" adlı yeni bir sınıf ekleyin:

     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. "Address" adlı bir sınıf ekleyin ve bunu aşağıdaki kodla değiştirin:

     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. "Rooms" adlı bir sınıf ekleyin ve bunu aşağıdaki kodla değiştirin:

     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. "SearchData" adlı bir sınıf ekleyin ve bunu aşağıdaki kodla değiştirin:

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

Denetleyiciyi değiştirme

Bu öğreticide, arama hizmetinizde yürütülen yöntemleri içeren varsayılan HomeController değeri değiştirin.

  1. Çözüm gezgini'nde Modeller'in altında öğesini açınHomeController.

  2. Varsayılan değeri aşağıdaki içerikle değiştirin:

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

Görünümü değiştirme

  1. Çözüm gezgininde, Görünümler>Giriş'in altında öğesini açın.index.cshtml

  2. Varsayılan değeri aşağıdaki içerikle değiştirin:

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

Örneği çalıştırma

  1. Projeyi derlemek ve çalıştırmak için F5 tuşuna basın. Uygulama yerel ana bilgisayarda çalışır ve varsayılan tarayıcınızda açılır.

  2. Tüm sonuçları döndürmek için Ara'yı seçin.

  3. Bu kod, ve basit söz dizimini searchMode=Anydestekleyen varsayılan arama yapılandırmasını kullanır. Anahtar sözcükler girebilir, Boole işleçleriyle genişletebilir veya bir ön ek araması (pool*) çalıştırabilirsiniz.

Sonraki birkaç bölümde, filtre ve sıralama eklemek için içindeki HomeController RunQueryAsync yöntemini değiştirin.

Sonuçları filtrele

Dizin alanı öznitelikleri hangi alanların aranabilir, filtrelenebilir, sıralanabilir, modellenebilir ve alınabilir olduğunu belirler. hotels-sample-index içinde filtrelenebilir alanlar Kategori, Adres/Şehir ve Adres/StateProvince'tır. Bu örnek, Category'ye bir $Filter ifadesi ekler.

Her zaman önce bir filtre yürütülür, ardından bir sorgu belirtilir.

  1. HomeController öğesini açın ve RunQueryAsync yöntemini bulun. Içine var options = new SearchOptions()Filtre Ekle:

     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. Uygulamayı çalıştırın.

  3. Boş bir sorgu çalıştırmak için Ara'yı seçin. Filtre, özgün 50 yerine 18 belge döndürür.

Filtre ifadeleri hakkında daha fazla bilgi için bkz. Azure AI Search'teki filtreler ve Azure AI Search'te OData $filter söz dizimi.

Sonuçları sıralama

Hotels-sample-index içinde sıralanabilir alanlar Derecelendirme ve LastRenovated alanlarını içerir. Bu örnek Derecelendirme alanına bir $OrderBy ifadesi ekler.

  1. HomeController ve runQueryAsync yöntemini aşağıdaki sürümle değiştirin:

     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. Uygulamayı çalıştırın. Sonuçlar Derecelendirmeye göre azalan düzende sıralanır.

Sıralama hakkında daha fazla bilgi için bkz . Azure AI Search'te OData $orderby söz dizimi.

Sonraki adımlar

Bu öğreticide, bir arama hizmetine bağlı olan ve sunucu tarafı filtreleme ve sıralama için Arama API'leri olarak adlandırılan bir ASP.NET Core (MVC) projesi oluşturdunuz.

Kullanıcı eylemlerine yanıt veren istemci tarafı kodunu keşfetmek istiyorsanız çözümünüze bir React şablonu eklemeyi göz önünde bulundurun: