Partilhar via


Tutorial: Ordenar os resultados da pesquisa com o SDK .NET

Ao longo desta série de tutoriais, os resultados foram devolvidos e apresentados por uma ordem predefinida. Neste tutorial, irá adicionar critérios de ordenação primária e secundária. Como alternativa à ordenação com base em valores numéricos, o exemplo final mostra como classificar os resultados com base num perfil de classificação personalizado. Também vamos aprofundar um pouco a apresentação de tipos complexos.

Neste tutorial, ficará a saber como:

  • Resultados da encomenda com base numa propriedade
  • Resultados da encomenda com base em várias propriedades
  • Resultados da encomenda com base num perfil de classificação

Descrição Geral

Este tutorial expande o projeto de deslocamento infinito criado no tutorial Adicionar paginação aos resultados da pesquisa .

Pode encontrar uma versão concluída do código neste tutorial no projeto seguinte:

Pré-requisitos

Resultados da encomenda com base numa propriedade

Ao encomendar resultados com base numa propriedade, como a classificação de hotel, não só queremos os resultados ordenados, como também queremos confirmar que a encomenda está correta. Adicionar o campo Classificação aos resultados permite-nos confirmar que os resultados estão ordenados corretamente.

Neste exercício, vamos também adicionar um pouco mais à apresentação de resultados: a tarifa de quarto mais barata, e a tarifa de quarto mais cara, para cada hotel.

Não é necessário modificar nenhum dos modelos para ativar a ordenação. Apenas a vista e o controlador necessitam de atualizações. Comece por abrir o controlador doméstico.

Adicionar a propriedade OrderBy aos parâmetros de pesquisa

  1. Em HomeController.cs, adicione a opção OrderBy e inclua a propriedade Classificação, com uma sequência de ordenação descendente. No método Index (SearchData model), adicione a seguinte linha aos parâmetros de pesquisa.

    options.OrderBy.Add("Rating desc");
    

    Nota

    A ordem predefinida é ascendente, embora possa adicionar asc à propriedade para deixar isto claro. A ordem descendente é especificada ao adicionar desc.

  2. Agora, execute a aplicação e introduza qualquer termo de pesquisa comum. Os resultados podem ou não estar na ordem correta, uma vez que nem o programador, nem o utilizador, têm uma forma fácil de verificar os resultados!

  3. Vamos deixar claro que os resultados são ordenados na classificação. Primeiro, substitua as classes box1 e box2 no ficheiro hotels.css pelas seguintes classes (estas 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;
    }
    

    Dica

    Normalmente, os browsers colocam ficheiros css em cache, o que pode levar à utilização de um ficheiro CSS antigo e as suas edições ignoradas. Uma boa forma de contornar esta situação é adicionar uma cadeia de consulta com um parâmetro de versão à ligação. Por exemplo:

      <link rel="stylesheet" href="~/css/hotels.css?v1.1" />
    

    Atualize o número da versão se achar que um ficheiro CSS antigo está a ser utilizado pelo browser.

  4. Adicione a propriedade Classificação ao parâmetro Select , no método Index(SearchData model) para que os resultados incluam os três campos seguintes:

    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Rating");
    
  5. Abra a vista (index.cshtml) e substitua o ciclo de composição (<!-- Mostrar os dados do hotel. -->) pelo seguinte código.

    <!-- 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" })
    }
    
  6. A classificação tem de estar disponível na primeira página apresentada e nas páginas subsequentes que são chamadas através do deslocamento infinito. Para esta última destas duas situações, temos de atualizar a ação Seguinte no controlador e a função de deslocamento na vista. A partir do controlador, altere o método Seguinte para o seguinte código. 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);
    }
    
  7. Atualize a função de deslocamento na vista para apresentar 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>
    
  8. Agora, execute novamente a aplicação. Pesquise em qualquer termo comum, como "wi-fi" e verifique se os resultados são ordenados por ordem descendente de classificação hoteleira.

    Ordenação com base na classificação

    Irá reparar que vários hotéis têm uma classificação idêntica, pelo que a sua aparência no ecrã é novamente a ordem pela qual os dados são encontrados, o que é arbitrário.

    Antes de analisarmos a adição de um segundo nível de ordenação, vamos adicionar algum código para apresentar o intervalo de taxas de sala. Estamos a adicionar este código para mostrar a extração de dados de um tipo complexo e também para podermos discutir os resultados da encomenda com base no preço (talvez o mais barato primeiro).

