Samouczek: dodawanie autouzupełniania i sugestii przy użyciu zestawu SDK platformy .NET

Dowiedz się, jak zaimplementować autouzupełnianie (zapytania typowe i sugerowane wyniki), gdy użytkownik zacznie wpisywać dane w polu wyszukiwania. W tym samouczku pokażemy zapytania autouzupełniane i sugerowane wyniki oddzielnie, a następnie razem. Użytkownik może wpisać tylko dwa lub trzy znaki, aby zlokalizować wszystkie dostępne wyniki.

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

  • Dodawanie sugestii
  • Dodawanie wyróżniania do sugestii
  • Dodawanie autouzupełniania
  • Łączenie autouzupełniania i sugestii

Omówienie

Ten samouczek dodaje autouzupełnianie i sugerowane wyniki do poprzedniego samouczka Dodawanie stronicowania do wyników wyszukiwania .

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

Wymagania wstępne

Dodawanie sugestii

Zacznijmy od najprostszego przypadku oferowania alternatyw dla użytkownika: listy rozwijanej sugerowanych wyników.

  1. W pliku index.cshtml zmień instrukcję @idTextBoxFor na azureautosuggest.

     @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautosuggest" }) <input value="" class="searchBoxSubmit" type="submit">
    
  2. Po wykonaniu tej instrukcji po zamknięciu </div> wprowadź ten skrypt. Ten skrypt korzysta z widżetu autouzupełniania z biblioteki interfejsu użytkownika jQuery typu open source, aby wyświetlić listę rozwijaną sugerowanych wyników.

    <script>
        $("#azureautosuggest").autocomplete({
            source: "/Home/SuggestAsync?highlights=false&fuzzy=false",
            minLength: 2,
            position: {
                my: "left top",
                at: "left-23 bottom+10"
            }
        });
    </script>
    

    Identyfikator "azureautosuggest" łączy powyższy skrypt z polem wyszukiwania. Opcja źródłowa widżetu jest ustawiona na metodę Suggest, która wywołuje interfejs API Suggest z dwoma parametrami zapytania: wyróżnia i rozmyty, oba mają wartość false w tym wystąpieniu. Ponadto do wyzwolenia wyszukiwania potrzebne są co najmniej dwa znaki.

Dodawanie odwołań do skryptów jQuery do widoku

  1. Aby uzyskać dostęp do biblioteki jQuery, zmień sekcję <head> pliku widoku na następujący kod:

    <head>
        <meta charset="utf-8">
        <title>Typeahead</title>
        <link href="https://code.jquery.com/ui/1.12.1/themes/start/jquery-ui.css"
              rel="stylesheet">
        <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
        <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    
        <link rel="stylesheet" href="~/css/hotels.css" />
    </head>
    
  2. Ponieważ wprowadzamy nowe odwołanie jQuery, musimy również usunąć lub dodać komentarz do domyślnego odwołania jQuery w pliku _Layout.cshtml (w folderze Views/Shared ). Znajdź następujące wiersze i oznacz jako komentarz pierwszy wiersz skryptu, jak pokazano. Ta zmiana pozwala uniknąć kolizji odwołań do biblioteki jQuery.

    <environment include="Development">
        <!-- <script src="~/lib/jquery/dist/jquery.js"></script> -->
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    

    Teraz możemy użyć wstępnie zdefiniowanych funkcji autouzupełniania jQuery.

Dodawanie akcji Sugerowanie do kontrolera

  1. W kontrolerze głównym dodaj akcję SuggestAsync (po akcji PageAsync ).

    public async Task<ActionResult> SuggestAsync(bool highlights, bool fuzzy, string term)
    {
        InitSearch();
    
        // Setup the suggest parameters.
        var options = new SuggestOptions()
        {
            UseFuzzyMatching = fuzzy,
            Size = 8,
        };
    
        if (highlights)
        {
            options.HighlightPreTag = "<b>";
            options.HighlightPostTag = "</b>";
        }
    
        // Only one suggester can be specified per index. It is defined in the index schema.
        // The name of the suggester is set when the suggester is specified by other API calls.
        // The suggester for the hotel database is called "sg", and simply searches the hotel name.
        var suggestResult = await _searchClient.SuggestAsync<Hotel>(term, "sg", options).ConfigureAwait(false);
    
        // Convert the suggested query results to a list that can be displayed in the client.
        List<string> suggestions = suggestResult.Value.Results.Select(x => x.Text).ToList();
    
        // Return the list of suggestions.
        return new JsonResult(suggestions);
    }
    

    Parametr Rozmiar określa, ile wyników ma być zwracanych (jeśli nieokreślone, wartość domyślna to 5). Sugestor jest określony w indeksie wyszukiwania podczas tworzenia indeksu. W przykładowym indeksie hoteli hostowanym przez firmę Microsoft nazwa sugestora to "sg" i wyszukuje sugerowane dopasowania wyłącznie w polu HotelName .

    Dopasowywanie rozmyte umożliwia "niemal pominięte" uwzględnianie w danych wyjściowych maksymalnie jednej odległości edycji. Jeśli parametr highlights ma wartość true, do danych wyjściowych zostaną dodane pogrubione tagi HTML. W następnej sekcji ustawimy oba parametry na true.

  2. Mogą wystąpić błędy składniowe. Jeśli tak, dodaj następujące dwie instrukcje using na początku pliku.

    using System.Collections.Generic;
    using System.Linq;
    
  3. Uruchom aplikację. Czy podczas wprowadzania "po", na przykład, uzyskasz szereg opcji? Teraz spróbuj użyć ciągu "pa".

    Wpisanie *po* wyświetla dwie sugestie

    Zwróć uwagę, że wprowadzone litery muszą rozpoczynać się od wyrazu, a nie po prostu być zawarte w słowie.

  4. W skrytecie widoku ustaw &rozmyte na true, a następnie ponownie uruchom aplikację. Teraz wprowadź "po". Zwróć uwagę, że wyszukiwanie zakłada, że wystąpiła jedna litera źle.

    Wpisywanie *pa* z rozmytą wartością true

    Jeśli cię interesuje, składnia zapytań Lucene w Azure Cognitive Search opisuje logikę używaną w wyszukiwaniach rozmytych szczegółowo.

Dodawanie wyróżniania do sugestii

Możemy poprawić wygląd sugestii użytkownika, ustawiając parametr highlights na true. Najpierw musimy jednak dodać kod do widoku, aby wyświetlić pogrubiony tekst.

  1. W widoku (index.cshtml) dodaj następujący skrypt po "azureautosuggest" opisanym wcześniej skrypcie.

    <script>
        var updateTextbox = function (event, ui) {
            var result = ui.item.value.replace(/<\/?[^>]+(>|$)/g, "");
            $("#azuresuggesthighlights").val(result);
            return false;
        };
    
        $("#azuresuggesthighlights").autocomplete({
            html: true,
            source: "/home/suggest?highlights=true&fuzzy=false&",
            minLength: 2,
            position: {
                my: "left top",
                at: "left-23 bottom+10"
            },
            select: updateTextbox,
            focus: updateTextbox
        }).data("ui-autocomplete")._renderItem = function (ul, item) {
            return $("<li></li>")
                .data("item.autocomplete", item)
                .append("<a>" + item.label + "</a>")
                .appendTo(ul);
        };
    </script>
    
  2. Teraz zmień identyfikator pola tekstowego, aby był odczytywany w następujący sposób.

    @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azuresuggesthighlights" }) <input value="" class="searchBoxSubmit" type="submit">
    
  3. Uruchom aplikację ponownie i w sugestiach powinien zostać wyświetlony wprowadzony tekst pogrubiony. Spróbuj wpisać "pa".

    Wpisywanie *pa* z wyróżnianiem

    Logika używana w powyższym skrypce wyróżniania nie jest niezawodna. Jeśli wprowadzisz termin, który będzie wyświetlany dwa razy w tej samej nazwie, pogrubione wyniki nie są całkiem takie, jak chcesz. Spróbuj wpisać "mo".

    Jednym z pytań, na które deweloper musi odpowiedzieć, jest to, kiedy skrypt działa "wystarczająco dobrze" i kiedy powinien zostać rozwiązany jego dziwactwa. Nie będziemy już wyróżniać w tym samouczku, ale znalezienie dokładnego algorytmu jest czymś, co należy wziąć pod uwagę, jeśli wyróżnianie danych nie jest skuteczne. Aby uzyskać więcej informacji, zobacz Wyróżnianie trafień.

