Condividi tramite


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 verranno aggiunti criteri di ordinamento primario e secondario. In alternativa all'ordinamento in base ai valori numerici, l'esempio finale mostra come classificare i risultati in base a un profilo di punteggio personalizzato. Si passerà anche un po' più in dettaglio alla visualizzazione di tipi complessi.

In questa esercitazione si apprenderà come:

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

Informazioni generali

Questa guida estende il progetto di scorrimento infinito creato nel tutorial Aggiungere paging ai risultati della ricerca.

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

Prerequisiti

Ordinare i risultati in base a una proprietà

Quando si ordinano i risultati in base a una proprietà, ad esempio la classificazione dell'hotel, non solo si desiderano i risultati ordinati, ma si vuole anche confermare che l'ordine è corretto. L'aggiunta del campo Rating ai risultati consente di verificare che i risultati siano ordinati correttamente.

In questo esercizio si aggiungerà anche un po' di più alla visualizzazione dei risultati: la tariffa della camera più economica e la tariffa della camera più costosa, per ogni hotel.

Non è necessario modificare uno dei modelli per abilitare l'ordinamento. Solo la visualizzazione e il controller richiedono aggiornamenti. Iniziare aprendo 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 ordinamento decrescente. Nel metodo Index(SearchData model) aggiungere la riga seguente ai parametri di ricerca.

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

    Annotazioni

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

  2. Eseguire ora l'app e immettere qualsiasi termine di ricerca comune. I risultati possono essere o non essere nell'ordine corretto, poiché né tu come sviluppatore né l'utente hanno un modo semplice per verificare i risultati!

  3. Ora è chiaro che i risultati vengono ordinati in base alla classificazione. Sostituire prima di tutto le classi box1 e box2 nel file hotels.css con le classi seguenti (queste classi sono tutte le nuove 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 nella cache i file CSS e questo può causare l'uso di un vecchio file CSS e le modifiche ignorate. Un buon modo per eseguire l'operazione consiste nell'aggiungere una stringa di query con un parametro version al collegamento. Per esempio:

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

    Aggiornare il numero di versione se si ritiene che il browser stia usando un file CSS precedente.

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

    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Rating");
    
  5. Aprire la vista (index.cshtml) e sostituire il ciclo di rendering (<-- Mostra i dati dell'hotel. -->) 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 classificazione deve essere disponibile sia nella prima pagina visualizzata che nelle pagine successive chiamate tramite lo scorrimento infinito. Per queste due situazioni, è necessario aggiornare sia l'azione Next nel controller che la funzione scorrevole nella visualizzazione. A partire dal controller, modificare il metodo Next nel codice seguente. Questo codice crea e comunica il testo della classificazione.

    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. Aggiornare ora la funzione scorrevole nella visualizzazione per visualizzare il testo della classificazione.

    <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 qualsiasi termine comune, ad esempio "wifi" e verificare che i risultati siano ordinati in base all'ordine decrescente della classificazione dell'hotel.

    Ordinamento in base alla classificazione

    Si noterà che diversi hotel hanno una classificazione identica e quindi il loro aspetto nella visualizzazione è di nuovo l'ordine in cui vengono trovati i dati, che è arbitrario.

    Prima di esaminare l'aggiunta di un secondo livello di ordinamento, aggiungere un codice per visualizzare l'intervallo di tariffe delle camere. Questo codice viene aggiunto sia per mostrare l'estrazione di dati da un tipo complesso, sia per poter discutere l'ordinamento dei risultati in base al prezzo (più economico forse).

Aggiungere l'intervallo di tariffe delle camere alla visualizzazione

  1. Aggiungere proprietà contenenti la tariffa di camera più economica e più costosa al modello di Hotel.cs.

    // Room rate range
    public double cheapest { get; set; }
    public double expensive { get; set; }
    
  2. Calcolare le tariffe delle sale alla fine dell'azione Index(SearchData model) nel controller home. Aggiungere i calcoli dopo l'archiviazione di 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 di azione Index(SearchData model) del controller.

    options.Select.Add("Rooms");
    
  4. Modificare il ciclo di rendering nella visualizzazione per visualizzare l'intervallo di frequenza per la prima pagina dei 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 l'intervallo di frequenza, per le pagine successive dei risultati.

    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. Aggiorna 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 siano visualizzati gli intervalli di tariffe delle camere.

    Visualizzazione degli intervalli di tariffa delle camere

La proprietà OrderBy dei parametri di ricerca non accetterà una voce come Rooms.BaseRate per fornire la tariffa della camera più economica, anche se le camere erano già ordinate in base alla tariffa. In questo caso, le camere non sono ordinate in base alla tariffa. Per visualizzare gli hotel nel set di dati di esempio, ordinato in base alla tariffa della camera, è necessario ordinare i risultati nel controller di casa e inviare questi risultati alla visualizzazione nell'ordine desiderato.

Ordinare i risultati in base a più valori

La domanda è ora come distinguere tra alberghi con la stessa classificazione. Un approccio potrebbe essere un ordinamento secondario basato sull'ultima volta che l'hotel è stato ristrutturato in modo che gli hotel più recentemente ristrutturati appaiano più in alto 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

    È possibile immettere un numero qualsiasi di proprietà nell'elenco OrderBy . Se gli alberghi hanno la stessa data di classificazione e ristrutturazione, potrebbe essere immesso un terzo immobile per distinguerli.

  2. Anche in questo caso, dobbiamo vedere la data di ristrutturazione nella vista, solo per essere certi che l'ordine sia corretto. Per una cosa come una ristrutturazione, probabilmente solo l'anno è sufficiente. Modificare il ciclo di rendering nella visualizzazione 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 inoltrare il componente dell'anno dell'ultima data di 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 scorrevole nella visualizzazione per visualizzare il testo della 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. Avvia l'app. Cercare un termine comune, ad esempio "pool" o "view" e verificare che gli hotel con la stessa classificazione siano ora visualizzati in ordine decrescente di data di ristrutturazione.

    Ordini relativi alla data di ristrutturazione

Ordinare i risultati in base a un profilo di punteggio

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

I profili di punteggio vengono definiti nello schema dell'indice. Sono stati configurati diversi profili di punteggio sui dati degli hotel. Si esaminerà ora come viene definito un profilo di punteggio, quindi provare a scrivere codice per cercarli.

Come vengono definiti i profili di punteggio

I profili di punteggio vengono definiti in un indice di ricerca in fase di progettazione. L'indice degli hotel di sola lettura ospitato da Microsoft ha tre profili di punteggio. Questa sezione esplora i profili di punteggio e mostra come utilizzarli nel codice.

  1. Di seguito è riportato il profilo di punteggio predefinito per il set di dati hotels, usato quando non si specifica alcun parametro OrderBy o ScoringProfile . Questo profilo aumenta il punteggio per un hotel se il testo di ricerca è presente nel nome dell'hotel, nella descrizione o nell'elenco di tag (servizi). Si noti come i pesi dell'attribuzione dei punteggi favoriscono determinati campi. Se il testo di ricerca viene visualizzato in un altro campo, non elencato di seguito, avrà un peso pari a 1. Ovviamente, maggiore è il punteggio, maggiore è il risultato visualizzato nella visualizzazione.

    {
       "name": "boostByField",
       "text": {
           "weights": {
               "Tags": 3,
               "HotelName": 2,
               "Description": 1.5,
               "Description_fr": 1.5,
           }
       }
    }
    
  2. Il seguente profilo di punteggio alternativo aumenta significativamente il punteggio se un parametro fornito include uno o più dal elenco di tag (che chiamiamo "servizi"). Il punto chiave di questo profilo è che è necessario specificare un parametro contenente 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 l'ultima data rinnovata aumenterà il punteggio, ma solo se i dati rientrano entro 730 giorni (2 anni) della 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
    }
    

    A questo punto, vediamo se questi profili funzionano come pensiamo che dovrebbero.

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 "home controller". Aggiungere l'istruzione using seguente (per facilitare la creazione di elenchi).

    using System.Linq;
    
  2. Per questo esempio, è necessaria la chiamata iniziale a Index per eseguire un po' di più rispetto alla semplice restituzione della vista iniziale. Il metodo cerca ora fino a 20 servizi da visualizzare nella 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. Sono necessari due metodi privati per salvare le funzioni nell'archiviazione temporanea e per recuperarle da questa archiviazione al fine di 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 in base alle esigenze. 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 i commenti per ciascuna selezione di switch.

  5. Non è necessario apportare modifiche all'azione Avanti , se è stato completato il codice aggiuntivo per la sezione precedente sull'ordinamento in base a più proprietà.

Eseguire e testare l'app

  1. Avvia l'app. Nella vista dovrebbe essere visualizzato un set completo di servizi.

  2. Per l'ordinamento, selezionare "Per classificazione numerica" fornirà l'ordinamento numerico già implementato in questo tutorial, con la data di ristrutturazione a decidere tra gli hotel di uguale classificazione.

    Ordinamento della

  3. Ora prova il profilo "Per servizi". Effettuare varie selezioni di servizi e verificare che gli hotel con tali servizi vengano alzati di livello nell'elenco dei risultati.

    Ordinamento di

  4. Prova il profilo "Per data di ristrutturazione/Profilo di valutazione" per vedere se ottieni quello che ti aspetti. Solo gli hotel recentemente rinnovati dovrebbero ottenere una spinta di freschezza .

Risorse

Per ulteriori informazioni, vedere la sezione seguente: Aggiungere profili di punteggio a un indice di Ricerca cognitiva di Azure.

Elementi chiave

Si considerino le considerazioni seguenti di questo progetto:

  • Gli utenti si aspettano che i risultati della ricerca vengano ordinati, più rilevanti per primi.
  • I dati devono essere strutturati in modo che l'ordinamento sia semplice. Non siamo riusciti a ordinare facilmente per "più economici" prima, perché i dati non sono strutturati per consentire l'ordinamento senza codice aggiuntivo.
  • Esistono molti livelli per ordinare, per distinguere i risultati con lo stesso valore a un livello superiore di ordinamento.
  • È naturale che alcuni risultati vengano ordinati in ordine crescente (ad esempio, distanza da un punto) e alcuni in ordine decrescente (ad esempio, la classificazione degli ospiti).
  • I profili di punteggio possono essere definiti quando i confronti numerici non sono disponibili o non sono sufficientemente intelligenti per un set di dati. L'assegnazione dei punteggi a ogni risultato consentirà di ordinare e visualizzare i risultati in modo intelligente.

Passaggi successivi

Questa serie di esercitazioni per C# è stata completata. Si dovrebbe avere acquisito una conoscenza preziosa delle API di Ricerca cognitiva di Azure.

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