Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Ao longo desta série de tutoriais, os resultados foram retornados e exibidos em uma ordem padrão. Neste tutorial, você adicionará critérios de classificação primários e secundários. Como alternativa à ordenação com base em valores numéricos, o exemplo final mostra como classificar os resultados com base em um perfil de pontuação personalizado. Também nos aprofundaremos um pouco mais na exibição de tipos complexos.
Neste tutorial, você aprenderá a:
- Ordenar resultados com base em uma propriedade
- Ordenar resultados com base em várias propriedades
- Ordene os resultados com base em um perfil de pontuação
Visão geral
Este tutorial estende o projeto de rolagem infinita criado no tutorial Adicionar paginação aos resultados da pesquisa .
Uma versão concluída do código neste tutorial pode ser encontrada no seguinte projeto:
Pré-requisitos
- Solução 2b-add-infinite-scroll (GitHub). Este projeto pode ser sua própria versão construída a partir do tutorial anterior ou uma cópia do GitHub.
Ordenar resultados com base em uma propriedade
Ao encomendar resultados com base em uma propriedade, como a classificação do hotel, não queremos apenas os resultados encomendados, mas também queremos a confirmação de que o pedido está correto. Adicionar o campo Classificação aos resultados permite-nos confirmar que os resultados estão ordenados corretamente.
Neste exercício, também adicionaremos um pouco mais à exibição de resultados: a tarifa de quarto mais barata e a tarifa de quarto mais cara para cada hotel.
Não há necessidade de modificar nenhum dos modelos para permitir o pedido. Apenas a vista e o controlador requerem atualizações. Comece por abrir o controlador doméstico.
Adicione a propriedade OrderBy aos parâmetros de pesquisa
No HomeController.cs, adicione a opção OrderBy e inclua a propriedade Rating, com uma ordem de classificação decrescente. No método Index(SearchData), adicione a seguinte linha aos parâmetros de pesquisa.
options.OrderBy.Add("Rating desc");
Observação
A ordem padrão é crescente, embora você possa adicionar
asc
à propriedade para deixar isso claro. A ordem decrescente é especificada adicionandodesc
.Agora execute o aplicativo e insira qualquer termo de pesquisa comum. Os resultados podem ou não estar na ordem correta, pois nem você como desenvolvedor, nem o usuário, tem qualquer maneira fácil de verificar os resultados!
Vamos deixar claro que os resultados são ordenados na classificação. Primeiro, substitua as classes box1 e box2 no arquivo hotels.css pelas seguintes classes (essas classes são todas as novas que precisamos 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; }
Sugestão
Os navegadores geralmente armazenam em cache arquivos css, e isso pode levar a um arquivo css antigo sendo usado, e suas edições ignoradas. Uma boa maneira de contornar isso é adicionar uma cadeia de caracteres de consulta com um parâmetro version ao link. Por exemplo:
<link rel="stylesheet" href="~/css/hotels.css?v1.1" />
Atualize o número da versão se achar que um arquivo css antigo está sendo usado pelo seu navegador.
Adicione a propriedade Rating ao parâmetro Select , no método Index(SearchData model) para que os resultados incluam os três campos a seguir:
options.Select.Add("HotelName"); options.Select.Add("Description"); options.Select.Add("Rating");
Abra a exibição (index.cshtml) e substitua o loop de renderização (<-- Mostrar os dados do hotel. -->) pelo código a seguir.
<!-- 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" }) }
A classificação precisa estar disponível tanto na primeira página exibida, quanto nas páginas subsequentes que são chamadas através da rolagem infinita. Para a última dessas duas situações, precisamos atualizar a ação Next no controlador e a função scroll na vista. Começando com o controlador, altere o método Next para o código a seguir. Este código cria e comunica o texto de classificação.
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); }
Agora atualize a função rolada na exibição, para exibir o texto da classificação.
<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>
Agora execute o aplicativo novamente. Pesquise em qualquer termo comum, como "wifi", e verifique se os resultados estão ordenados por ordem decrescente de classificação do hotel.
Você notará que vários hotéis têm uma classificação idêntica e, portanto, sua aparência na exibição é novamente a ordem em que os dados são encontrados, o que é arbitrário.
Antes de analisarmos a adição de um segundo nível de pedidos, vamos adicionar algum código para exibir a variedade de tarifas de quarto. Estamos adicionando este código para mostrar a extração de dados de um tipo complexo, e também para que possamos discutir os resultados de pedidos com base no preço (mais barato primeiro, talvez).
Adicione a gama de tarifas ao modo de exibição
Adicione propriedades com a tarifa de quarto mais barata e mais cara ao modelo Hotel.cs.
// Room rate range public double cheapest { get; set; } public double expensive { get; set; }
Calcule as tarifas de quarto no final da ação Index(SearchData model), no controlador principal. Adicione os cálculos após o armazenamento de dados temporários.
// 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; }
Adicione a propriedade Rooms ao parâmetro Select no método de ação Index(SearchData model) do controlador.
options.Select.Add("Rooms");
Altere o loop de renderização na exibição para exibir o intervalo de taxas para a primeira 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" }) }
Altere o método Next no controlador principal com a finalidade de comunicar o intervalo de tarifas nas páginas subsequentes de 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); }
Atualize a função scroll na vista para gerir o texto das tarifas de quarto.
<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>
Execute o aplicativo e verifique se os intervalos de tarifas de quarto são exibidos.
A propriedade OrderBy dos parâmetros de pesquisa não aceitará uma entrada como Rooms.BaseRate para fornecer a tarifa de quarto mais barata, mesmo que os quartos já tenham sido classificados na tarifa. Neste caso, os quartos não são classificados na tarifa. Para exibir os hotéis no conjunto de dados de amostra, ordenados na tarifa do quarto, você teria que classificar os resultados no controlador da sua casa e enviar esses resultados para a visualização na ordem desejada.
Ordenar resultados com base em vários valores
A questão agora é como diferenciar entre hotéis com a mesma classificação. Uma abordagem pode ser uma ordenação secundária baseada na última vez que o hotel foi renovado, para que os hotéis renovados mais recentemente apareçam mais acima nos resultados.
Para adicionar um segundo nível de ordenamento, adicione LastRenovationDate aos resultados da pesquisa e a OrderBy no método Index(modelo SearchData).
options.Select.Add("LastRenovationDate"); options.OrderBy.Add("LastRenovationDate desc");
Sugestão
Qualquer número de propriedades pode ser inserido na lista OrderBy . Se os hotéis tivessem a mesma classificação e data de renovação, uma terceira propriedade poderia ser inserida para diferenciá-los.
Mais uma vez, precisamos ver a data de renovação na exibição, apenas para assegurarmo-nos de que a ordem está correta. Para uma renovação, provavelmente apenas o ano é suficiente. Altere o loop de renderização na exibição para o código a seguir.
<!-- 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" }) }
Altere o método Next no controlador principal, para transferir o componente de ano da última data de renovação.
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); }
Altere a função rolada no modo de exibição para exibir o texto de renovação.
<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>
Execute o aplicativo. Pesquise um termo comum, como "piscina" ou "vista", e verifique se os hotéis com a mesma classificação agora são exibidos em ordem decrescente de data de renovação.
Ordene os resultados com base em um perfil de pontuação
Os exemplos dados no tutorial até agora mostram como ordenar em valores numéricos (classificação e data de renovação), fornecendo uma sequência exata de ordenação. No entanto, algumas pesquisas e alguns dados não se prestam a uma comparação tão fácil entre dois elementos de dados. Para consultas de pesquisa de texto completo, a Pesquisa Cognitiva inclui o conceito de classificação. Os perfis de pontuação podem ser especificados para influenciar a forma como os resultados são classificados, fornecendo comparações mais complexas e qualitativas.
Os perfis de pontuação são definidos no esquema de índice. Vários perfis de pontuação foram criados nos dados dos hotéis. Vamos ver como um perfil de pontuação é definido e, em seguida, tente escrever código para pesquisar neles.
Como os perfis de pontuação são definidos
Os perfis de pontuação são definidos em um índice de pesquisa no momento do design. O índice de hotéis em modo apenas de leitura hospedado pela Microsoft tem três perfis de pontuação. Esta seção explora os perfis de pontuação e mostra como usar o código in.
Abaixo está o perfil de pontuação padrão para o conjunto de dados de hotéis, usado quando você não especifica nenhum parâmetro OrderBy ou ScoringProfile . Esse perfil aumenta a pontuação de um hotel se o texto da pesquisa estiver presente no nome, na descrição ou na lista de tags (comodidades) do hotel. Observe como os pesos da pontuação favorecem determinados campos. Se o texto da pesquisa aparecer em outro campo, não listado abaixo, ele terá um peso de 1. Obviamente, quanto maior a pontuação, maior um resultado aparece na visualização.
{ "name": "boostByField", "text": { "weights": { "Tags": 3, "HotelName": 2, "Description": 1.5, "Description_fr": 1.5, } } }
O seguinte perfil de pontuação alternativo aumenta significativamente a pontuação se um parâmetro fornecido incluir uma ou mais da lista de tags (que estamos chamando de "comodidades"). O ponto-chave deste perfil é que um parâmetro deve ser fornecido, contendo texto. Se o parâmetro estiver vazio ou não for fornecido, um erro será lançado.
{ "name":"boostAmenities", "functions":[ { "fieldName":"Tags", "freshness":null, "interpolation":"linear", "magnitude":null, "distance":null, "tag":{ "tagsParameter":"amenities" }, "type":"tag", "boost":5 } ], "functionAggregation":0 },
Neste terceiro perfil, a classificação do hotel dá um impulso significativo à pontuação. A última data renovada também aumentará a pontuação, mas apenas se esses dados caírem dentro de 730 dias (2 anos) da data atual.
{ "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 }
Agora, vamos ver se esses perfis funcionam como achamos que deveriam.
Adicionar código à vista para comparar perfis
Abra o arquivo index.cshtml e substitua a <seção body> pelo código a seguir.
<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 o arquivo SearchData.cs e substitua a classe SearchData pelo código a seguir.
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 o arquivo hotels.css e adicione as seguintes classes 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; }
Adicionar código ao controlador para especificar um perfil de pontuação
Abra o arquivo do controlador inicial. Adicione a seguinte instrução using (para ajudar na criação de listas).
using System.Linq;
Para este exemplo, precisamos que a chamada inicial para Index faça um pouco mais do que apenas retornar a visualização inicial. O método agora procura até 20 amenidades para exibir na visualização.
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); }
Precisamos de dois métodos privados: um para salvar as facetas no armazenamento temporário e outro para recuperá-las de lá e preencher um 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()]; } } }
Precisamos definir os parâmetros OrderBy e ScoringProfile conforme necessário. Substitua o método Index(SearchData) existente pelo seguinte.
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); }
Leia os comentários para cada uma das seleções de switch.
Não precisamos fazer nenhuma alteração na próxima ação, se você tiver concluído o código adicional para a seção anterior sobre pedidos com base em várias propriedades.
Executar e testar o aplicativo
Execute o aplicativo. Você deve ver um conjunto completo de comodidades na vista.
Para fazer o pedido, selecionar "Por classificação numérica" lhe dará a ordem numérica que você já implementou neste tutorial, com a data de renovação decidindo entre hotéis de classificação igual.
Agora experimente o perfil "Por comodidades". Faça várias seleções de comodidades e verifique se os hotéis com essas comodidades são promovidos na lista de resultados.
Experimente o perfil "Por data de atualização/Perfil de classificação" para verificar se atende às suas expectativas. Apenas os hotéis recentemente renovados devem receber um impulso de frescura .
Recursos
Para obter mais informações, consulte Adicionar perfis de pontuação a um índice da Pesquisa Cognitiva do Azure.
Conclusões
Considere as seguintes conclusões deste projeto:
- Os usuários esperam que os resultados da pesquisa sejam ordenados, mais relevantes primeiro.
- Os dados precisam ser estruturados para que o pedido seja fácil. Não fomos capazes de classificar em "mais barato" primeiro facilmente, pois os dados não são estruturados para permitir que o pedido seja feito sem código adicional.
- Pode haver muitos níveis para ordenar, para diferenciar entre resultados que têm o mesmo valor em um nível mais alto de ordenação.
- É natural que alguns resultados sejam ordenados em ordem crescente (digamos, distância de um ponto) e outros em ordem decrescente (digamos, classificação do hóspede).
- Os perfis de pontuação podem ser definidos quando as comparações numéricas não estão disponíveis, ou não são suficientemente inteligentes, para um conjunto de dados. Pontuar cada resultado ajudará a ordenar e exibir os resultados de forma inteligente.
Próximos passos
Você concluiu esta série de tutoriais em C# - deve ter adquirido um conhecimento valioso das APIs de Pesquisa Cognitiva do Azure.
Para obter mais referências e tutoriais, considere navegar no catálogo de treinamento do Microsoft Learn ou nos outros tutoriais na documentação da Pesquisa Cognitiva do Azure.