Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
A lo largo de esta serie de tutoriales, los resultados se han devuelto y mostrado en un orden predeterminado. En este tutorial, agregará criterios de ordenación principales y secundarios. Como alternativa a la ordenación basada en valores numéricos, el ejemplo final muestra cómo clasificar los resultados en función de un perfil de puntuación personalizado. También profundizaremos un poco más en la visualización de tipos complejos.
En este tutorial, aprenderá a:
- Ordenar los resultados en función de una propiedad
- Ordenar los resultados en función de varias propiedades
- Ordenar los resultados en función de un perfil de puntuación
Información general
En este tutorial se amplía el proyecto de desplazamiento infinito creado en el tutorial Agregar paginación a los resultados de búsqueda .
En el proyecto siguiente se puede encontrar una versión finalizada del código de este tutorial:
Prerrequisitos
- Solución 2b-add-infinite-scroll (GitHub). Este proyecto puede ser su propia versión compilada a partir del tutorial anterior o una copia de GitHub.
Ordenar los resultados en función de una propiedad
Al ordenar los resultados en función de una propiedad, como la clasificación del hotel, no solo queremos los resultados ordenados, también queremos confirmar que el pedido es correcto. Agregar el campo Clasificación a los resultados nos permite confirmar que los resultados se ordenan correctamente.
En este ejercicio, también agregaremos un poco más a la visualización de los resultados: la tarifa de habitación más barata y la tarifa de habitación más costosa, para cada hotel.
No es necesario modificar ninguno de los modelos para habilitar la ordenación. Solo la vista y el controlador requieren actualizaciones. Comience abriendo el controlador principal.
Agregar la propiedad OrderBy a los parámetros de búsqueda
En HomeController.cs, agregue la opción OrderBy e incluya la propiedad Rating, con un criterio de ordenación descendente. En el método Index(SearchData model), agregue la siguiente línea a los parámetros de búsqueda.
options.OrderBy.Add("Rating desc");Nota:
El orden predeterminado es ascendente, aunque puede agregar
asca la propiedad para que esto esté claro. El orden descendente se especifica agregandodesc.Ahora ejecute la aplicación y escriba cualquier término de búsqueda común. Los resultados pueden estar o no en el orden correcto, ya que ni usted como desarrollador, ni el usuario, tiene ninguna manera fácil de comprobar los resultados.
Vamos a aclarar que los resultados se ordenan en la clasificación. En primer lugar, reemplace las clases box1 y box2 del archivo hotels.css por las siguientes clases (estas clases son todas las nuevas que necesitamos para este tutorial).
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; }Sugerencia
Los exploradores suelen almacenar en caché los archivos css, lo que puede provocar que se use un archivo css antiguo y las modificaciones se omitan. Una buena manera de hacerlo consiste en agregar una cadena de consulta con un parámetro de versión al vínculo. Por ejemplo:
<link rel="stylesheet" href="~/css/hotels.css?v1.1" />Actualice el número de versión si cree que el explorador usa un archivo css antiguo.
Agregue la propiedad Rating al parámetro Select , en el método Index(SearchData model) para que los resultados incluyan los tres campos siguientes:
options.Select.Add("HotelName"); options.Select.Add("Description"); options.Select.Add("Rating");Abra la vista (index.cshtml) y reemplace el bucle de representación (<-- Mostrar los datos del hotel. -->) por el código siguiente.
<!-- 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" }) }La clasificación debe estar disponible tanto en la primera página que se muestra como en las páginas posteriores a las que se accede mediante el desplazamiento infinito. Para la última de estas dos situaciones, es necesario actualizar la acción Next en el controlador y la función scroll en la vista. A partir del controlador, cambie el método Next al código siguiente. Este código crea y comunica el texto de clasificación.
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); }Ahora, actualiza la función scroll en la vista para mostrar el texto de valoración.
<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>Ahora vuelva a ejecutar la aplicación. Busque en cualquier término común, como "wifi" y compruebe que los resultados se ordenan por orden descendente de clasificación de hoteles.
Observará que varios hoteles tienen una clasificación idéntica, por lo que su apariencia en la pantalla es de nuevo el orden en el que se encuentran los datos, que es arbitrario.
Antes de examinar cómo agregar un segundo nivel de ordenación, vamos a agregar código para mostrar el intervalo de tarifas de habitación. Estamos agregando este código para mostrar la extracción de datos de un tipo complejo y también para que podamos analizar los resultados de la ordenación en función del precio (lo más barato primero posiblemente).
Agregar el rango de tarifas de habitación de hotel al sistema de visualización
Agregue propiedades que contengan la tarifa de habitación más barata y más costosa al modelo de Hotel.cs.
// Room rate range public double cheapest { get; set; } public double expensive { get; set; }Calcule las tarifas de habitación al final de la acción Index(SearchData model), en el controlador principal. Agregue los cálculos después del almacenamiento de datos temporales.
// 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; }Agregue la propiedad Rooms al parámetro Select en el método de acción Index(SearchData model) del controlador.
options.Select.Add("Rooms");Cambie el bucle de representación en la vista para mostrar el intervalo de velocidad de la primera página de resultados.
<!-- 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" }) }Cambie el método Next en el controlador principal para comunicar el intervalo de velocidad para las páginas posteriores de los resultados.
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); }Actualice la función scroll en la vista para gestionar el texto de tarifas de la habitación.
<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>Ejecute la aplicación y compruebe que se muestran los intervalos de tarifas de las habitaciones.
La propiedad OrderBy de los parámetros de búsqueda no aceptará una entrada como Rooms.BaseRate para proporcionar la tarifa de habitación más barata, incluso si las habitaciones ya estaban ordenadas según la tarifa. En este caso, las habitaciones no se ordenan según la tarifa. Para mostrar hoteles en el conjunto de datos de ejemplo, ordenados según la tarifa de la habitación, tendría que ordenar los resultados en el controlador de casa y enviar estos resultados a la vista en el orden deseado.
Ordenar los resultados en función de varios valores
La pregunta ahora es cómo diferenciar entre hoteles con la misma clasificación. Un enfoque podría ser una ordenación secundaria basada en la última vez que se renovó el hotel para que los hoteles más recientemente renovados aparezcan más altos en los resultados.
Para agregar un segundo nivel de ordenación, agregue LastRenovationDate a los resultados de búsqueda y a OrderBy en el método Index(SearchData model).
options.Select.Add("LastRenovationDate"); options.OrderBy.Add("LastRenovationDate desc");Sugerencia
Se puede escribir cualquier número de propiedades en la lista OrderBy . Si los hoteles tenían la misma clasificación y fecha de renovación, se podría introducir una tercera propiedad para diferenciar entre ellos.
De nuevo, necesitamos ver la fecha de renovación en la pantalla, solo para asegurarnos de que el orden es correcto. Para algo así como una renovación, probablemente sólo el año es suficiente. Cambie el bucle de representación en la vista por el código siguiente.
<!-- 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" }) }Cambie el método Next en el controlador del hogar para reenviar el componente del año de la última fecha de renovación.
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); }Cambie la función scrolled en la vista para mostrar el texto de renovación.
<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>Ejecute la aplicación. Busque en un término común, como "pool" o "view", y compruebe que los hoteles con la misma clasificación se muestran ahora en orden descendente de fecha de renovación.
Ordenar los resultados en función de un perfil de puntuación
Los ejemplos indicados en el tutorial hasta ahora muestran cómo ordenar los valores numéricos (clasificación y fecha de renovación), proporcionando una secuencia exacta de ordenación. Sin embargo, algunas búsquedas y algunos datos no se prestan a una comparación tan sencilla entre dos elementos de datos. Para las consultas de búsqueda de texto completo, Cognitive Search incluye el concepto de clasificación. Los perfiles de puntuación se pueden especificar para influir en cómo se clasifican los resultados, lo que proporciona comparaciones más complejas y cualitativas.
Los perfiles de puntuación se definen en el esquema de índice. Se han configurado varios perfiles de puntuación en los datos de hoteles. Echemos un vistazo a cómo se define un perfil de puntuación y, a continuación, intente escribir código para buscar en ellos.
Cómo se definen los perfiles de puntuación
Los perfiles de puntuación se definen en un índice de búsqueda en la fase de diseño. El índice de hoteles de solo lectura hospedado por Microsoft tiene tres perfiles de puntuación. En esta sección se exploran los perfiles de puntuación y se muestra cómo usarlo en el código.
A continuación se muestra el perfil de puntuación predeterminado para el conjunto de datos de hoteles, que se usa cuando no se especifica ningún parámetro OrderBy o ScoringProfile . Este perfil aumenta la puntuación de un hotel si el texto de búsqueda está presente en el nombre, la descripción o la lista de etiquetas (comodidades). Observe cómo las ponderaciones de la puntuación favorecen determinados campos. Si el texto de búsqueda aparece en otro campo, no está enumerado a continuación, tendrá un peso de 1. Obviamente, cuanto mayor sea la puntuación, más arriba aparecerá un resultado en la visualización.
{ "name": "boostByField", "text": { "weights": { "Tags": 3, "HotelName": 2, "Description": 1.5, "Description_fr": 1.5, } } }El siguiente perfil de puntuación alternativo aumenta significativamente la puntuación si un parámetro proporcionado incluye una o varias de las listas de etiquetas (que llamamos "comodidades"). El punto clave de este perfil es que se debe proporcionar un parámetro que contenga texto. Si el parámetro está vacío o no se proporciona, se producirá un error.
{ "name":"boostAmenities", "functions":[ { "fieldName":"Tags", "freshness":null, "interpolation":"linear", "magnitude":null, "distance":null, "tag":{ "tagsParameter":"amenities" }, "type":"tag", "boost":5 } ], "functionAggregation":0 },En este tercer perfil, la clasificación del hotel da un impulso significativo a la puntuación. La última fecha renovada también aumentará la puntuación, pero solo si esos datos se encuentran en un plazo de 730 días (2 años) de la fecha actual.
{ "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 }Ahora, veamos si estos perfiles funcionan como creemos que deberían.
Adición de código a la vista para comparar perfiles
Abra el archivo index.cshtml y reemplace la <sección body> por el código siguiente.
<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>Abra el archivo SearchData.cs y reemplace la clase SearchData por el código siguiente.
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; } }Abra el archivo hotels.css y agregue las siguientes clases 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; }
Agregar código al controlador para especificar un perfil de puntuación
Abra el archivo del controlador de inicio. Agregue la siguiente instrucción using (para ayudar con la creación de listas).
using System.Linq;En este ejemplo, necesitamos la llamada inicial a Index para hacer un poco más que devolver la vista inicial. El método ahora busca hasta 20 servicios para mostrar en la vista.
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); }Necesitamos dos métodos privados para guardar las facetas en el almacenamiento temporal y para recuperarlas del almacenamiento temporal y rellenar un modelo.
// 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()]; } } }Es necesario establecer los parámetros OrderBy y ScoringProfile según sea necesario. Reemplace el método Index(SearchData model) existente por lo siguiente.
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); }Lea los comentarios para cada una de las selecciones de interruptor.
No es necesario realizar ningún cambio en la acción Siguiente , si completó el código adicional de la sección anterior sobre la ordenación basada en varias propiedades.
Ejecución y prueba de la aplicación
Ejecute la aplicación. Debería ver un conjunto completo de comodidades en la vista.
Para ordenar, al seleccionar "Por clasificación numérica", se le proporcionará la ordenación numérica que ya ha implementado en este tutorial, utilizando la fecha de renovación para desempatar entre hoteles con la misma clasificación.
Ahora pruebe el perfil "Por comodidades". Realice varias selecciones de comodidades y compruebe que los hoteles con esas comodidades se promocionan en la lista de resultados.
Pruebe el perfil "Por fecha renovada/Clasificación" para ver si obtiene lo que espera. Solo los hoteles recientemente renovados deben obtener un impulso de frescura.
Recursos
Para más información, consulte el siguiente artículo sobre cómo agregar perfiles de puntuación a un índice de Azure Cognitive Search.
Conclusiones
Tenga en cuenta los siguientes aspectos de este proyecto:
- Los usuarios esperarán que los resultados de la búsqueda se ordenen, con los más relevantes primero.
- Los datos necesitan estructurarse para que la ordenación sea fácil. No hemos podido ordenar por "más barato" en primer lugar, ya que los datos no están estructurados para permitir que la ordenación se realice sin código adicional.
- Puede haber muchos niveles para ordenar, para diferenciar entre los resultados que tienen el mismo valor en un nivel superior de ordenación.
- Es natural que algunos resultados se ordenen en orden ascendente (por ejemplo, distancia lejos de un punto) y algunos en orden descendente (por ejemplo, la clasificación del invitado).
- Los perfiles de puntuación se pueden definir cuando las comparaciones numéricas no están disponibles o no son lo suficientemente inteligentes para un conjunto de datos. La puntuación de cada resultado ayudará a ordenar y mostrar los resultados de forma inteligente.
Pasos siguientes
Ha completado esta serie de tutoriales de C#: debe haber obtenido conocimientos valiosos de las API de Azure Cognitive Search.
Para obtener más referencia y tutoriales, considere la posibilidad de examinar el catálogo de aprendizaje de Microsoft Learn o los otros tutoriales de la documentación de Azure Cognitive Search.