Udostępnij za pośrednictwem


Samouczek: określanie kolejności wyników wyszukiwania przy użyciu zestawu .NET SDK

W tej serii samouczków wyniki zostały zwrócone i wyświetlone w kolejności domyślnej. W tym samouczku dodasz podstawowe i pomocnicze kryteria sortowania. Jako alternatywa dla porządkowania na podstawie wartości liczbowych, w ostatnim przykładzie pokazano, jak sklasyfikować wyniki na podstawie niestandardowego profilu oceniania. Przejdziemy również nieco głębiej do wyświetlania typów złożonych.

Ten samouczek zawiera informacje na temat wykonywania następujących czynności:

  • Kolejność wyników na podstawie jednej właściwości
  • Porządkowaj wyniki na podstawie wielu właściwości
  • Porządkowanie wyników na podstawie profilu oceniania

Omówienie

Ten samouczek rozszerza nieskończony projekt przewijania utworzony w samouczku Dodawanie stronicowania do wyników wyszukiwania .

Gotowa wersja kodu w tym samouczku znajduje się w następującym projekcie:

Wymagania wstępne

Kolejność wyników na podstawie jednej właściwości

Podczas zamawiania wyników na podstawie jednej właściwości, takiej jak ocena hotelu, chcemy nie tylko uporządkowanych wyników, ale także potwierdzić, że zamówienie jest poprawne. Dodanie pola Ocena do wyników pozwala nam potwierdzić, że wyniki są prawidłowo posortowane.

W tym ćwiczeniu dodamy również nieco więcej do wyświetlania wyników: najtańszą stawkę pokoju i najdroższą stawkę pokoju dla każdego hotelu.

Nie ma potrzeby modyfikowania żadnego z modeli w celu włączenia kolejności. Tylko widok i kontroler wymagają aktualizacji. Zacznij od otwarcia kontrolera macierzystego.

Dodawanie właściwości OrderBy do parametrów wyszukiwania

  1. W pliku HomeController.cs dodaj opcję OrderBy i dołącz właściwość Rating z malejącą kolejnością sortowania. W metodzie Index(SearchData model) dodaj następujący wiersz do parametrów wyszukiwania.

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

    Uwaga

    Domyślna kolejność jest rosnąca, ale można dodać asc do właściwości , aby to wyjaśnić. Kolejność malejąca jest określana przez dodanie descelementu .

  2. Teraz uruchom aplikację i wprowadź dowolny wspólny termin wyszukiwania. Wyniki mogą być w prawidłowej kolejności, ponieważ nie jesteś deweloperem, a nie użytkownikiem, nie ma łatwego sposobu sprawdzania wyników.

  3. Wyjaśnijmy, że wyniki są uporządkowane według klasyfikacji. Najpierw zastąp klasy box1 i box2 w pliku hotels.css następującymi klasami (wszystkie te klasy są nowymi, których potrzebujemy w tym samouczku).

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

    Porada

    Przeglądarki zwykle buforować pliki css, co może prowadzić do użycia starego pliku css, a zmiany są ignorowane. Dobrym sposobem jest dodanie ciągu zapytania z parametrem wersji do linku. Przykład:

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

    Zaktualizuj numer wersji, jeśli uważasz, że stary plik CSS jest używany przez przeglądarkę.

  4. Dodaj właściwość Rating do parametru Select w metodzie Index(SearchData model), aby wyniki zawierały następujące trzy pola:

    options.Select.Add("HotelName");
    options.Select.Add("Description");
    options.Select.Add("Rating");
    
  5. Otwórz widok (index.cshtml) i zastąp pętlę renderowania (<!-- Pokaż dane hotelu. -->) następującym kodem.

    <!-- 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. Ocena musi być dostępna zarówno na pierwszej wyświetlonej stronie, jak i na kolejnych stronach, które są wywoływane za pośrednictwem nieskończonego przewijania. W przypadku ostatnich tych dwóch sytuacji musimy zaktualizować zarówno akcję Dalej w kontrolerze, jak i funkcję przewijaną w widoku. Począwszy od kontrolera, zmień metodę Next na następujący kod. Ten kod tworzy i komunikuje tekst oceny.

    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. Teraz zaktualizuj funkcję przewijaną w widoku, aby wyświetlić tekst oceny.

    <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. Teraz ponownie uruchom aplikację. Wyszukaj dowolny wspólny termin, taki jak "wifi", i sprawdź, czy wyniki są uporządkowane według kolejności malejącej klasyfikacji hotelowej.

    Porządkowanie na podstawie klasyfikacji

    Zauważysz, że kilka hoteli ma identyczną ocenę, a więc ich wygląd na wyświetlaczu jest ponownie kolejnością znalezienia danych, która jest dowolna.

    Zanim przyjrzymy się dodaniu drugiego poziomu kolejności, dodajmy kod, aby wyświetlić zakres stawek za pokój. Dodajemy ten kod, aby pokazać wyodrębnianie danych z typu złożonego, a także omówić kolejność wyników na podstawie ceny (najtaniej być może).