Dodawanie autouzupełniania

Inną odmianą, nieco różni się od sugestii, jest autouzupełnianie (czasami nazywane "type-ahead"), które kończy termin zapytania. Ponownie zaczniemy od najprostszej implementacji, zanim poprawimy środowisko użytkownika.

  1. Wprowadź następujący skrypt w widoku, postępując zgodnie z poprzednimi skryptami.

    <script>
        $("#azureautocompletebasic").autocomplete({
            source: "/Home/Autocomplete",
            minLength: 2,
            position: {
                my: "left top",
                at: "left-23 bottom+10"
            }
        });
    </script>
    
  2. Teraz zmień identyfikator pola tekstowego, aby był odczytywany w następujący sposób.

    @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautocompletebasic" }) <input value="" class="searchBoxSubmit" type="submit">
    
  3. W kontrolerze głównym wprowadź akcję AutouzupełnianieSync po akcji SuggestAsync .

    public async Task<ActionResult> AutoCompleteAsync(string term)
    {
        InitSearch();
    
        // Setup the autocomplete parameters.
        var ap = new AutocompleteOptions()
        {
            Mode = AutocompleteMode.OneTermWithContext,
            Size = 6
        };
        var autocompleteResult = await _searchClient.AutocompleteAsync(term, "sg", ap).ConfigureAwait(false);
    
        // Convert the autocompleteResult results to a list that can be displayed in the client.
        List<string> autocomplete = autocompleteResult.Value.Results.Select(x => x.Text).ToList();
    
        return new JsonResult(autocomplete);
    }
    

    Zwróć uwagę, że używamy tej samej funkcji sugestora o nazwie "sg" w wyszukiwaniu autouzupełniania, tak jak w przypadku sugestii (dlatego staramy się automatycznie wypełniać nazwy hoteli).

    Istnieje szereg ustawień autouzupełnianiamode i używamy elementu OneTermWithContext. Aby uzyskać opis dodatkowych opcji, zobacz Autouzupełnianie interfejsu API .

  4. Uruchom aplikację. Zwróć uwagę, że zakres opcji wyświetlanych na liście rozwijanej to pojedyncze wyrazy. Spróbuj wpisać wyrazy rozpoczynające się od ciągu "re". Zwróć uwagę, że liczba opcji zmniejsza się w miarę wpisywania większej liczby liter.

    Wpisywanie przy użyciu podstawowego autouzupełniania

    W obecnej postaci skrypt sugestii, który został uruchomiony wcześniej, jest prawdopodobnie bardziej przydatny niż ten skrypt autouzupełniania. Aby zwiększyć przyjazną dla użytkownika funkcję autouzupełniania, rozważ użycie jej z sugerowanymi wynikami.

Łączenie autouzupełniania i sugestii

Łączenie autouzupełniania i sugestii jest najbardziej złożone z naszych opcji i prawdopodobnie zapewnia najlepsze środowisko użytkownika. Chcemy wyświetlić tekst wbudowany z wpisywanym tekstem, to pierwszy wybór Azure Cognitive Search na potrzeby autouzupełniania tekstu. Ponadto chcemy, aby zakres sugestii był listą rozwijaną.

Istnieją biblioteki, które oferują tę funkcję — często nazywane "wbudowanym autouzupełnianiem" lub podobną nazwą. Jednak zamierzamy natywnie zaimplementować tę funkcję, aby móc eksplorować interfejsy API. Najpierw rozpoczniemy pracę nad kontrolerem w tym przykładzie.

  1. Dodaj akcję do kontrolera, która zwraca tylko jeden wynik autouzupełniania wraz z określoną liczbą sugestii. Wywołamy tę akcję AutoUzupełnianieAndSuggestAsync. W kontrolerze macierzystym dodaj następującą akcję, postępując zgodnie z innymi nowymi akcjami.

    public async Task<ActionResult> AutoCompleteAndSuggestAsync(string term)
    {
        InitSearch();
    
        // Setup the type-ahead search parameters.
        var ap = new AutocompleteOptions()
        {
            Mode = AutocompleteMode.OneTermWithContext,
            Size = 1,
        };
        var autocompleteResult = await _searchClient.AutocompleteAsync(term, "sg", ap);
    
        // Setup the suggest search parameters.
        var sp = new SuggestOptions()
        {
            Size = 8,
        };
    
        // Only one suggester can be specified per index. The name of the suggester is set when the suggester is specified by other API calls.
        // The suggester for the hotel database is called "sg" and simply searches the hotel name.
        var suggestResult = await _searchClient.SuggestAsync<Hotel>(term, "sg", sp).ConfigureAwait(false);
    
        // Create an empty list.
        var results = new List<string>();
    
        if (autocompleteResult.Value.Results.Count > 0)
        {
            // Add the top result for type-ahead.
            results.Add(autocompleteResult.Value.Results[0].Text);
        }
        else
        {
            // There were no type-ahead suggestions, so add an empty string.
            results.Add("");
        }
    
        for (int n = 0; n < suggestResult.Value.Results.Count; n++)
        {
            // Now add the suggestions.
            results.Add(suggestResult.Value.Results[n].Text);
        }
    
        // Return the list.
        return new JsonResult(results);
    }
    

    Jedna opcja autouzupełniania jest zwracana w górnej części listy wyników , a następnie wszystkie sugestie.

  2. W widoku najpierw zaimplementujemy sztuczkę, aby jasnoszary wyraz autouzupełniania był renderowany bezpośrednio pod tekstem pogrubionym wprowadzonym przez użytkownika. Kod HTML obejmuje pozycjonowanie względne w tym celu. Zmień instrukcję TextBoxFor (i otaczające <jej instrukcje div> ) na następującą, zauważając, że drugie pole wyszukiwania zidentyfikowane jako poniżej znajduje się tuż pod naszym normalnym polem wyszukiwania, ściągając to pole wyszukiwania 39 pikseli od domyślnej lokalizacji!

    <div id="underneath" class="searchBox" style="position: relative; left: 0; top: 0">
    </div>
    
    <div id="searchinput" class="searchBoxForm" style="position: relative; left: 0; top: -39px">
        @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautocomplete" }) <input value="" class="searchBoxSubmit" type="submit">
    </div>
    

    Zwróć uwagę, że w tym przypadku ponownie zmienimy identyfikator na azureautocomplete .

  3. Również w widoku wprowadź następujący skrypt, po wszystkich skryptach, które zostały wprowadzone do tej pory. Skrypt jest długi i złożony ze względu na różne zachowania wejściowe, które obsługuje.

    <script>
        $('#azureautocomplete').autocomplete({
            delay: 500,
            minLength: 2,
            position: {
                my: "left top",
                at: "left-23 bottom+10"
            },
    
            // Use Ajax to set up a "success" function.
            source: function (request, response) {
                var controllerUrl = "/Home/AutoCompleteAndSuggestAsync?term=" + $("#azureautocomplete").val();
                $.ajax({
                    url: controllerUrl,
                    dataType: "json",
                    success: function (data) {
                        if (data && data.length > 0) {
    
                            // Show the autocomplete suggestion.
                            document.getElementById("underneath").innerHTML = data[0];
    
                            // Remove the top suggestion as it is used for inline autocomplete.
                            var array = new Array();
                            for (var n = 1; n < data.length; n++) {
                                array[n - 1] = data[n];
                            }
    
                            // Show the drop-down list of suggestions.
                            response(array);
                        } else {
    
                            // Nothing is returned, so clear the autocomplete suggestion.
                            document.getElementById("underneath").innerHTML = "";
                        }
                    }
                });
            }
        });
    
        // Complete on TAB.
        // Clear on ESC.
        // Clear if backspace to less than 2 characters.
        // Clear if any arrow key hit as user is navigating the suggestions.
        $("#azureautocomplete").keydown(function (evt) {
    
            var suggestedText = document.getElementById("underneath").innerHTML;
            if (evt.keyCode === 9 /* TAB */ && suggestedText.length > 0) {
                $("#azureautocomplete").val(suggestedText);
                return false;
            } else if (evt.keyCode === 27 /* ESC */) {
                document.getElementById("underneath").innerHTML = "";
                $("#azureautocomplete").val("");
            } else if (evt.keyCode === 8 /* Backspace */) {
                if ($("#azureautocomplete").val().length < 2) {
                    document.getElementById("underneath").innerHTML = "";
                }
            } else if (evt.keyCode >= 37 && evt.keyCode <= 40 /* Any arrow key */) {
                document.getElementById("underneath").innerHTML = "";
            }
        });
    
        // Character replace function.
        function setCharAt(str, index, chr) {
            if (index > str.length - 1) return str;
            return str.substr(0, index) + chr + str.substr(index + 1);
        }
    
        // This function is needed to clear the "underneath" text when the user clicks on a suggestion, and to
        // correct the case of the autocomplete option when it does not match the case of the user input.
        // The interval function is activated with the input, blur, change, or focus events.
        $("#azureautocomplete").on("input blur change focus", function (e) {
    
            // Set a 2 second interval duration.
            var intervalDuration = 2000, 
                interval = setInterval(function () {
    
                    // Compare the autocorrect suggestion with the actual typed string.
                    var inputText = document.getElementById("azureautocomplete").value;
                    var autoText = document.getElementById("underneath").innerHTML;
    
                    // If the typed string is longer than the suggestion, then clear the suggestion.
                    if (inputText.length > autoText.length) {
                        document.getElementById("underneath").innerHTML = "";
                    } else {
    
                        // If the strings match, change the case of the suggestion to match the case of the typed input.
                        if (autoText.toLowerCase().startsWith(inputText.toLowerCase())) {
                            for (var n = 0; n < inputText.length; n++) {
                                autoText = setCharAt(autoText, n, inputText[n]);
                            }
                            document.getElementById("underneath").innerHTML = autoText;
    
                        } else {
                            // The strings do not match, so clear the suggestion.
                            document.getElementById("underneath").innerHTML = "";
                        }
                    }
    
                    // If the element loses focus, stop the interval checking.
                    if (!$input.is(':focus')) clearInterval(interval);
    
                }, intervalDuration);
        });
    </script>
    

    Zwróć uwagę, że funkcja interval służy zarówno do czyszczenia tekstu bazowego, gdy nie pasuje do tekstu wpisywanego przez użytkownika, jak i ustawiania tego samego przypadku (górnego lub dolnego), ponieważ użytkownik wpisuje tekst (jak "pa" pasuje do "PA", "pA", "Pa" podczas wyszukiwania), tak aby nakładany tekst był schludny.

    Przeczytaj komentarze w skrycie, aby uzyskać pełniejszą wiedzę.

  4. Na koniec musimy wprowadzić niewielkie dostosowanie do dwóch klas HTML, aby były niewidoczne. Dodaj następujący wiersz do klas searchBoxForm i searchBox w pliku hotels.css.

    background: rgba(0,0,0,0);
    
  5. Teraz uruchom aplikację. Wprowadź ciąg "pa" w polu wyszukiwania. Czy otrzymujesz "pałac" jako sugestię autouzupełniania, wraz z dwoma hotelami zawierającymi "pa"?

    Wpisywanie przy użyciu wbudowanego autouzupełniania i sugestii

  6. Spróbuj zaakceptować sugestię autouzupełniania i spróbować wybrać sugestie przy użyciu klawiszy strzałek i klawisza tabulacji, a następnie spróbuj ponownie użyć myszy i jednego kliknięcia. Sprawdź, czy skrypt dokładnie obsługuje wszystkie te sytuacje.

    Możesz zdecydować, że łatwiej jest załadować tę funkcję w bibliotece, która oferuje tę funkcję, ale teraz wiesz co najmniej jeden sposób na automatyczne uzupełnianie wbudowane do pracy.

Wnioski

Weź pod uwagę następujące wnioski z tego projektu:

  • Autouzupełnianie (nazywane również "type-ahead") i sugestie mogą umożliwić użytkownikowi wpisywanie tylko kilku kluczy w celu zlokalizowania dokładnie tego, czego chcą.
  • Autouzupełnianie i sugestie współpracujące ze sobą mogą zapewnić rozbudowane środowisko użytkownika.
  • Zawsze testuj funkcje autouzupełniania przy użyciu wszystkich form danych wejściowych.
  • Użycie funkcji setInterval może być przydatne podczas weryfikowania i poprawiania elementów interfejsu użytkownika.

Następne kroki

W następnym samouczku przedstawiono inny sposób ulepszania środowiska użytkownika przy użyciu aspektów w celu zawężenia wyszukiwań jednym kliknięciem.