Esercitazione: Ordinare i risultati della ricerca con .NET SDK

In questa serie di esercitazioni i risultati sono stati restituiti e visualizzati in un ordine predefinito. In questa esercitazione si aggiungeranno i criteri di ordinamento primario e secondario. In alternativa all'ordinamento basato su valori numerici, nell'esempio finale viene illustrato come classificare i risultati in base a un profilo di punteggio personalizzato. Verrà anche illustrata in dettaglio la visualizzazione dei tipi complessi.

In questa esercitazione verranno illustrate le procedure per:

  • Ordinare i risultati in base a un'unica proprietà
  • Ordinare i risultati in base a più proprietà
  • Ordinare i risultati in base a un profilo di punteggio

Panoramica

Questa esercitazione estende il progetto di scorrimento infinto creato nell'esercitazione Aggiungere la paginazione ai risultati della ricerca.

Una versione completa del codice di questa esercitazione si trova nel progetto seguente:

Prerequisiti

  • Soluzione 2b-add-infinite-scroll (GitHub). Questo progetto può essere una versione personalizzata, completata nell'esercitazione precedente, oppure una copia di GitHub.

Ordinare i risultati in base a un'unica proprietà

Quando si ordinano i risultati in base a una proprietà, ad esempio la valutazione di un hotel, non basta che i risultati siano ordinati, ma bisogna anche avere la conferma che l'ordine sia corretto. L'aggiunta del campo Rating ai risultati consente di verificare che i risultati siano ordinati correttamente.

In questo esercizio si aggiungeranno anche altre informazioni alla visualizzazione dei risultati per ogni hotel, ovvero la tariffa più bassa e quella più alta per camera.

Per abilitare l'ordinamento, non è necessario modificare alcun modello. Solo la visualizzazione e il controller richiedono aggiornamenti. Per iniziare, aprire il controller home.

Aggiungere la proprietà OrderBy ai parametri di ricerca

  1. In HomeController.cs aggiungere l'opzione OrderBy e includere la proprietà Rating, con un ordine di ordinamento decrescente. Nel metodo Index(SearchData model) aggiungere la riga seguente ai parametri di ricerca.

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

    Nota

    L'ordine predefinito è crescente, anche se è possibile aggiungere asc alla proprietà per renderlo chiaro. L'ordine decrescente viene specificato aggiungendo desc.

  2. A questo punto, eseguire l'app e immettere un qualsiasi termine di ricerca comune. I risultati possono o meno essere visualizzati nell'ordine corretto, dal momento che né lo sviluppatore né l'utente possono verificarli facilmente.

  3. Bisogna quindi chiarire che i risultati sono ordinati in base alla valutazione. Sostituire innanzitutto le classi box1 e box2 nel file hotels.css con le classi seguenti, completamente nuove e necessarie per questa esercitazione.

    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;
    }
    

    Suggerimento

    I browser in genere memorizzano i file CSS nella cache. Può quindi capitare che venga usato un file CSS obsoleto e che le modifiche apportate vengano ignorate. Una soluzione valida per ovviare a questo problema consiste nell'aggiungere al collegamento una stringa di query con un parametro version. Ad esempio:

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

    Aggiornare il numero di versione se si ritiene che il browser usi un file CSS obsoleto.

  4. Aggiungere la proprietà Rating al parametro Select , nel metodo Index(SearchData model) in modo che i risultati includono i tre campi seguenti:

    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Rating");
    
  5. Aprire la visualizzazione (index.cshtml) e sostituire il ciclo di rendering ( <!-- Show the hotel data. --> ) con il codice seguente.

    <!-- 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. La valutazione deve essere disponibile sia nella prima pagina visualizzata che nelle pagine successive, che vengono chiamate tramite lo scorrimento infinito. Nella seconda di queste due situazioni, è necessario aggiornare sia l'azione Next nel controller che la funzione scrolled nella visualizzazione. Iniziando con il controller, modificare il metodo Next sostituendolo con il codice seguente. Questo codice consente di creare e comunicare il testo della valutazione.

    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. A questo punto, aggiornare la funzione scrolled nella visualizzazione in modo da visualizzare il testo della valutazione.

    <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. Eseguire di nuovo l'app. Cercare un termine comune, ad esempio "wifi", e verificare che i risultati vengano visualizzati in ordine decrescente in base alla valutazione dell'albergo.

    Ordinamento basato sulla valutazione

    Si noterà che la valutazione è identica per diversi alberghi, di conseguenza anche in questo caso vengono visualizzati nell'ordine arbitrario in cui sono stati trovati i dati.

    Prima di vedere come aggiungere un secondo livello di ordinamento, è necessario aggiungere il codice per visualizzare le tariffe delle camere. Questo codice viene aggiunto non solo per illustrare l'estrazione dei dati da un tipo complesso, ma anche per esaminare l'ordinamento dei risultati in base al prezzo (probabilmente il più economico per primo).

Aggiungere le tariffe delle camere alla visualizzazione

  1. Aggiungere proprietà con le tariffe più alte e più basse al modello Hotel.cs.

    // Room rate range
    public double cheapest { get; set; }
    public double expensive { get; set; }
    
  2. Calcolare le tariffe delle camere alla fine dell'azione Index(SearchData model) , nel controller home. Aggiungere i calcoli dopo l'archiviazione dei dati temporanei.

    // 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. Aggiungere la proprietà Rooms al parametro Select nel metodo dell'azione Index(SearchData model) del controller.

    options.Select.Add("Rooms");
    
  4. Modificare il ciclo di rendering nella visualizzazione in modo da visualizzare le tariffe per la prima pagina di risultati.

    <!-- 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. Modificare il metodo Next nel controller home per comunicare le tariffe per le pagine di risultati successive.

    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. Aggiornare la funzione scrolled nella visualizzazione, per gestire il testo delle tariffe delle camere.

    <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. Eseguire l'app e verificare che le tariffe delle camere siano visualizzate.

    Visualizzazione delle tariffe delle camere

La proprietà OrderBy dei parametri di ricerca non accetterà una voce come Rooms.BaseRate per fornire la tariffa più bassa, neanche se le camere fossero già ordinate in base alla tariffa. In questo caso, le camere non sono ordinate in base alla tariffa. Per visualizzare gli alberghi nel set di dati di esempio, ordinati in base alla tariffa della camera, è necessario ordinare i risultati nel controller home e inviarli alla visualizzazione nell'ordine desiderato.

Ordinare i risultati in base a più valori

La questione è ora come distinguere tra alberghi con la stessa valutazione. Un approccio potrebbe essere un ordine secondario basato sull'ultima volta che l'hotel è stato rinnovato in modo che più recentemente gli hotel rinnovati appaiono più alti nei risultati.

  1. Per aggiungere un secondo livello di ordinamento, aggiungere LastRenovationDate ai risultati della ricerca e a OrderBy nel metodo Index(SearchData model).

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

    Suggerimento

    Nell'elenco OrderBy è possibile aggiungere un numero qualsiasi di proprietà. Nel caso in cui gli alberghi abbiano la stessa valutazione e siano stati ristrutturati nello stesso periodo, è possibile aggiungere una terza proprietà per distinguerli ulteriormente.

  2. Anche in questo caso, per essere sicuri che l'ordinamento sia corretto, è necessario che la data di ristrutturazione sia inclusa nella visualizzazione. Per una cosa come una ristrutturazione, probabilmente solo l'anno è sufficiente. Modificare il ciclo di rendering nella visualizzazione sostituendolo con il codice seguente.

    <!-- 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. Modificare il metodo Next nel controller home per trasferire il componente dell'anno della data dell'ultima ristrutturazione.

        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. Modificare la funzione scrolled nella visualizzazione in modo da visualizzare il testo relativo alla ristrutturazione.

    <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. Eseguire l'app. Cercare un termine comune, come "pool" o "view", quindi verificare che gli alberghi con la stessa valutazione siano ora elencati in ordine decrescente in base alla data di ristrutturazione.

    Ordinamento in base alla data di ristrutturazione

Ordinare i risultati in base a un profilo di punteggio

Gli esempi indicati nell'esercitazione finora mostrano come ordinare i valori numerici (data di classificazione e ristrutturazione), fornendo una sequenza esatta di ordinamento. Tuttavia, con alcune ricerche e alcuni dati non è semplice eseguire un confronto tra due elementi dati. Per le query di ricerca full-text, Ricerca cognitiva include il concetto di classificazione. I profili di assegnazione dei punteggi possono essere specificati per influenzare la classificazione dei risultati, fornendo confronti più complessi e qualitativi.

I profili di assegnazione dei punteggi sono definiti nello schema di indice. Per i dati degli alberghi sono stati configurati diversi profili di punteggio. Verrà ora esaminato come definire un profilo di punteggio e provare a scrivere codice per eseguire ricerche basate sui profili.

Modalità di definizione dei profili di punteggio

I profili di assegnazione dei punteggi sono definiti in un indice di ricerca in fase di progettazione. L'indice degli hotel di sola lettura ospitato da Microsoft ha tre profili di assegnazione dei punteggi. Questa sezione illustra i profili di assegnazione dei punteggi e illustra come usare il codice.

  1. Di seguito è riportato il profilo di assegnazione dei punteggi predefinito per il set di dati degli hotel, usato quando non si specifica alcun parametro OrderBy o ScoringProfile . Questo profilo consente di incrementare il punteggio di un albergo se il testo cercato è presente nel nome, nella descrizione o nell'elenco di tag (servizi) di un albergo. Notare come i pesi del punteggio favoriscono determinati campi. Se il testo da cercare è presente in un altro campo, non elencato di seguito, avrà peso pari a 1. Ovviamente, più alto è il punteggio, il risultato più alto viene visualizzato nella visualizzazione.

    {
       "name": "boostByField",
       "text": {
           "weights": {
               "Tags": 3,
               "HotelName": 2,
               "Description": 1.5,
               "Description_fr": 1.5,
           }
       }
    }
    
  2. Il profilo di punteggio alternativo seguente aumenta significativamente il punteggio se un parametro fornito include uno o più dell'elenco di tag (che stiamo chiamando "servizi"). L'aspetto fondamentale di questo profilo è che è necessario specificare un parametro che contiene testo. Se il parametro è vuoto o non viene specificato, verrà generato un errore.

    {
        "name":"boostAmenities",
        "functions":[
            {
            "fieldName":"Tags",
            "freshness":null,
            "interpolation":"linear",
            "magnitude":null,
            "distance":null,
            "tag":{
                "tagsParameter":"amenities"
            },
            "type":"tag",
            "boost":5
            }
        ],
        "functionAggregation":0
    },
    
  3. In questo terzo profilo, la classificazione dell'hotel dà una spinta significativa al punteggio. Anche la data dell'ultima ristrutturazione contribuisce al punteggio, ma solo se risale a meno di 730 giorni (2 anni) dalla data corrente.

    {
        "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
    }
    

    Ora, vediamo se questi profili funzionano come dovremmo.