Dodawanie zakresu stawek za pokój do widoku

  1. Dodaj właściwości zawierające najtańszą i najdroższą stawkę pokoju do modelu Hotel.cs.

    // Room rate range
    public double cheapest { get; set; }
    public double expensive { get; set; }
    
  2. Oblicz stawki pomieszczeń na końcu akcji Index(Model SearchData) w kontrolerze macierzysym. Dodaj obliczenia po przechowywaniu danych tymczasowych.

    // 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. Dodaj właściwość Rooms do parametru Select w metodzie akcji Index(SearchData model) kontrolera.

    options.Select.Add("Rooms");
    
  4. Zmień pętlę renderowania w widoku, aby wyświetlić zakres szybkości dla pierwszej strony wyników.

    <!-- 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. Zmień metodę Next na kontrolerze macierzystych, aby komunikować się z zakresem szybkości dla kolejnych stron wyników.

    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. Zaktualizuj funkcję przewijaną w widoku, aby obsłużyć tekst współczynników pomieszczeń.

    <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. Uruchom aplikację i sprawdź, czy są wyświetlane zakresy szybkości pokoju.

    Wyświetlanie zakresów stawek pomieszczeń

Właściwość OrderBy parametrów wyszukiwania nie zaakceptuje wpisu takiego jak Rooms.BaseRate , aby zapewnić najtańszą stawkę pokoju, nawet jeśli pokoje zostały już posortowane według stawek. W takim przypadku pokoje nie są sortowane według stawek. Aby wyświetlić hotele w przykładowym zestawie danych uporządkowane według stawki pokoju, należy posortować wyniki w kontrolerze domu i wysłać te wyniki do widoku w żądanej kolejności.

Kolejność wyników na podstawie wielu wartości

Teraz pytanie brzmi, jak rozróżnić hotele o tej samej ocenie. Jednym z podejść może być zamówienie pomocnicze w oparciu o ostatni raz hotel został odnowiony, aby ostatnio odnowione hotele pojawiły się wyższe w wynikach.

  1. Aby dodać drugi poziom kolejności, dodaj metodę LastRenovationDate do wyników wyszukiwania i orderBy w metodzie Index(SearchData model).

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

    Porada

    Na liście OrderBy można wprowadzić dowolną liczbę właściwości. Jeśli hotele miały taką samą ocenę i datę renowacji, można wprowadzić trzecią nieruchomość, aby je odróżnić.

  2. Ponownie musimy zobaczyć datę renowacji w widoku, aby mieć pewność, że kolejność jest poprawna. Dla czegoś takiego jak renowacja, prawdopodobnie tylko rok jest wystarczający. Zmień pętlę renderowania w widoku na następujący kod.

    <!-- 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. Zmień metodę Next w kontrolerze domu, aby przekazać składnik roku ostatniej daty renowacji.

        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. Zmień funkcję przewijaną w widoku, aby wyświetlić tekst renowacji.

    <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. Uruchom aplikację. Wyszukaj wspólny termin, taki jak "basen" lub "widok", i sprawdź, czy hotele o tej samej ocenie są teraz wyświetlane w kolejności malejącej daty renowacji.

    Zamawianie w dniu remontu

Porządkowanie wyników na podstawie profilu oceniania

Przykłady podane w samouczku do tej pory pokazują, jak porządkować wartości liczbowe (ocena i data renowacji), podając dokładną sekwencję zamawiania. Jednak niektóre wyszukiwania i niektóre dane nie nadają się do tak łatwego porównania między dwoma elementami danych. W przypadku zapytań wyszukiwania pełnotekstowego usługa Cognitive Search obejmuje koncepcję klasyfikacji. Profile oceniania można określić, aby wpływać na sposób klasyfikacji wyników, zapewniając bardziej złożone i jakościowe porównania.

Profile oceniania są definiowane w schemacie indeksu. Na danych dotyczących hoteli skonfigurowano kilka profilów oceniania. Przyjrzyjmy się zdefiniowaniu profilu oceniania, a następnie spróbujmy napisać kod, aby je wyszukać.

Sposób definiowania profilów oceniania

