在 ASP.NET Core 中建立搜尋應用程式
在本教學課程中,建立在localhost中執行的基本 ASP.NET Core (Model-View-Controller) 應用程式,並連線到搜尋服務上的 hotels-sample-index。 在本教學課程中,您將了解如何:
- 建立基本搜尋頁面
- 篩選結果
- 排序結果
本教學課程會將焦點放在透過搜尋 API 呼叫的伺服器端作業。 雖然在用戶端腳本中很常見,但瞭解如何在伺服器上叫用這些作業,在設計搜尋體驗時提供更多選項。
本教學課程的範例程式代碼可在 GitHub 上的 azure-search-dotnet-samples 存放庫中找到。
必要條件
- Visual Studio
- Azure.Search.Documents NuGet 套件
- Azure AI 搜尋服務,任何層級,但必須具有公用網路存取權。
- 旅館範例索引
逐步執行匯入數據精靈 ,以在搜尋服務上建立 hotels-sample-index。 或者,變更檔案中的 HomeController.cs
索引名稱。
建立專案
啟動 Visual Studio 並選取 [建立新專案]。
選取 [ASP.NET Core Web 應用程式 (Model-View-Controller),然後選取 [ 下一步]。
提供專案名稱,然後選取 [ 下一步]。
在下一個頁面上,選取 [.NET 6.0] 或 [.NET 7.0] 或 [.NET 8.0]。
確認未勾選 [不要使用最上層陳述式]。
選取 建立。
新增 NuGet 套件
在 [工具] 上,針對解決方案選取 [NuGet 封裝管理員> 管理 NuGet 套件]。
Azure.Search.Documents
流覽並安裝最新的穩定版本。流覽並安裝
Microsoft.Spatial
套件。 範例索引包含 GeographyPoint 數據類型。 安裝此套件可避免運行時錯誤。 或者,如果您不想安裝套件,請從 Hotels 類別移除 [位置] 字段。 本教學課程中未使用該欄位。
新增服務資訊
針對連線,應用程式會將查詢 API 金鑰呈現給您的完整搜尋 URL。 這兩者都會在檔案中 appsettings.json
指定。
修改 appsettings.json
以指定您的搜尋服務和 查詢 API 金鑰。
{
"SearchServiceUri": "<YOUR-SEARCH-SERVICE-URL>",
"SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-QUERY-API-KEY>"
}
您可以從入口網站取得服務 URL 和 API 金鑰。 由於此程式代碼正在查詢索引,而不是建立索引,因此您可以使用查詢密鑰,而不是系統管理密鑰。
請務必指定具有 hotels-sample-index 的搜尋服務。
加入模型
在此步驟中,建立代表hotels-sample-index架構的模型。
在 [方案總管] 中,以滑鼠右鍵按兩下 [模型 ],並針對下列程式代碼新增名為 “Hotel” 的新類別:
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; } } }
新增名為 「Address」 的類別,並將它取代為下列程式代碼:
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; } } }
新增名為 「Rooms」 的類別,並將它取代為下列程式代碼:
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; } } }
新增名為 「SearchData」 的類別,並將它取代為下列程式代碼:
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; } }
修改控制器
在本教學課程中,修改預設值 HomeController
以包含在搜尋服務上執行的方法。
在 [模型] 下的 [方案總管] 中,開啟
HomeController
。將預設值取代為下列內容:
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(); } } }
修改檢視
在 [檢視>首頁] 底下的 [方案總管] 中,開啟 。
index.cshtml
將預設值取代為下列內容:
@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>
執行範例
按 F5 編譯並執行專案。 應用程式會在本機主機上執行,並在預設瀏覽器中開啟。
選取 [ 搜尋 ] 以傳回所有結果。
此程式代碼會使用預設搜尋組態,支援 簡單語法 和
searchMode=Any
。 您可以輸入關鍵詞、使用布爾運算符增強,或執行前置詞搜尋 (pool*
)。
在接下來的幾個區段中,修改 中的 HomeController
RunQueryAsync 方法以新增篩選和排序。
篩選結果
索引欄位屬性可決定哪些欄位可搜尋、可篩選、可排序、可 Facet 和可擷取。 在 hotels-sample-index 中,可篩選的字段包括 Category、Address/City 和 Address/StateProvince。 本範例會在 Category 上新增 $Filter 表示式。
篩選一律會先執行,後面接著假設已指定查詢。
開啟並
HomeController
尋找 RunQueryAsync 方法。 將篩選新增至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); }
執行應用程式。
選取 [ 搜尋 ] 以執行空的查詢。 篩選條件會傳回 18 份檔,而不是原始的 50 份檔。
如需篩選表達式的詳細資訊,請參閱 Azure AI 搜尋 中的篩選和 OData $filter Azure AI 搜尋中的語法。
排序結果
在 hotels-sample-index 中,可排序的字段包括 Rating 和 LastRenovated。 本範例會將$OrderBy表達式新增至 [評等] 字段。
開啟 ,
HomeController
並以下列版本取代 RunQueryAsync 方法: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); }
執行應用程式。 結果會以遞減順序依評等排序。
如需排序的詳細資訊,請參閱 Azure AI 搜尋中的 OData $orderby語法。
下一步
在本教學課程中,您已建立 ASP.NET Core (MVC) 專案,該專案已連線至搜尋服務,並呼叫搜尋 API 以進行伺服器端篩選和排序。
如果您要探索回應使用者動作的用戶端程式代碼,請考慮將 React 範本新增至您的解決方案: