本教學課程說明如何使用 Azure 認知搜尋和 Visual Studio 建立 Web 應用程式,以查詢及傳回搜尋索引的結果。
在本教學課程中,您將了解如何:
- 設定開發環境
- 模型數據結構
- 建立網頁以收集查詢輸入並顯示結果
- 定義搜尋方法
- 測試應用程式
您也將了解搜尋呼叫有多簡單。 程式碼中的關鍵語句包含在下列幾行中:
var options = new SearchOptions()
{
// The Select option specifies fields for the result set
options.Select.Add("HotelName");
options.Select.Add("Description");
};
var searchResult = await _searchClient.SearchAsync<Hotel>(model.searchText, options).ConfigureAwait(false);
model.resultList = searchResult.Value.GetResults().ToList();
只有一個呼叫會查詢搜尋索引並傳回結果。
概觀
本教學課程使用「hotels-sample-index」這個範例索引,您可以按照〈匯入數據快速入門〉中的指示,在自己的搜尋服務上快速建立。 索引包含虛構的酒店數據,可在每個搜尋服務中以內建數據源的形式提供。
本教學課程的第一課會建立基本的查詢結構和搜尋頁面,您將在後續的課程中增強,以包含分頁、Facet 和預先輸入體驗。
您可以在下列專案中找到已完成的程式碼版本:
先決條件
範例索引 (hotels-sample-index),裝載於您的搜尋服務上。
從 GitHub 安裝並執行專案
如果您想要跳到運作中的應用程式,請遵循下列步驟來下載並執行完成的程式代碼。
在 GitHub 上找出範例: 建立第一個應用程式。
在 根資料夾中,選取 [ 程序代碼],後面接著 [複製 ] 或 [ 下載 ZIP ],以建立專案的私人本機複本。
使用 Visual Studio,流覽至並開啟基本搜尋頁面的解決方案(「1-basic-search-page」),然後選取 [不偵錯啟動] 或按 F5 來建置和執行程式。
這是旅館索引,因此輸入一些字組,讓您可用來搜尋旅館(例如“wifi”、“view”、“bar”、“parking” )。 檢查結果。
這個應用程式包含更複雜的搜尋基本元件。 如果您不熟悉搜尋開發,您可以逐步重新建立此應用程式來瞭解工作流程。 下列各節將示範如何。
設定開發環境
若要從頭開始建立此專案,並因此強化 Azure 認知搜尋的概念,請從 Visual Studio 項目開始。
在 Visual Studio 中,選取 [ 新增>專案],然後 ASP.NET Core Web 應用程式 (Model-View-Controller) 。
提供項目的名稱,例如 「FirstSearchApp」,並設定位置。 選取 下一步。
接受目標架構、驗證類型和 HTTPS 的預設值。 選取 ,創建。
安裝用戶端程式庫。 在 [工具>NuGet 套件管理員>管理解決方案的 NuGet 套件...] 中,選取 [流覽 ],然後搜尋 “azure.search.documents”。 安裝 Azure.Search.Documents (版本 11 或更新版本),接受許可協定和相依性。
初始化 Azure 認知搜尋
在此步驟中,設定端點和存取密鑰,以連線到提供 旅館範例索引的搜尋服務。
開啟 appsettings.json ,並以搜尋服務 URL 和
https://<service-name>.search.windows.net
搜尋服務的 管理員或查詢 API 金鑰 取代預設行。 由於您不需要建立或更新索引,因此您可以使用本教學課程的查詢索引鍵。{ "SearchServiceUri": "<YOUR-SEARCH-SERVICE-URI>", "SearchServiceQueryApiKey": "<YOUR-SEARCH-SERVICE-API-KEY>" }
在 [方案總管] 中,選取檔案,然後在 [屬性] 中,將 [ 複製到輸出目錄 ] 設定變更為 [如果更新時複製]。
模型數據結構
模型(C# 類別)可用來使用MVC(模型、檢視、控制器)架構,在用戶端(檢視)、伺服器(控制器)和 Azure 雲端之間傳達數據。 一般而言,這些模型會反映所存取數據的結構。
在此步驟中,您將建立搜尋索引的數據結構模型,以及檢視/控制器通訊中使用的搜尋字串。 在旅館索引中,每個旅館都有許多房間,而且每家酒店都有多部分位址。 總的來說,完整表示旅館的是一個階層式和巢狀的數據結構。 您需要三個類別來建立每個元件。
旅館、位址和會議室類別集合稱為複雜類型,這是 Azure 認知搜尋的重要功能。 複雜型別可以是許多層級的類別和子類別,而且能夠比使用 簡單型 別來表示更複雜的數據結構(只包含基本成員的類別)。
在 [方案總管] 中,按一下滑鼠右鍵 模型>新增>新項目。
選取 [類別 ],並將專案命名為Hotel.cs。 以下列程式代碼取代Hotel.cs的所有內容。 請注意類別的 Address 和 Room 成員,這些字段本身是類別,因此您也需要模型。
using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; using Microsoft.Spatial; using System; using System.Text.Json.Serialization; namespace FirstAzureSearchApp.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 Room[] Rooms { get; set; } } }
重複為 Address 類別建立模型的相同程式,將檔案命名為Address.cs。 將內容取代為下列內容。
using Azure.Search.Documents.Indexes; namespace FirstAzureSearchApp.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; } } }
同樣地,請遵循相同的程式來建立 Room 類別,並將檔案命名為Room.cs。
using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; using System.Text.Json.Serialization; namespace FirstAzureSearchApp.Models { public partial class Room { [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 的類別,它代表使用者的輸入(searchText),以及搜尋的輸出(resultList)。 輸出的類型很重要, SearchResults<Hotel>,因為此類型完全符合搜尋的結果,因此您必須將此參考傳遞至檢視。 以下列程式代碼取代預設範本。
using Azure.Search.Documents.Models; namespace FirstAzureSearchApp.Models { public class SearchData { // The text to search for. public string searchText { get; set; } // The list of results. public SearchResults<Hotel> resultList; } }
建立網頁
專案範本隨附數個位於 Views 資料夾中的客戶端檢視。 確切檢視取決於您所使用的Core.NET版本(在此範例中使用3.1)。 在本教學課程中,您將修改 Index.cshtml 以包含搜尋頁面的元素。
完整刪除 Index.cshtml 的內容,並在下列步驟中重建檔案。
本教學課程使用檢視中的兩個小影像:Azure 標誌和搜尋放大鏡圖示(azure-logo.png 和 search.png)。 從 GitHub 專案將影像複製到您專案中的 wwwroot/images 資料夾。
Index.cshtml 的第一行應該參考用來在用戶端(檢視)與伺服器(控制器)之間通訊數據的模型,這是先前建立的 SearchData 模型。 將這一行新增至 Index.cshtml 檔案。
@model FirstAzureSearchApp.Models.SearchData
輸入檢視標題是標準做法,因此下一行應該是:
@{ ViewData["Title"] = "Home Page"; }
在標題之後,輸入 HTML 樣式表單的參考,您很快就會建立此樣式表單。
<head> <link rel="stylesheet" href="~/css/hotels.css" /> </head>
視圖的主體處理兩個使用案例。 首先,在第一次使用時,必須提供一個空白頁面,然後才能輸入任何搜尋文字。 其次,除了搜尋文字框之外,它還必須處理重複查詢的結果。
若要處理這兩種情況,您必須檢查提供給檢視的模型是否為 Null。 Null 模型表示第一個使用案例(應用程式的初始執行)。 將下列內容新增至 Index.cshtml 檔案,並讀取批注。
<body> <h1 class="sampleTitle"> <img src="~/images/azure-logo.png" width="80" /> Hotels Search </h1> @using (Html.BeginForm("Index", "Home", FormMethod.Post)) { // Display the search text box, with the search icon to the right of it. <div class="searchBoxForm"> @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox" }) <input class="searchBoxSubmit" type="submit" value=""> </div> @if (Model != null) { // Show the result count. <p class="sampleText"> @Model.resultList.TotalCount Results </p> var results = Model.resultList.GetResults().ToList(); @for (var i = 0; i < results.Count; i++) { // Display the hotel name and description. @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" }) @Html.TextArea($"desc{i}", results[i].Document.Description, new { @class = "box2" }) } } } </body>
新增樣式表單。 在 Visual Studio 的 [ 檔案>新>檔案] 中,選取 [樣式表單 ] (已醒目提示 [ 一般 ]。
將預設程式代碼取代為下方的程式碼。 我們不會更詳細地進入此檔案,樣式為標準 HTML。
textarea.box1 { width: 648px; height: 30px; border: none; background-color: azure; font-size: 14pt; color: blue; padding-left: 5px; } textarea.box2 { width: 648px; height: 100px; border: none; background-color: azure; font-size: 12pt; padding-left: 5px; margin-bottom: 24px; } .sampleTitle { font: 32px/normal 'Segoe UI Light',Arial,Helvetica,Sans-Serif; margin: 20px 0; font-size: 32px; text-align: left; } .sampleText { font: 16px/bold 'Segoe UI Light',Arial,Helvetica,Sans-Serif; margin: 20px 0; font-size: 14px; text-align: left; height: 30px; } .searchBoxForm { width: 648px; box-shadow: 0 0 0 1px rgba(0,0,0,.1), 0 2px 4px 0 rgba(0,0,0,.16); background-color: #fff; display: inline-block; border-collapse: collapse; border-spacing: 0; list-style: none; color: #666; } .searchBox { width: 568px; font-size: 16px; margin: 5px 0 1px 20px; padding: 0 10px 0 0; border: 0; max-height: 30px; outline: none; box-sizing: content-box; height: 35px; vertical-align: top; } .searchBoxSubmit { background-color: #fff; border-color: #fff; background-image: url(/images/search.png); background-repeat: no-repeat; height: 20px; width: 20px; text-indent: -99em; border-width: 0; border-style: solid; margin: 10px; outline: 0; }
將 stylesheet 檔案儲存為 hotels.css,並連同預設site.css檔案一起儲存至 wwwroot/css 資料夾。
這完成了我們的檢視。 此時,模型和視圖都已完成。 只有控制器能夠將所有部分系結在一起。
定義方法
在此步驟中,修改主控制器的內容。
開啟 HomeController.cs 檔案,並以下列程序代碼取代 using 語句。
using Azure; using Azure.Search.Documents; using Azure.Search.Documents.Indexes; using FirstAzureSearchApp.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks;
新增 Index 方法
在MVC應用程式中, Index() 方法是任何控制器的預設動作方法。 它會開啟索引 HTML 頁面。 本教學課程中會針對應用程式啟動使用案例使用預設方法,不採用任何參數:轉譯空的搜尋頁面。
在本節中,我們會擴充 方法以支援第二個使用案例:在使用者輸入搜尋文字時轉譯頁面。 為了支援此案例,索引方法會擴充為採用模型做為參數。
在預設 Index() 方法後面新增下列方法。
[HttpPost] public async Task<ActionResult> Index(SearchData model) { try { // Ensure the search string is valid. if (model.searchText == null) { model.searchText = ""; } // Make the Azure Cognitive Search call. await RunQueryAsync(model); } catch { return View("Error", new ErrorViewModel { RequestId = "1" }); } return View(model); }
請注意 方法的 async 宣告,以及 RunQueryAsync 的 await 呼叫。 這些關鍵詞會負責進行異步呼叫,因此避免在伺服器上封鎖線程。
catch 區塊會使用已建立的默認錯誤模型。
請注意錯誤處理和其他預設檢視和方法
根據您使用的 .NET Core 版本,會建立一組稍微不同的默認檢視。 針對 .NET Core 3.1,默認檢視為 Index、Privacy 和 Error。 您可以在執行應用程式時檢視這些預設頁面,並檢查它們在控制器中的處理方式。
稍後在本教學課程中,您將測試錯誤視圖。
在 GitHub 範例中,會刪除未使用的檢視及其相關聯的動作。
新增 RunQueryAsync 方法
Azure 認知搜尋呼叫會封裝在我們的 RunQueryAsync 方法中。
首先新增一些靜態變數來設定 Azure 服務,並呼叫 來起始它們。
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"); }
現在,新增 RunQueryAsync 方法本身。
private async Task<ActionResult> RunQueryAsync(SearchData model) { InitSearch(); var options = new SearchOptions() { IncludeTotalCount = true }; // Enter Hotel property names into this list so only these values will be returned. // If Select is empty, all values will be returned, which can be inefficient. options.Select.Add("HotelName"); 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); }
在此方法中,請先確定我們的 Azure 設定已起始,然後設定一些搜尋選項。 Select 選項會指定要在結果中傳回哪些欄位,因此符合旅館類別中的屬性名稱。 如果您省略 Select,則會傳回所有未隱藏的欄位,如果您只對所有可能欄位的子集感興趣,這可能會沒有效率。
非同步呼叫的搜尋會制定要求(模擬為 searchText)及回應(模擬為 searchResult)。 如果您正在偵錯這段程式碼,並且需要檢查 model.resultList 的內容,SearchResult 類別是不錯的設定斷點的選擇。 您應該發現它是直覺式的,只提供您要求的數據,而不是太多。
測試應用程式
現在,讓我們檢查應用程式是否正確執行。
> 選取 [不偵錯直接執行] 或按 F5。 如果應用程式如預期般執行,您應該會取得初始索引檢視。
輸入查詢字串,例如 「beach」 (或任何要注意的文字),然後按下搜尋圖示以傳送要求。
請嘗試輸入「五星」。 請注意,此查詢不會傳回任何結果。 更複雜的搜尋會將「五星」視為「豪華」的同義字,並傳回這些結果。 Azure 認知搜尋中提供 同義字 的支援,但本教學課程系列並未涵蓋。
嘗試輸入「熱」作為搜索字詞。 它不會傳回包含「飯店」一字的條目。 我們的搜尋只會尋找整個單字,不過會傳回一些結果。
請嘗試其他字:「pool」、 「sunshine」、 「view」以及其他任何詞。 您會看到 Azure 認知搜尋在最簡單的水平上運作,但仍令人信服。
測試邊緣條件和錯誤
確認我們的錯誤處理功能能夠正常運作是很重要的,即使在一切正常的情況下也一樣。
在 Index 方法中,try { 呼叫之後,輸入行 拋出新的 Exception()。 當您搜尋文字時,此例外狀況會導致錯誤。
執行應用程式,輸入 「bar」 作為搜尋文字,然後按下搜尋圖示。 例外狀況應該會導致錯誤畫面。
這很重要
將內部錯誤號碼顯示在錯誤頁面上被認為是一個安全風險。 如果您的應用程式適用於一般用途,請遵循發生錯誤時要傳回之內容的安全性最佳做法。
當您確認錯誤處理如預期運作時,請移除拋出新的例外()。
外賣
請考慮這個專案的下列要點:
- Azure 認知搜尋呼叫很簡潔,而且很容易解譯結果。
- 異步呼叫會使控制器稍微複雜化,但這是改善效能的最佳做法。
- 此應用程式會執行直接的文字搜尋,由 searchOptions 中設定的內容所定義。 不過,這個類別可以擁有許多成員,從而增加搜尋的複雜性。 稍微再多花點心思,您就可以讓這個應用程式變得更強大。
後續步驟
若要改善用戶體驗,請新增更多功能,特別是分頁(使用頁碼或無限捲動),以及自動完成或建議。 您也可以考慮其他搜尋選項(例如,指定點的指定半徑內旅館的地理搜尋),以及搜尋結果排序。
後續步驟會在其餘教學課程中加以解決。 讓我們從分頁開始。