Adicionar o intervalo de tarifas de sala à vista

  1. Adicione propriedades que contenham a tarifa de quarto mais barata e cara ao modelo Hotel.cs.

    // Room rate range
    public double cheapest { get; set; }
    public double expensive { get; set; }
    
  2. Calcule as taxas de sala no final da ação Índice (modelo SearchData), no controlador doméstico. 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;
    }
    
  3. Adicione a propriedade Salas ao parâmetro Select no método de ação Índice (modelo SearchData) do controlador.

    options.Select.Add("Rooms");
    
  4. Altere o ciclo de composição na vista para apresentar o intervalo de taxas da 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" })
    }
    
  5. Altere o método Seguinte no controlador inicial para comunicar o intervalo de taxas, para 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);
    }
    
  6. Atualize a função de deslocamento na vista, para processar o texto das taxas de sala.

    <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>
    
  7. Execute a aplicação e verifique se os intervalos de taxa de sala são apresentados.

    Apresentar intervalos de taxa de sala

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 as salas já tenham sido ordenadas com taxa. Neste caso, as salas não estão ordenadas à taxa. Para apresentar hotéis no conjunto de dados de exemplo, encomendados à taxa de quarto, teria de ordenar os resultados no seu controlador doméstico e enviar estes resultados para a vista pela ordem pretendida.

Resultados da encomenda 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 encomenda secundária baseada na última vez que o hotel foi renovado para que os hotéis mais recentemente renovados apareçam mais altos nos resultados.

  1. Para adicionar um segundo nível de ordenação, adicione LastRenovationDate aos resultados da pesquisa e a OrderBy no método Index (SearchData model ).

    options.Select.Add("LastRenovationDate");
    
    options.OrderBy.Add("LastRenovationDate desc");
    

    Dica

    Qualquer número de propriedades pode ser introduzido na lista OrderBy . Se os hotéis tivessem a mesma data de avaliação e renovação, uma terceira propriedade poderia ser introduzida para diferenciar entre eles.

  2. Mais uma vez, precisamos de ver a data de renovação na vista, só para ter a certeza de que a encomenda está correta. Para tal como uma renovação, provavelmente apenas o ano é suficiente. Altere o ciclo de composição na vista para o seguinte código.

    <!-- 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" })
    }
    
  3. Altere o método Seguinte no controlador doméstico, para reencaminhar o componente do 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);
        }
    
  4. Altere a função de deslocamento na vista para apresentar 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>
    
  5. Execute a aplicação. Procure um termo comum, como "piscina" ou "vista", e verifique se os hotéis com a mesma classificação são agora apresentados por ordem descendente de data de renovação.

    Encomenda na data de renovação

Resultados da encomenda com base num perfil de classificação