Profile oceniania są definiowane w indeksie wyszukiwania w czasie projektowania. Indeks hoteli tylko do odczytu obsługiwany przez firmę Microsoft ma trzy profile oceniania. W tej sekcji opisano profile oceniania i pokazano, jak używać kodu w kodzie.

  1. Poniżej znajduje się domyślny profil oceniania dla zestawu danych hoteli używany, gdy nie określisz żadnego parametru OrderBy lub ScoringProfile . Ten profil zwiększa ocenę dla hotelu, jeśli tekst wyszukiwania znajduje się w nazwie hotelu, opisie lub liście tagów (udogodnienia). Zwróć uwagę, jak wagi oceniania faworyzują określone pola. Jeśli tekst wyszukiwania pojawi się w innym polu, a nie na poniższej liście, będzie miał wagę 1. Oczywiście im wyższa ocena, tym wyższy wynik pojawia się w widoku.

    {
       "name": "boostByField",
       "text": {
           "weights": {
               "Tags": 3,
               "HotelName": 2,
               "Description": 1.5,
               "Description_fr": 1.5,
           }
       }
    }
    
  2. Poniższy alternatywny profil oceniania znacznie zwiększa ocenę, jeśli podany parametr zawiera co najmniej jedną listę tagów (które nazywamy "udogodnieniami"). Kluczowym punktem tego profilu jest to, że należy podać parametr zawierający tekst. Jeśli parametr jest pusty lub nie zostanie podany, zostanie zgłoszony błąd.

    {
        "name":"boostAmenities",
        "functions":[
            {
            "fieldName":"Tags",
            "freshness":null,
            "interpolation":"linear",
            "magnitude":null,
            "distance":null,
            "tag":{
                "tagsParameter":"amenities"
            },
            "type":"tag",
            "boost":5
            }
        ],
        "functionAggregation":0
    },
    
  3. W tym trzecim profilu ocena hotelu daje znaczący impuls do wyniku. Data ostatniej renowacji zwiększy również wynik, ale tylko wtedy, gdy dane te mieszczą się w ciągu 730 dni (2 lat) od bieżącej daty.

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

    Teraz sprawdźmy, czy te profile działają, jak uważamy, że powinny.

Dodawanie kodu do widoku w celu porównania profilów

  1. Otwórz plik index.cshtml i zastąp sekcję <body> poniższym kodem.

    <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. Otwórz plik SearchData.cs i zastąp klasę SearchData następującym kodem.

    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. Otwórz plik hotels.css i dodaj następujące klasy 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;
    }
    

Dodawanie kodu do kontrolera w celu określenia profilu oceniania

  1. Otwórz plik kontrolera domu. Dodaj następującą instrukcję using (aby ułatwić tworzenie list).

    using System.Linq;
    
  2. W tym przykładzie potrzebujemy początkowego wywołania indeksu , aby wykonać nieco więcej czynności niż tylko zwrócenie widoku początkowego. Metoda wyszukuje teraz do 20 udogodnień do wyświetlenia w widoku.

        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. Potrzebujemy dwóch metod prywatnych, aby zapisać aspekty w magazynie tymczasowym i odzyskać je z magazynu tymczasowego i wypełnić model.

        // 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. W razie potrzeby należy ustawić parametry OrderBy i ScoringProfile . Zastąp istniejącą metodę Index(SearchData model) następującą wartością.

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

    Zapoznaj się z komentarzami dla każdego z zaznaczeń przełącznika .

  5. Nie musimy wprowadzać żadnych zmian w akcji Dalej , jeśli ukończono dodatkowy kod dla poprzedniej sekcji na podstawie wielu właściwości.

Uruchamianie i testowanie aplikacji

  1. Uruchom aplikację. W widoku powinien zostać wyświetlony pełny zestaw udogodnień.

  2. W przypadku zamawiania wybranie opcji "Według klasyfikacji liczbowej" spowoduje nadanie kolejności liczbowej, która została już zaimplementowana w tym samouczku, z datą renowacji decydującą się na hotele o równej ocenie.

    Zamawianie

  3. Teraz wypróbuj profil "Według udogodnień". Dokonaj różnych wyborów udogodnień i sprawdź, czy hotele z tymi udogodnieniami są promowane na liście wyników.

    Zamawianie

  4. Wypróbuj profil "Wyremontowana data/ocena", aby sprawdzić, czy otrzymasz oczekiwaną datę. Tylko niedawno odnowione hotele powinny uzyskać impuls świeżości .

Zasoby

Aby uzyskać więcej informacji, zobacz następujące dodawanie profilów oceniania do indeksu Azure Cognitive Search.

Wnioski

Rozważ następujące wnioski z tego projektu:

  • Użytkownicy będą oczekiwać, że wyniki wyszukiwania będą uporządkowane, a najbardziej istotne.
  • Dane muszą być ustrukturyzowane, aby porządkowanie było łatwe. Nie byliśmy w stanie posortować "najtańszych" najpierw, ponieważ dane nie mają struktury umożliwiającej zamawianie bez dodatkowego kodu.
  • Istnieje wiele poziomów do porządkowania, aby odróżnić wyniki, które mają tę samą wartość na wyższym poziomie porządkowania.
  • Jest to naturalne, że niektóre wyniki mają być uporządkowane w kolejności rosnącej (powiedzmy, odległość od punktu) i niektóre w kolejności malejącej (powiedzmy, ocena gościa).
  • Profile oceniania można zdefiniować, gdy porównania liczbowe nie są dostępne lub nie są wystarczająco inteligentne dla zestawu danych. Ocenianie każdego wyniku pomoże uporządkować i wyświetlić wyniki inteligentnie.

Następne kroki

Ta seria samouczków języka C# została ukończona — musisz zdobyć cenną wiedzę na temat interfejsów API Azure Cognitive Search.

Aby uzyskać więcej informacji i samouczków, rozważ przejrzenie katalogu szkoleń usługi Microsoft Learn lub innych samouczków w dokumentacji Azure Cognitive Search.