Udostępnij przez


Samouczek: zamawianie wyników wyszukiwania przy użyciu zestawu SDK platformy .NET

W całej tej serii samouczków wyniki zostały zwrócone i wyświetlone w kolejności domyślnej. W tym samouczku dodasz pierwotne i drugorzędne 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 złożonych typów.

W tym poradniku nauczysz się, jak:

  • Porządkowaj wyniki 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

Przegląd

Ten samouczek rozszerza projekt nieskończonego przewijania utworzony w samouczku zatytułowanym Dodawanie stronicowania do wyników wyszukiwania.

Ukończona wersja kodu w tym samouczku można znaleźć w następującym projekcie:

Wymagania wstępne

Porządkowaj wyniki na podstawie jednej właściwości

Podczas sortowania wyników według jednej właściwości, takiej jak ocena hotelu, chcemy nie tylko uporządkowanych wyników, ale także potwierdzenia, że porządek jest poprawny. Dodanie pola Ocena do wyników pozwala nam potwierdzić, że wyniki są sortowane poprawnie.

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

Nie ma potrzeby modyfikowania żadnego z modeli, aby umożliwić zamawianie. Tylko widok i kontroler wymagają aktualizacji. Zacznij od otwarcia kontrolera macierzystego.

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

  1. W 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 / Notatka

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

  2. Teraz uruchom aplikację i wprowadź dowolny wspólny termin wyszukiwania. Wyniki mogą być lub nie być w prawidłowej kolejności, ponieważ ani Ty jako deweloper, ani użytkownik nie mają łatwego sposobu na weryfikację 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;
    }
    

    Wskazówka

    Przeglądarki zwykle buforują pliki css, co może prowadzić do używania starego pliku css, przez co 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 hotelowe. -->) 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 z tych dwóch sytuacji musimy zaktualizować zarówno akcję Next 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ę przewijania 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.

    Kolejność 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 sortowania, dodajmy kod do wyświetlenia zakresu cen pokoi. Dodajemy ten kod zarówno w celu pokazania wyodrębniania danych ze złożonego typu, jak i abyśmy mogli omówić kolejność wyników na podstawie ceny (może od najtańszych).

Dodaj zakres stawek za pokój do widoku

  1. Dodaj właściwości zawierające najtańszą i najdroższą cenę za pokój do modelu Hotel.cs.

    // Room rate range
    public double cheapest { get; set; }
    public double expensive { get; set; }
    
  2. Oblicz stawki za pokój na końcu akcji Index(SearchData model) w kontrolerze macierzystym. 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 w kontrolerze "home", aby przekazywać zakres stawki 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ę przewijania w widoku, w celu obsługi tekstu stawek za pokój.

    <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 cen pokoju.

    Wyświetlanie zakresów stawek za pokój

Właściwość OrderBy parametrów wyszukiwania nie zaakceptuje wpisu, takiego jak Rooms.BaseRate , aby zapewnić najtańszą stawkę za pokój, 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.

Porządkowaj wyniki na podstawie wielu wartości

Teraz pytanie brzmi, jak rozróżnić hotele z tą samą oceną. Jednym ze sposobów może być dodatkowe uporządkowanie na podstawie daty ostatniego remontu hotelu, tak aby te przebudowane ostatnio pojawiały się wyżej w wynikach.

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

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

    Wskazówka

    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 rozróż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ę przewijanej 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 z tą samą oceną są teraz wyświetlane w kolejności malejącej daty renowacji.

    Zamawianie w dniu renowacji

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), zapewniając dokładną sekwencję kolejności. 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 zawiera 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ę, jak jest zdefiniowany profil oceniania, a następnie spróbujmy napisać kod, aby je wyszukać.

Sposób definiowania profilów oceniania

Profile oceny są definiowane w indeksie wyszukiwania na etapie projektowania. Indeks hoteli tylko do odczytu hostowany przez firmę Microsoft ma trzy profile oceny. W tej sekcji omawiane są profile oceniania i pokazano, jak korzystać z nich w kodzie.

  1. Poniżej znajduje się domyślny profil oceniania dla zestawu danych hoteli, używany, gdy nie określasz 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ę, że 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 znacząco poprawia wynik. Ostatnia odnowiona data zwiększy również wynik, ale tylko wtedy, gdy dane te mieszczą się w ciągu 730 dni (2 lata) 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ą zgodnie z tym, jak 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 Index, aby zrobić coś więcej niż tylko zwrócenie widoku początkowego. Metoda wyszukuje teraz maksymalnie 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ącymi wartościami.

    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 dotyczącymi każdego z wyborów przełącznika.

  5. Nie musimy wprowadzać żadnych zmian w działaniu Następny, jeśli ukończono dodatkowy kod dla poprzedniej sekcji porządkowania według wielu właściwości.

Uruchamianie i testowanie aplikacji

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

  2. Aby uporządkować, wybranie opcji "Według klasyfikacji liczbowej" zapewni ci uporządkowanie liczbowe już zaimplementowane w tym samouczku, z datą renowacji rozstrzygającą między hotelami o równej ocenie.

    Sortowanie

  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 "Według wyremontowanej daty/oceny", 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 usługi Azure Cognitive Search.

Wnioski

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

  • Użytkownicy będą oczekiwać, że wyniki wyszukiwania będą uporządkowane, najbardziej istotne jako pierwsze.
  • Dane wymagają struktury, dzięki czemu kolejność jest łatwa. Nie byliśmy w stanie najpierw posortować na "najtańszy", ponieważ dane nie mają struktury umożliwiającej zamawianie bez dodatkowego kodu.
  • Istnieje wiele poziomów kolejności, aby odróżnić wyniki, które mają tę samą wartość na wyższym poziomie kolejności.
  • 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.

Dalsze kroki

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

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