Os exemplos apresentados no tutorial até agora mostram como ordenar valores numéricos (data de classificação e 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 em texto completo, o Cognitive Search inclui o conceito de classificação. Os perfis de classificação podem ser especificados para influenciar a forma como os resultados são classificados, fornecendo comparações mais complexas e qualitativas.

Os perfis de classificação são definidos no esquema de índice. Foram criados vários perfis de classificação nos dados dos hotéis. Vamos ver como um perfil de classificação é definido e, em seguida, tentar escrever código para os procurar.

Como os perfis de classificação são definidos

Os perfis de classificação são definidos num índice de pesquisa no momento da conceção. O índice de hotéis só de leitura alojado pela Microsoft tem três perfis de classificação. Esta secção explora os perfis de classificação e mostra-lhe como utilizar o no código.

  1. Segue-se o perfil de classificação predefinido para o conjunto de dados de hotéis, utilizado quando não especifica nenhum parâmetro OrderBy ou ScoringProfile . Este perfil aumenta a classificação de um hotel se o texto de pesquisa estiver presente no nome do hotel, descrição ou lista de etiquetas (comodidades). Repare como os pesos da classificação favorecem determinados campos. Se o texto de pesquisa aparecer noutro campo, não listado abaixo, terá um peso de 1. Obviamente, quanto maior for a pontuação, maior será o resultado na vista.

    {
       "name": "boostByField",
       "text": {
           "weights": {
               "Tags": 3,
               "HotelName": 2,
               "Description": 1.5,
               "Description_fr": 1.5,
           }
       }
    }
    
  2. O seguinte perfil de classificação alternativo aumenta significativamente a classificação se um parâmetro fornecido incluir uma ou mais etiquetas (a que chamamos "comodidades"). O ponto-chave deste perfil é que tem de ser fornecido um parâmetro que contenha texto. Se o parâmetro estiver vazio ou não for fornecido, será gerado um erro.

    {
        "name":"boostAmenities",
        "functions":[
            {
            "fieldName":"Tags",
            "freshness":null,
            "interpolation":"linear",
            "magnitude":null,
            "distance":null,
            "tag":{
                "tagsParameter":"amenities"
            },
            "type":"tag",
            "boost":5
            }
        ],
        "functionAggregation":0
    },
    
  3. Neste terceiro perfil, a classificação do hotel dá um impulso significativo à pontuação. A última data renovada também aumentará a classificação, mas apenas se esses dados se enquadrarem num prazo 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 estes perfis funcionam como achamos que deveriam.

Adicionar código à vista para comparar perfis

  1. Abra o ficheiro index.cshtml e substitua a secção do <corpo> pelo seguinte código.

    <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>&nbsp;Order:&nbsp;</b>
                        @Html.RadioButtonFor(m => m.scoring, "Default") Default&nbsp;&nbsp;
                        @Html.RadioButtonFor(m => m.scoring, "RatingRenovation") By numerical Rating&nbsp;&nbsp;
                        @Html.RadioButtonFor(m => m.scoring, "boostAmenities") By Amenities&nbsp;&nbsp;
                        @Html.RadioButtonFor(m => m.scoring, "renovatedAndHighlyRated") By Renovated date/Rating profile&nbsp;&nbsp;
                    </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>
    
  2. Abra o ficheiro SearchData.cs e substitua a classe SearchData pelo seguinte código.

    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; }       
    }
    
  3. Abra o ficheiro 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 classificação

  1. Abra o ficheiro do controlador inicial. Adicione a seguinte instrução using (para ajudar a criar listas).

    using System.Linq;
    
  2. Para este exemplo, precisamos da chamada inicial para Índice para fazer um pouco mais do que apenas devolver a vista inicial. O método procura agora até 20 comodidades para apresentar na 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);
        }
    
  3. Precisamos de dois métodos privados para guardar as facetas no armazenamento temporário e para recuperá-las do armazenamento temporário 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()];
                }
            }
        }
    
  4. Temos de definir os parâmetros OrderBy e ScoringProfile conforme necessário. Substitua o método Index (SearchData model) 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 de cada uma das seleções de comutadores .

  5. Não precisamos de efetuar alterações à ação Seguinte se tiver concluído o código adicional da secção anterior sobre a ordenação com base em várias propriedades.

Executar e testar a aplicação

  1. Execute a aplicação. Você deve ver um conjunto completo de comodidades na vista.

  2. Para encomenda, selecionar "Por Classificação Numérica" irá dar-lhe a ordenação numérica que já implementou neste tutorial, com a data de renovação a decidir entre hotéis de classificação igual.

    Encomendar

  3. 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.

    Encomendar

  4. Experimente o perfil "Por Data/Classificação Renovada" para ver se consegue o que espera. Só os hotéis recentemente renovados devem ter um impulso de frescura .

Recursos

Para obter mais informações, veja Adicionar perfis de classificação a um índice de Azure Cognitive Search.

Conclusões

Considere as seguintes conclusões deste projeto:

  • Os utilizadores esperam que os resultados da pesquisa sejam ordenados, o mais relevante primeiro.
  • Os dados precisam de ser estruturados para que a ordenação seja fácil. Não conseguimos ordenar "mais barato" primeiro, uma vez que os dados não estão estruturados para permitir que a ordenação seja feita sem código adicional.
  • Pode haver muitos níveis de ordenação, para diferenciar entre resultados que têm o mesmo valor num nível mais elevado de ordenação.
  • É natural que alguns resultados sejam ordenados por ordem ascendente (por exemplo, distância de um ponto) e alguns por ordem descendente (por exemplo, classificação do convidado).
  • Os perfis de classificação podem ser definidos quando as comparações numéricas não estão disponíveis ou não são inteligentes o suficiente para um conjunto de dados. A classificação de cada resultado ajudará a ordenar e a apresentar os resultados de forma inteligente.

Passos seguintes

Concluiu esta série de tutoriais em C#. Deverá ter obtido conhecimento valioso das APIs Azure Cognitive Search.

Para obter mais referências e tutoriais, considere navegar no catálogo de formação do Microsoft Learn ou noutros tutoriais na documentação Azure Cognitive Search.