このチュートリアル シリーズでは、結果が返され、 既定の順序で表示されています。 このチュートリアルでは、プライマリとセカンダリの並べ替え条件を追加します。 最後の例では、数値に基づく順序付けの代わりに、カスタム スコアリング プロファイルに基づいて結果をランク付けする方法を示します。 また、 複合型の表示についても少し詳しく見ていきます。
このチュートリアルでは、以下の内容を学習します。
- 1 つのプロパティに基づいて結果を並べ替えます
- 複数のプロパティに基づいて結果を並べ替えます
- スコアリング プロファイルに基づいて結果を並べ替えます
概要
このチュートリアルでは、「 検索結果にページングを追加 する」チュートリアルで作成した無限スクロール プロジェクトを拡張します。
このチュートリアルのコードの完成版は、次のプロジェクトにあります。
[前提条件]
- 2b-add-infinite-scroll (GitHub) ソリューション。 このプロジェクトは、前のチュートリアルからビルドされた独自のバージョンか、GitHub からのコピーのいずれかになります。
1 つのプロパティに基づいて結果を並べ替えます
ホテルの評価など、1 つのプロパティに基づいて結果を注文する場合、順序付けされた結果が必要なだけでなく、注文が正しいことを確認する必要もあります。 結果に [評価] フィールドを追加すると、結果が正しく並べ替えられているかどうかを確認できます。
この演習では、結果の表示にもう少し追加します。各ホテルの最も安い客室料金と最も高価な客室料金です。
順序付けを有効にするために、モデルを変更する必要はありません。 更新が必要なのは、ビューとコントローラーだけです。 まず、ホーム コントローラーを開きます。
OrderBy プロパティを検索パラメーターに追加する
HomeController.csで、OrderByオプションを追加し、Rating プロパティを降順で含めます。 Index(SearchData モデル) メソッドで、検索パラメーターに次の行を追加します。
options.OrderBy.Add("Rating desc");注
既定の順序は昇順ですが、プロパティに
ascを追加してこれを明確にすることができます。 降順は、descを追加して指定します。次に、アプリを実行し、一般的な検索用語を入力します。 結果は、ユーザーではなく開発者のどちらでも、結果を検証する簡単な方法を持っていないので、正しい順序である場合とそうでない場合があります。
評価に基づいて結果が並べ替えられたことを明確にしましょう。 まず、hotels.css ファイル内の box1 クラスと box2 クラスを次のクラスに置き換えます (これらのクラスはすべて、このチュートリアルに必要な新しいクラスです)。
textarea.box1A { width: 324px; height: 32px; border: none; background-color: azure; font-size: 14pt; color: blue; padding-left: 5px; text-align: left; } textarea.box1B { width: 324px; height: 32px; border: none; background-color: azure; font-size: 14pt; color: blue; text-align: right; padding-right: 5px; } textarea.box2A { width: 324px; height: 32px; border: none; background-color: azure; font-size: 12pt; color: blue; padding-left: 5px; text-align: left; } textarea.box2B { width: 324px; height: 32px; border: none; background-color: azure; font-size: 12pt; color: blue; text-align: right; padding-right: 5px; } textarea.box3 { width: 648px; height: 100px; border: none; background-color: azure; font-size: 12pt; padding-left: 5px; margin-bottom: 24px; }ヒント
通常、ブラウザーは css ファイルをキャッシュするため、古い css ファイルが使用される可能性があり、編集は無視されます。 これを丸める良い方法は、バージョン パラメーターを含むクエリ文字列をリンクに追加することです。 例えば次が挙げられます。
<link rel="stylesheet" href="~/css/hotels.css?v1.1" />ブラウザーで古い css ファイルが使用されていると思われる場合は、バージョン番号を更新します。
結果に次の 3 つのフィールドが含まれるように、Index(SearchData モデル) メソッドの Select パラメーターに Rating プロパティを追加します。
options.Select.Add("HotelName"); options.Select.Add("Description"); options.Select.Add("Rating");ビュー (index.cshtml) を開き、レンダリング ループ (<!--ホテル データを表示します。-->) を次のコードに置き換えます。
<!-- Show the hotel data. --> @for (var i = 0; i < result.Count; i++) { var ratingText = $"Rating: {result[i].Document.Rating}"; // Display the hotel details. @Html.TextArea($"name{i}", result[i].Document.HotelName, new { @class = "box1A" }) @Html.TextArea($"rating{i}", ratingText, new { @class = "box1B" }) @Html.TextArea($"desc{i}", fullDescription, new { @class = "box3" }) }評価は、最初に表示されるページと、無限スクロールで呼び出される後続のページの両方で使用できる必要があります。 これら 2 つの状況の後者では、コントローラーの Next アクションとビュー内の スクロール 関数の両方を更新する必要があります。 コントローラーから始めて、 Next メソッドを次のコードに変更します。 このコードは、評価テキストを作成して伝達します。
public async Task<ActionResult> Next(SearchData model) { // Set the next page setting, and call the Index(model) action. model.paging = "next"; await Index(model); // Create an empty list. var nextHotels = new List<string>(); // Add a hotel details to the list. await foreach (var result in model.resultList.GetResultsAsync()) { var ratingText = $"Rating: {result.Document.Rating}"; var rateText = $"Rates from ${result.Document.cheapest} to ${result.Document.expensive}"; string fullDescription = result.Document.Description; // Add strings to the list. nextHotels.Add(result.Document.HotelName); nextHotels.Add(ratingText); nextHotels.Add(fullDescription); } // Rather than return a view, return the list of data. return new JsonResult(nextHotels); }次に、ビューの スクロール関数 を更新して、評価テキストを表示します。
<script> function scrolled() { if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) { $.getJSON("/Home/Next", function (data) { var div = document.getElementById('myDiv'); // Append the returned data to the current list of hotels. for (var i = 0; i < data.length; i += 3) { div.innerHTML += '\n<textarea class="box1A">' + data[i] + '</textarea>'; div.innerHTML += '\n<textarea class="box1B">' + data[i + 1] + '</textarea>'; div.innerHTML += '\n<textarea class="box3">' + data[i + 2] + '</textarea>'; } }); } } </script>次に、アプリをもう一度実行します。 "wifi" などの一般的な用語を検索し、結果がホテル評価の降順で並べ替えられたことを確認します。
複数のホテルが同じ評価を持っていることに気付くでしょう。そのため、ディスプレイでの外観は、データが見つかった順序 (任意) になります。
2 番目のレベルの注文を追加する前に、部屋の料金の範囲を表示するコードを追加しましょう。 このコードを追加して 、複合型からデータを抽出することを示しています。また、価格 (おそらく最も安い方) に基づいて順序付けの結果について説明することもできます。
客室料金の範囲をビューに追加する
Hotel.cs モデルに、最も安価で最も高価なルーム レートを含むプロパティを追加します。
// Room rate range public double cheapest { get; set; } public double expensive { get; set; }ホーム コントローラーの Index(SearchData モデル) アクションの最後にあるルーム レートを計算します。 一時データの格納後に計算を追加します。
// Ensure TempData is stored for the next call. TempData["page"] = page; TempData["searchfor"] = model.searchText; // Calculate the room rate ranges. await foreach (var result in model.resultList.GetResultsAsync()) { var cheapest = 0d; var expensive = 0d; foreach (var room in result.Document.Rooms) { var rate = room.BaseRate; if (rate < cheapest || cheapest == 0) { cheapest = (double)rate; } if (rate > expensive) { expensive = (double)rate; } } model.resultList.Results[n].Document.cheapest = cheapest; model.resultList.Results[n].Document.expensive = expensive; }コントローラーの Index(SearchData モデル) アクション メソッドの Select パラメーターに Rooms プロパティを追加します。
options.Select.Add("Rooms");ビューのレンダリング ループを変更して、結果の最初のページのレート範囲を表示します。
<!-- Show the hotel data. --> @for (var i = 0; i < result.Count; i++) { var rateText = $"Rates from ${result[i].Document.cheapest} to ${result[i].Document.expensive}"; var ratingText = $"Rating: {result[i].Document.Rating}"; string fullDescription = result[i].Document.Description; // Display the hotel details. @Html.TextArea($"name{i}", result[i].Document.HotelName, new { @class = "box1A" }) @Html.TextArea($"rating{i}", ratingText, new { @class = "box1B" }) @Html.TextArea($"rates{i}", rateText, new { @class = "box2A" }) @Html.TextArea($"desc{i}", fullDescription, new { @class = "box3" }) }結果の後続のページのレート範囲を伝えるために、ホーム コントローラーの Next メソッドを変更します。
public async Task<ActionResult> Next(SearchData model) { // Set the next page setting, and call the Index(model) action. model.paging = "next"; await Index(model); // Create an empty list. var nextHotels = new List<string>(); // Add a hotel details to the list. await foreach (var result in model.resultList.GetResultsAsync()) { var ratingText = $"Rating: {result.Document.Rating}"; var rateText = $"Rates from ${result.Document.cheapest} to ${result.Document.expensive}"; string fullDescription = result.Document.Description; // Add strings to the list. nextHotels.Add(result.Document.HotelName); nextHotels.Add(ratingText); nextHotels.Add(rateText); nextHotels.Add(fullDescription); } // Rather than return a view, return the list of data. return new JsonResult(nextHotels); }ルーム 料金のテキストを処理するように、ビューの スクロール 関数を更新します。
<script> function scrolled() { if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) { $.getJSON("/Home/Next", function (data) { var div = document.getElementById('myDiv'); // Append the returned data to the current list of hotels. for (var i = 0; i < data.length; i += 4) { div.innerHTML += '\n<textarea class="box1A">' + data[i] + '</textarea>'; div.innerHTML += '\n<textarea class="box1B">' + data[i + 1] + '</textarea>'; div.innerHTML += '\n<textarea class="box2A">' + data[i + 2] + '</textarea>'; div.innerHTML += '\n<textarea class="box3">' + data[i + 4] + '</textarea>'; } }); } } </script>アプリを実行し、部屋の料金範囲が表示されていることを確認します。
検索パラメーターの OrderBy プロパティは、ルームが既にレートで並べ替えられている場合でも、最も安い客室料金を提供する Rooms.BaseRate などのエントリを受け入れません。 この場合、客室は料金に基づいて並べ替えされません。 ルーム レートで並べ替えられたサンプル データ セットにホテルを表示するには、ホーム コントローラーで結果を並べ替え、これらの結果を目的の順序でビューに送信する必要があります。
複数の値に基づいて結果を並べ替えます
ここでの問題は、同じ評価を持つホテルを区別する方法です。 1 つのアプローチは、最近改装されたホテルが結果の上位に表示されるように、ホテルが最後に改装された時刻に基づくセカンダリ注文である可能性があります。
2 番目のレベルの順序を追加するには、Index(SearchData モデル) メソッドの検索結果と OrderBy に LastRenovationDate を追加します。
options.Select.Add("LastRenovationDate"); options.OrderBy.Add("LastRenovationDate desc");ヒント
OrderBy リストには、任意の数のプロパティを入力できます。 ホテルの評価と改装日が同じ場合は、3 番目のプロパティを入力してそれらを区別できます。
ここでも、順序が正しいことを確認するために、ビューに改装日を表示する必要があります。 このような改装には、おそらく年のことだけで十分です。 ビューのレンダリング ループを次のコードに変更します。
<!-- Show the hotel data. --> @for (var i = 0; i < result.Count; i++) { var rateText = $"Rates from ${result[i].Document.cheapest} to ${result[i].Document.expensive}"; var lastRenovatedText = $"Last renovated: { result[i].Document.LastRenovationDate.Value.Year}"; var ratingText = $"Rating: {result[i].Document.Rating}"; string fullDescription = result[i].Document.Description; // Display the hotel details. @Html.TextArea($"name{i}", result[i].Document.HotelName, new { @class = "box1A" }) @Html.TextArea($"rating{i}", ratingText, new { @class = "box1B" }) @Html.TextArea($"rates{i}", rateText, new { @class = "box2A" }) @Html.TextArea($"renovation{i}", lastRenovatedText, new { @class = "box2B" }) @Html.TextArea($"desc{i}", fullDescription, new { @class = "box3" }) }ホーム コントローラーの Next メソッドを変更して、最後の改装日の年のコンポーネントを転送します。
public async Task<ActionResult> Next(SearchData model) { // Set the next page setting, and call the Index(model) action. model.paging = "next"; await Index(model); // Create an empty list. var nextHotels = new List<string>(); // Add a hotel details to the list. await foreach (var result in model.resultList.GetResultsAsync()) { var ratingText = $"Rating: {result.Document.Rating}"; var rateText = $"Rates from ${result.Document.cheapest} to ${result.Document.expensive}"; var lastRenovatedText = $"Last renovated: {result.Document.LastRenovationDate.Value.Year}"; string fullDescription = result.Document.Description; // Add strings to the list. nextHotels.Add(result.Document.HotelName); nextHotels.Add(ratingText); nextHotels.Add(rateText); nextHotels.Add(lastRenovatedText); nextHotels.Add(fullDescription); } // Rather than return a view, return the list of data. return new JsonResult(nextHotels); }ビューの スクロール 関数を変更して、改装テキストを表示します。
<script> function scrolled() { if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) { $.getJSON("/Home/Next", function (data) { var div = document.getElementById('myDiv'); // Append the returned data to the current list of hotels. for (var i = 0; i < data.length; i += 5) { div.innerHTML += '\n<textarea class="box1A">' + data[i] + '</textarea>'; div.innerHTML += '\n<textarea class="box1B">' + data[i + 1] + '</textarea>'; div.innerHTML += '\n<textarea class="box2A">' + data[i + 2] + '</textarea>'; div.innerHTML += '\n<textarea class="box2B">' + data[i + 3] + '</textarea>'; div.innerHTML += '\n<textarea class="box3">' + data[i + 4] + '</textarea>'; } }); } } </script>アプリを実行します。 "pool" や "view" などの一般的な用語を検索し、同じ評価のホテルが改装日の降順で表示されていることを確認します。
スコアリング プロファイルに基づいて結果を並べ替えます
ここまでのチュートリアルで示した例では、数値 (評価と改装日) を順序付けする方法を示し、順序付けの 正確な シーケンスを提供します。 ただし、一部の検索と一部のデータは、2 つのデータ要素間のこのような簡単な比較には役立たない場合があります。 フルテキスト検索クエリの場合、Cognitive Search には ランク付けの概念が含まれています。 スコアリング プロファイルを 指定して、結果のランク付け方法に影響を与え、より複雑で定性的な比較を提供できます。
スコアリング プロファイル は、インデックス スキーマで定義されます。 ホテル データには、いくつかのスコアリング プロファイルが設定されています。 スコアリング プロファイルがどのように定義されているかを見てから、それらを検索するコードを記述してみましょう。
スコアリング プロファイルの定義方法
スコアリング プロファイルは、デザイン時に検索インデックスで定義されます。 Microsoft がホストする読み取り専用ホテル インデックスには、3 つのスコアリング プロファイルがあります。 このセクションでは、スコアリング プロファイルについて説明し、イン コードの使用方法について説明します。
OrderBy または ScoringProfile パラメーターを指定しない場合に使用される、ホテル データ セットの既定のスコアリング プロファイルを次に示します。 このプロファイルは、検索テキストがホテル名、説明、またはタグ (アメニティ) の一覧に含まれている場合に、ホテルの スコア を向上させます。 スコアリングの重みによって特定のフィールドがどのように優先されるかに注目してください。 検索テキストが下にない別のフィールドに表示される場合、重みは 1 になります。 明らかに、スコアが高いほど、結果がビューに表示されます。
{ "name": "boostByField", "text": { "weights": { "Tags": 3, "HotelName": 2, "Description": 1.5, "Description_fr": 1.5, } } }次の代替スコアリング プロファイルは、指定されたパラメーターに 1 つ以上のタグの一覧 ("amenities" と呼んでいる) が含まれている場合、スコアを大幅に向上させます。 このプロファイルの重要なポイントは、テキストを含むパラメーターを指定 する必要がある ということです。 パラメーターが空の場合、または指定されていない場合は、エラーが発生します。
{ "name":"boostAmenities", "functions":[ { "fieldName":"Tags", "freshness":null, "interpolation":"linear", "magnitude":null, "distance":null, "tag":{ "tagsParameter":"amenities" }, "type":"tag", "boost":5 } ], "functionAggregation":0 },この 3 番目のプロファイルでは、ホテルの評価によってスコアが大幅に向上します。 最後に改装された日付もスコアを向上させますが、そのデータが現在の日付から 730 日以内 (2 年) 以内に収まる場合に限ります。
{ "name":"renovatedAndHighlyRated", "functions":[ { "fieldName":"Rating", "freshness":null, "interpolation":"linear", "magnitude":{ "boostingRangeStart":0, "boostingRangeEnd":5, "constantBoostBeyondRange":false }, "distance":null, "tag":null, "type":"magnitude", "boost":20 }, { "fieldName":"LastRenovationDate", "freshness":{ "boostingDuration":"P730D" }, "interpolation":"quadratic", "magnitude":null, "distance":null, "tag":null, "type":"freshness", "boost":10 } ], "functionAggregation":0 }次に、これらのプロファイルが、必要に応じて動作するかどうかを確認します。
プロファイルを比較するコードをビューに追加する
index.cshtml ファイルを開き、 <body> セクションを次のコードに置き換えます。
<body> @using (Html.BeginForm("Index", "Home", FormMethod.Post)) { <table> <tr> <td></td> <td> <h1 class="sampleTitle"> <img src="~/images/azure-logo.png" width="80" /> Hotels Search - Order Results </h1> </td> </tr> <tr> <td></td> <td> <!-- 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> <div class="searchBoxForm"> <b> Order: </b> @Html.RadioButtonFor(m => m.scoring, "Default") Default @Html.RadioButtonFor(m => m.scoring, "RatingRenovation") By numerical Rating @Html.RadioButtonFor(m => m.scoring, "boostAmenities") By Amenities @Html.RadioButtonFor(m => m.scoring, "renovatedAndHighlyRated") By Renovated date/Rating profile </div> </td> </tr> <tr> <td valign="top"> <div id="facetplace" class="facetchecks"> @if (Model != null && Model.facetText != null) { <h5 class="facetheader">Amenities:</h5> <ul class="facetlist"> @for (var c = 0; c < Model.facetText.Length; c++) { <li> @Html.CheckBoxFor(m => m.facetOn[c], new { @id = "check" + c.ToString() }) @Model.facetText[c] </li> } </ul> } </div> </td> <td> @if (Model != null && Model.resultList != null) { // Show the total result count. <p class="sampleText"> @Html.DisplayFor(m => m.resultList.Count) Results <br /> </p> <div id="myDiv" style="width: 800px; height: 450px; overflow-y: scroll;" onscroll="scrolled()"> <!-- Show the hotel data. --> @for (var i = 0; i < Model.resultList.Results.Count; i++) { var rateText = $"Rates from ${Model.resultList.Results[i].Document.cheapest} to ${Model.resultList.Results[i].Document.expensive}"; var lastRenovatedText = $"Last renovated: { Model.resultList.Results[i].Document.LastRenovationDate.Value.Year}"; var ratingText = $"Rating: {Model.resultList.Results[i].Document.Rating}"; string amenities = string.Join(", ", Model.resultList.Results[i].Document.Tags); string fullDescription = Model.resultList.Results[i].Document.Description; fullDescription += $"\nAmenities: {amenities}"; // Display the hotel details. @Html.TextArea($"name{i}", Model.resultList.Results[i].Document.HotelName, new { @class = "box1A" }) @Html.TextArea($"rating{i}", ratingText, new { @class = "box1B" }) @Html.TextArea($"rates{i}", rateText, new { @class = "box2A" }) @Html.TextArea($"renovation{i}", lastRenovatedText, new { @class = "box2B" }) @Html.TextArea($"desc{i}", fullDescription, new { @class = "box3" }) } </div> <script> function scrolled() { if (myDiv.offsetHeight + myDiv.scrollTop >= myDiv.scrollHeight) { $.getJSON("/Home/Next", function (data) { var div = document.getElementById('myDiv'); // Append the returned data to the current list of hotels. for (var i = 0; i < data.length; i += 5) { div.innerHTML += '\n<textarea class="box1A">' + data[i] + '</textarea>'; div.innerHTML += '<textarea class="box1B">' + data[i + 1] + '</textarea>'; div.innerHTML += '\n<textarea class="box2A">' + data[i + 2] + '</textarea>'; div.innerHTML += '<textarea class="box2B">' + data[i + 3] + '</textarea>'; div.innerHTML += '\n<textarea class="box3">' + data[i + 4] + '</textarea>'; } }); } } </script> } </td> </tr> </table> } </body>SearchData.cs ファイルを開き、 SearchData クラスを次のコードに置き換えます。
public class SearchData { public SearchData() { } // Constructor to initialize the list of facets sent from the controller. public SearchData(List<string> facets) { facetText = new string[facets.Count]; for (int i = 0; i < facets.Count; i++) { facetText[i] = facets[i]; } } // Array to hold the text for each amenity. public string[] facetText { get; set; } // Array to hold the setting for each amenitity. public bool[] facetOn { get; set; } // The text to search for. public string searchText { get; set; } // Record if the next page is requested. public string paging { get; set; } // The list of results. public DocumentSearchResult<Hotel> resultList; public string scoring { get; set; } }hotels.css ファイルを開き、次の HTML クラスを追加します。
.facetlist { list-style: none; } .facetchecks { width: 250px; display: normal; color: #666; margin: 10px; padding: 5px; } .facetheader { font-size: 10pt; font-weight: bold; color: darkgreen; }
スコアリング プロファイルを指定するコードをコントローラーに追加する
ホーム コントローラー ファイルを開きます。 次の using ステートメントを追加します (リストの作成に役立つ)。
using System.Linq;この例では、最初のビューを返すよりも少し多くのことを行うには、 Index の最初の呼び出しが必要です。 このメソッドは、ビューに表示する最大 20 個のアメニティを検索するようになりました。
public async Task<ActionResult> Index() { InitSearch(); // Set up the facets call in the search parameters. SearchOptions options = new SearchOptions(); // Search for up to 20 amenities. options.Facets.Add("Tags,count:20"); SearchResults<Hotel> searchResult = await _searchClient.SearchAsync<Hotel>("*", options); // Convert the results to a list that can be displayed in the client. List<string> facets = searchResult.Facets["Tags"].Select(x => x.Value.ToString()).ToList(); // Initiate a model with a list of facets for the first view. SearchData model = new SearchData(facets); // Save the facet text for the next view. SaveFacets(model, false); // Render the view including the facets. return View(model); }ファセットを一時ストレージに保存し、一時ストレージから復旧してモデルを設定するには、2 つのプライベート メソッドが必要です。
// Save the facet text to temporary storage, optionally saving the state of the check boxes. private void SaveFacets(SearchData model, bool saveChecks = false) { for (int i = 0; i < model.facetText.Length; i++) { TempData["facet" + i.ToString()] = model.facetText[i]; if (saveChecks) { TempData["faceton" + i.ToString()] = model.facetOn[i]; } } TempData["facetcount"] = model.facetText.Length; } // Recover the facet text to a model, optionally recoving the state of the check boxes. private void RecoverFacets(SearchData model, bool recoverChecks = false) { // Create arrays of the appropriate length. model.facetText = new string[(int)TempData["facetcount"]]; if (recoverChecks) { model.facetOn = new bool[(int)TempData["facetcount"]]; } for (int i = 0; i < (int)TempData["facetcount"]; i++) { model.facetText[i] = TempData["facet" + i.ToString()].ToString(); if (recoverChecks) { model.facetOn[i] = (bool)TempData["faceton" + i.ToString()]; } } }必要に応じて 、OrderBy パラメーターと ScoringProfile パラメーターを設定する 必要があります。 既存の Index(SearchData モデル) メソッドを次のように置き換えます。
public async Task<ActionResult> Index(SearchData model) { try { InitSearch(); int page; if (model.paging != null && model.paging == "next") { // Recover the facet text, and the facet check box settings. RecoverFacets(model, true); // Increment the page. page = (int)TempData["page"] + 1; // Recover the search text. model.searchText = TempData["searchfor"].ToString(); } else { // First search with text. // Recover the facet text, but ignore the check box settings, and use the current model settings. RecoverFacets(model, false); // First call. Check for valid text input, and valid scoring profile. if (model.searchText == null) { model.searchText = ""; } if (model.scoring == null) { model.scoring = "Default"; } page = 0; } // Setup the search parameters. var options = new SearchOptions { SearchMode = SearchMode.All, // Skip past results that have already been returned. Skip = page * GlobalVariables.ResultsPerPage, // Take only the next page worth of results. Size = GlobalVariables.ResultsPerPage, // Include the total number of results. IncludeTotalCount = true, }; // Select the data properties to be returned. options.Select.Add("HotelName"); options.Select.Add("Description"); options.Select.Add("Tags"); options.Select.Add("Rooms"); options.Select.Add("Rating"); options.Select.Add("LastRenovationDate"); List<string> parameters = new List<string>(); // Set the ordering based on the user's radio button selection. switch (model.scoring) { case "RatingRenovation": // Set the ordering/scoring parameters. options.OrderBy.Add("Rating desc"); options.OrderBy.Add("LastRenovationDate desc"); break; case "boostAmenities": { options.ScoringProfile = model.scoring; // Create a string list of amenities that have been clicked. for (int a = 0; a < model.facetOn.Length; a++) { if (model.facetOn[a]) { parameters.Add(model.facetText[a]); } } if (parameters.Count > 0) { options.ScoringParameters.Add($"amenities-{ string.Join(',', parameters)}"); } else { // No amenities selected, so set profile back to default. options.ScoringProfile = ""; } } break; case "renovatedAndHighlyRated": options.ScoringProfile = model.scoring; break; default: break; } // For efficiency, the search call should be asynchronous, so use SearchAsync rather than Search. model.resultList = await _searchClient.SearchAsync<Hotel>(model.searchText, options); // Ensure TempData is stored for the next call. TempData["page"] = page; TempData["searchfor"] = model.searchText; TempData["scoring"] = model.scoring; SaveFacets(model, true); // Calculate the room rate ranges. await foreach (var result in model.resultList.GetResultsAsync()) { var cheapest = 0d; var expensive = 0d; foreach (var room in result.Document.Rooms) { var rate = room.BaseRate; if (rate < cheapest || cheapest == 0) { cheapest = (double)rate; } if (rate > expensive) { expensive = (double)rate; } } result.Document.cheapest = cheapest; result.Document.expensive = expensive; } } catch { return View("Error", new ErrorViewModel { RequestId = "1" }); } return View("Index", model); }各スイッチ選択に対するコメントをすべて読みます。
複数のプロパティに基づく順序付けに関する前のセクションの追加コードを完了した場合、 次 のアクションに変更を加える必要はありません。
アプリを実行してテストする
アプリを実行します。 画面上にアメニティの完全なセットがご覧いただけます。
並べ替えの場合は、[数値による評価] を選択すると、このチュートリアルで既に実装している数値の順序が表示され、改装日によって評価が等しいホテルが決定されます。
次に、「アメニティ別」プロファイルをお試しください。 さまざまなアメニティを選択し、それらのアメニティを備えたホテルが結果一覧に昇格していることを確認します。
「改装日付別/評価プロファイル」を試して、期待通りの結果が得られるかどうかを確認してください。 最近改装されたホテルだけが 新鮮さを 高めるはずです。
リソース
詳細については、次の 「Azure Cognitive Search インデックスにスコアリング プロファイルを追加する」を参照してください。
学んだこと
このプロジェクトの次の点について考えてみましょう。
- ユーザーは、最も関連性の高い検索結果が最初に表示されることを期待しています。
- データは、順序付けが簡単になるように構造化されている必要があります。 データは追加のコードなしで順序付けを行えるように構造化されていないため、最初に "最も安い" で簡単に並べ替えることができませんでした。
- 並べ替えには多くのレベルがあり、高い順序付けレベルで同じ値を持つ結果を区別できます。
- 一部の結果は昇順 (ポイントから離れた距離など) で並べ替え、一部は降順 (ゲストの評価など) で並べ替えられるのは自然です。
- スコアリング プロファイルは、データ セットに対して数値比較が使用できない場合、または十分にスマートでない場合に定義できます。 各結果をスコア付けすると、結果をインテリジェントに並べ替えて表示するのに役立ちます。
次のステップ
この一連の C# チュートリアルを完了しました。Azure Cognitive Search API に関する貴重な知識を得ているはずです。
詳細なリファレンスとチュートリアルについては、Azure Cognitive Search ドキュメントの Microsoft Learn トレーニング カタログまたはその他のチュートリアルを参照することを検討してください。