Aggiungere codice alla visualizzazione per confrontare i profili

  1. Aprire il file index.cshtml e sostituire la sezione <body> con il codice seguente.

    <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. Aprire il file SearchData.cs e sostituire la classe SearchData con il codice seguente.

    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. Aprire il file hotels.css e aggiungere le classi HTML seguenti.

    .facetlist {
        list-style: none;
    }
    
    .facetchecks {
        width: 250px;
        display: normal;
        color: #666;
        margin: 10px;
        padding: 5px;
    }
    
    .facetheader {
        font-size: 10pt;
        font-weight: bold;
        color: darkgreen;
    }
    

Aggiungere codice al controller per specificare un profilo di punteggio

  1. Aprire il file del controller home. Aggiungere l'istruzione using seguente per facilitare la creazione di elenchi.

    using System.Linq;
    
  2. Per questo esempio è necessario che la chiamata iniziale a Index non si limiti a restituire la visualizzazione iniziale. Il metodo ora cerca fino a 20 servizi da includere nella visualizzazione.

        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. Sono necessari due metodi privati, uno per salvare i facet nell'archivio temporaneo e l'altro per ripristinarli dall'archivio temporaneo e popolare un modello.

        // 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. È necessario impostare i parametri OrderBy e ScoringProfile secondo necessità. Sostituire il metodo Index(SearchData model) esistente con il codice seguente.

    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);
    }
    

    Leggere attentamente i commenti relativi a ognuna delle selezioni di switch.

  5. Non è necessario apportare modifiche all'azione Next se è stato completato il codice aggiuntivo per la sezione precedente sull'ordinamento basato su più proprietà.

Eseguire e testare l'app

  1. Eseguire l'app. La visualizzazione includerà un set completo di servizi.

  2. Per l'ordinamento, se si seleziona "By numerical Rating" verrà applicato l'ordinamento numerico già implementato in questa esercitazione, in cui la data di ristrutturazione consente di decidere tra alberghi con la stessa valutazione.

    Ordinamento di

  3. Provare ora il profilo "By amenities". Effettuare varie selezioni di servizi e verificare che gli alberghi con questi servizi occupino una posizione di rilievo nell'elenco risultati.

    Ordinamento di

  4. Provare il profilo "By Renovated date/Rating profile" per verificare se i risultati sono quelli previsti. Solo agli alberghi ristrutturati di recente dovrebbe essere assegnata una priorità freshness.

Risorse

Per altre informazioni, vedere l'articolo seguente Aggiungere profili di punteggio a un indice di Ricerca cognitiva di Azure.

Risultati

Tenere conto delle considerazioni seguenti riguardo a questo progetto:

  • Gli utenti si aspettano che i risultati della ricerca siano ordinati e che quelli più pertinenti siano visualizzati per primi.
  • I dati devono essere strutturati in modo da semplificare l'ordinamento. Non è stato possibile ordinare facilmente prima in base alla tariffa più bassa perché i dati non sono strutturati per consentire l'ordinamento senza codice aggiuntivo.
  • Possono esistere più livelli di ordinamento, per distinguere tra i risultati che hanno lo stesso valore a un livello di ordinamento superiore.
  • Per alcuni risultati viene applicato naturalmente l'ordine crescente, ad esempio la distanza da un punto, mentre per altri l'ordine decrescente, ad esempio la valutazione degli ospiti.
  • È possibile definire profili di punteggio quando i confronti numerici per un set di dati non sono disponibili o abbastanza efficaci. L'assegnazione di un punteggio a ogni risultato consente di ordinare e visualizzare i risultati in modo intelligente.

Passaggi successivi

Questa serie di esercitazioni per C# è stata completata. A questo punto sono state acquisite conoscenze preziose sulle API di Ricerca cognitiva di Azure.

Per ulteriori informazioni di riferimento e esercitazioni, è consigliabile esplorare il catalogo di training di Microsoft Learn o altre esercitazioni nella documentazione di Ricerca cognitiva di Azure.