Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az aspektusok lehetővé teszik az önálló navigációt azáltal, hogy hivatkozásokat biztosítanak az eredmények szűréséhez. Ebben az oktatóanyagban a lap bal oldalán egy tagolt navigációs struktúra jelenik meg címkékkel és kattintható szöveggel az eredmények vágásához.
Ebben az oktatóanyagban a következőket sajátíthatja el:
- A modelltulajdonságok beállítása az IsFacetable értékre
- Arculati navigáció hozzáadása az alkalmazáshoz
Áttekintés
Az aspektusok a keresési index mezőin alapulnak. A facet=[string] függvényt tartalmazó lekérdezési kérés megadja a csoportosításra szánt mezőt. Gyakori, hogy több aspektust is tartalmaz, például &facet=category&facet=amenitiesmindegyiket egy és (>) karakter választja el egymástól. A facettált navigációs struktúra implementálásához meg kell adnia mind a tulajdonságokat, mind a szűrőket. A szűrő egy kattintásos eseményen van használva az eredmények szűkítéséhez. A "költségvetés" gombra kattintva például a feltételek alapján szűri az eredményeket.
Ez az oktatóanyag kiterjeszti a Lapozás hozzáadása a keresési eredményekhez című oktatóanyagban létrehozott lapozási projektet.
Az oktatóanyagban szereplő kód egy kész verziója a következő projektben található:
Előfeltételek
- A 2a-add-paging (GitHub) megoldása. Ez a projekt lehet az előző oktatóanyagból készült saját verzió, vagy a GitHub egy példánya.
Állítsa be a modelltulajdonságokat "IsFacetable"-ként
Ahhoz, hogy egy modelltulajdonság szerepeljen egy aspektuskeresésben, rendelkeznie kell az IsFacetable címkével.
Vizsgálja meg a szállodai osztályt. A kategória és a címkék például IsFacetable néven vannak megjelölve, de a HotelName és a Leírás nem.
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; } }Az oktatóanyag részeként nem módosítunk címkéket, ezért zárja be a hotel.cs fájlt változatlanul.
Megjegyzés:
Az aspektusalapú keresés hibát jelez, ha a keresésben kért mező nincs megfelelően megjelölve.
Arculati navigáció hozzáadása az alkalmazáshoz
Ebben a példában lehetővé tesszük a felhasználó számára, hogy válasszon egy szállodakategóriát vagy egy kényelmi szolgáltatást az eredmények bal oldalán látható hivatkozások listájából. A felhasználó először beír egy keresési szöveget, majd fokozatosan szűkíti a keresés eredményeit egy kategória vagy szolgáltatás kiválasztásával.
A vezérlő feladata, hogy átadja az aspektusok listáját a nézetnek. Az állapot megőrzésének mechanizmusaként ideiglenes tárolást használunk, hogy a keresés során megőrizzük a felhasználói beállításokat.
Szűrősztringek hozzáadása a SearchData-modellhez
Nyissa meg a SearchData.cs fájlt, és adjon hozzá sztringtulajdonságokat a SearchData osztályhoz az aspektusszűrési sztringek tárolásához.
public string categoryFilter { get; set; } public string amenityFilter { get; set; }
A Facet művelet metódus hozzáadása
Az otthoni vezérlőnek szüksége van egy új műveletre, a Facet-ra és a meglévő index - és oldalműveletek frissítésére, valamint a RunQueryAsync metódusra.
Cserélje le az Index(SearchData modell) műveletmetódusát.
public async Task<ActionResult> Index(SearchData model) { try { // Ensure the search string is valid. if (model.searchText == null) { model.searchText = ""; } // Make the search call for the first page. await RunQueryAsync(model, 0, 0, "", "").ConfigureAwait(false); } catch { return View("Error", new ErrorViewModel { RequestId = "1" }); } return View(model); }Cserélje le a PageAsync(SearchData modell) műveletmetódust.
public async Task<ActionResult> PageAsync(SearchData model) { try { int page; // Calculate the page that should be displayed. switch (model.paging) { case "prev": page = (int)TempData["page"] - 1; break; case "next": page = (int)TempData["page"] + 1; break; default: page = int.Parse(model.paging); break; } // Recover the leftMostPage. int leftMostPage = (int)TempData["leftMostPage"]; // Recover the filters. string catFilter = TempData["categoryFilter"].ToString(); string ameFilter = TempData["amenityFilter"].ToString(); // Recover the search text. model.searchText = TempData["searchfor"].ToString(); // Search for the new page. await RunQueryAsync(model, page, leftMostPage, catFilter, ameFilter); } catch { return View("Error", new ErrorViewModel { RequestId = "2" }); } return View("Index", model); }Adjon hozzá egy FacetAsync(SearchData-modell) műveletmetódust, amely akkor aktiválódik, ha a felhasználó egy aspektushivatkozásra kattint. A modell kategória- vagy kényelmi keresési szűrőt fog tartalmazni. Adja hozzá a PageAsync művelet után.
public async Task<ActionResult> FacetAsync(SearchData model) { try { // Filters set by the model override those stored in temporary data. string catFilter; string ameFilter; if (model.categoryFilter != null) { catFilter = model.categoryFilter; } else { catFilter = TempData["categoryFilter"].ToString(); } if (model.amenityFilter != null) { ameFilter = model.amenityFilter; } else { ameFilter = TempData["amenityFilter"].ToString(); } // Recover the search text. model.searchText = TempData["searchfor"].ToString(); // Initiate a new search. await RunQueryAsync(model, 0, 0, catFilter, ameFilter).ConfigureAwait(false); } catch { return View("Error", new ErrorViewModel { RequestId = "2" }); } return View("Index", model); }
A keresési szűrő beállítása
Ha egy felhasználó kiválaszt egy adott aspektust, például az Resort and Spa kategóriára kattint, akkor csak az ebben a kategóriában megadott szállodák jelennek meg az eredményekben. Ha így szeretné szűkíteni a keresést, be kell állítania egy szűrőt.
Cserélje le a RunQueryAsync metódust a következő kódra. Elsősorban egy kategóriaszűrő sztringet és egy kényelmi szűrősztringet vesz igénybe, és beállítja a SearchOptions szűrőparaméterét.
private async Task<ActionResult> RunQueryAsync(SearchData model, int page, int leftMostPage, string catFilter, string ameFilter) { InitSearch(); string facetFilter = ""; if (catFilter.Length > 0 && ameFilter.Length > 0) { // Both facets apply. facetFilter = $"{catFilter} and {ameFilter}"; } else { // One, or zero, facets apply. facetFilter = $"{catFilter}{ameFilter}"; } var options = new SearchOptions { Filter = facetFilter, 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, }; // Return information on the text, and number, of facets in the data. options.Facets.Add("Category,count:20"); options.Facets.Add("Tags,count:20"); // Enter Hotel property names into this list, so only these values will be returned. options.Select.Add("HotelName"); options.Select.Add("Description"); options.Select.Add("Category"); options.Select.Add("Tags"); // 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); // This variable communicates the total number of pages to the view. model.pageCount = ((int)model.resultList.TotalCount + GlobalVariables.ResultsPerPage - 1) / GlobalVariables.ResultsPerPage; // This variable communicates the page number being displayed to the view. model.currentPage = page; // Calculate the range of page numbers to display. if (page == 0) { leftMostPage = 0; } else if (page <= leftMostPage) { // Trigger a switch to a lower page range. leftMostPage = Math.Max(page - GlobalVariables.PageRangeDelta, 0); } else if (page >= leftMostPage + GlobalVariables.MaxPageRange - 1) { // Trigger a switch to a higher page range. leftMostPage = Math.Min(page - GlobalVariables.PageRangeDelta, model.pageCount - GlobalVariables.MaxPageRange); } model.leftMostPage = leftMostPage; // Calculate the number of page numbers to display. model.pageRange = Math.Min(model.pageCount - leftMostPage, GlobalVariables.MaxPageRange); // Ensure Temp data is stored for the next call. TempData["page"] = page; TempData["leftMostPage"] = model.leftMostPage; TempData["searchfor"] = model.searchText; TempData["categoryFilter"] = catFilter; TempData["amenityFilter"] = ameFilter; // Return the new view. return View("Index", model); }Figyelje meg, hogy a Kategória és a Címkék tulajdonságok fel vannak véve a visszaadni kívánt elemek kijelölésének listájára. Ez a kiegészítés nem követelmény a részletes navigáció működéséhez, de ezeket az információkat használva ellenőrizzük, hogy a szűrők megfelelően működnek-e.
Aspektushivatkozások listájának hozzáadása a nézethez
A nézet jelentős módosításokat igényel.
Először nyissa meg a hotels.css fájlt (a wwwroot/css mappában), és adja hozzá a következő osztályokat.
.facetlist { list-style: none; } .facetchecks { width: 250px; display: normal; color: #666; margin: 10px; padding: 5px; } .facetheader { font-size: 10pt; font-weight: bold; color: darkgreen; }A nézethez rendezze a kimenetet egy táblázatba, hogy a bal oldalon lévő arclista és a jobb oldali eredmények megfelelően igazodjanak egymáshoz. Nyissa meg az index.cshtml fájlt. Cserélje le a HTML-törzscímkék <> teljes tartalmát a következő kódra.
<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 - Facet Navigation </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 value="" class="searchBoxSubmit" type="submit"> </div> </td> </tr> <tr> <td valign="top"> <div id="facetplace" class="facetchecks"> @if (Model != null && Model.resultList != null) { List<string> categories = Model.resultList.Facets["Category"].Select(x => x.Value.ToString()).ToList(); if (categories.Count > 0) { <h5 class="facetheader">Category:</h5> <ul class="facetlist"> @for (var c = 0; c < categories.Count; c++) { var facetLink = $"{categories[c]} ({Model.resultList.Facets["Category"][c].Count})"; <li> @Html.ActionLink(facetLink, "FacetAsync", "Home", new { categoryFilter = $"Category eq '{categories[c]}'" }, null) </li> } </ul> } List<string> tags = Model.resultList.Facets["Tags"].Select(x => x.Value.ToString()).ToList(); if (tags.Count > 0) { <h5 class="facetheader">Amenities:</h5> <ul class="facetlist"> @for (var c = 0; c < tags.Count; c++) { var facetLink = $"{tags[c]} ({Model.resultList.Facets["Tags"][c].Count})"; <li> @Html.ActionLink(facetLink, "FacetAsync", "Home", new { amenityFilter = $"Tags/any(t: t eq '{tags[c]}')" }, null) </li> } </ul> } } </div> </td> <td valign="top"> <div id="resultsplace"> @if (Model != null && Model.resultList != 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++) { string amenities = string.Join(", ", results[i].Document.Tags); string fullDescription = results[i].Document.Description; fullDescription += $"\nCategory: {results[i].Document.Category}"; fullDescription += $"\nAmenities: {amenities}"; // Display the hotel name and description. @Html.TextAreaFor(m => results[i].Document.HotelName, new { @class = "box1" }) @Html.TextArea($"desc{i}", fullDescription, new { @class = "box2" }) } } </div> </td> </tr> <tr> <td></td> <td valign="top"> @if (Model != null && Model.pageCount > 1) { // If there is more than one page of results, show the paging buttons. <table> <tr> <td class="tdPage"> @if (Model.currentPage > 0) { <p class="pageButton"> @Html.ActionLink("|<", "PageAsync", "Home", new { paging = "0" }, null) </p> } else { <p class="pageButtonDisabled">|<</p> } </td> <td class="tdPage"> @if (Model.currentPage > 0) { <p class="pageButton"> @Html.ActionLink("<", "PageAsync", "Home", new { paging = "prev" }, null) </p> } else { <p class="pageButtonDisabled"><</p> } </td> @for (var pn = Model.leftMostPage; pn < Model.leftMostPage + Model.pageRange; pn++) { <td class="tdPage"> @if (Model.currentPage == pn) { // Convert displayed page numbers to 1-based and not 0-based. <p class="pageSelected">@(pn + 1)</p> } else { <p class="pageButton"> @Html.ActionLink((pn + 1).ToString(), "PageAsync", "Home", new { paging = @pn }, null) </p> } </td> } <td class="tdPage"> @if (Model.currentPage < Model.pageCount - 1) { <p class="pageButton"> @Html.ActionLink(">", "PageAsync", "Home", new { paging = "next" }, null) </p> } else { <p class="pageButtonDisabled">></p> } </td> <td class="tdPage"> @if (Model.currentPage < Model.pageCount - 1) { <p class="pageButton"> @Html.ActionLink(">|", "PageAsync", "Home", new { paging = Model.pageCount - 1 }, null) </p> } else { <p class="pageButtonDisabled">>|</p> } </td> </tr> </table> } </td> </tr> </table> } </body>Figyelje meg a Html.ActionLink-hívás használatát. Ez a hívás érvényes szűrő karakterláncokat továbbít a vezérlőhöz, amikor a felhasználó egy facettahivatkozásra kattint.
Az alkalmazás futtatása és tesztelése
A felhasználóra mutató részletes navigáció előnye, hogy egyetlen kattintással szűkítheti a keresést, amelyet az alábbi sorrendben jeleníthetünk meg.
Futtassa az alkalmazást, írja be a "repülőtér" kifejezést keresési szövegként. Ellenőrizze, hogy az aspektusok listája szépen jelenik-e meg a bal oldalon. Ezek a aspektusok mind azokra a szállodákra vonatkoznak, amelyek szöveges adataiban "repülőtér" szerepel, és ezek előfordulási gyakorisága is megszámlálható.
Kattintson az Resort and Spa kategóriára. Ellenőrizze, hogy az összes találat ebben a kategóriában van-e.
Kattintson a kontinentális reggelire . Ellenőrizze, hogy az összes találat továbbra is a "Resort and Spa" kategóriában van-e, a kiválasztott kényelemmel.
Válasszon másik kategóriát, majd egy kényelmi lehetőséget, és tekintse meg a szűkítési eredményeket. Próbálja meg fordított sorrendben: előbb egy szolgáltatás, majd egy kategória. Küldjön egy üres keresést a lap alaphelyzetbe állításához.
Megjegyzés:
Ha egy elemlistán (például kategóriában) egy kijelölést végez, az felülírja a kategórialistában lévő korábbi kijelöléseket.
Főbb tanulságok
Vegye figyelembe a projekt alábbi tanulságait:
- Fontos, hogy minden egyes facetable mezőt megjelöljön az IsFacetable tulajdonsággal a facet navigationbe való felvételhez.
- A tulajdonságokat szűrőkkel kombinálják az eredmények csökkentése érdekében.
- Az aspektusok összegzőek, és az egyes kijelölések az előzőre épülnek, így tovább szűkíthetők az eredmények.
Következő lépések
A következő oktatóanyagban a rendelési eredményeket tekintjük át. Eddig a pontig az eredmények egyszerűen az adatbázisban található sorrendben vannak rendezve.