教程:使用 .NET SDK 新增自動完成和建議

瞭解如何在用戶開始輸入搜尋方塊時實作自動完成 (typeahead 查詢和建議的結果)。 在本教學課程中,我們將分別顯示自動完成的查詢和建議的結果,然後一起顯示。 使用者可能只需要輸入兩或三個字元,才能找出所有可用的結果。

在本教學課程中,您將瞭解如何:

  • 新增建議
  • 將醒目提示新增至建議
  • 新增自動完成
  • 結合自動完成和建議

概觀

本教學課程會將自動完成和建議的結果新增至上一個 將分頁新增至搜尋結果 教學課程。

您可以在下列專案中找到本教學課程中的最終版本程式碼:

先決條件

  • 2a-add-paging (GitHub) 的解決方案。 此專案可以是您自己從上一個教學課程建置的版本,或是從 GitHub 建立的複本。

新增建議

讓我們從向使用者提供替代方案的最簡單案例開始:建議結果的下拉式清單。

  1. 在 index.cshtml 檔案中,將 TextBoxFor 語句變更@id為 azureautosuggest。

     @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautosuggest" }) <input value="" class="searchBoxSubmit" type="submit">
    
  2. 在此語句之後,於結束標籤 </div> 後輸入此腳本。 此腳本會利用開放原始碼 jQuery UI 連結庫中的 自動完成小工具 來呈現建議結果的下拉式清單。

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

    "azureautosuggest"標識符會將上述腳本連接到搜尋方塊。 小工具的來源設定被設為一個呼叫 Suggest API 的 Suggest 方法,此 API 具有兩個查詢參數:highlights 和 fuzzy,這兩者在此實例中皆設定為 false。 此外,至少需要兩個字元才能觸發搜尋。

將 jQuery 腳本的參考新增至檢視

  1. 若要存取 jQuery 函式庫,請將檢視檔案的 <head> 區段修改為以下程式碼:

    <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. 因為我們引進了新的 jQuery 參考,所以我們也需要移除或批注化_Layout.cshtml 檔案中的預設 jQuery 參考(在 Views/Shared 資料夾中)。 找出下列幾行,並將第一個腳本行批註化,如下所示。 這項變更可避免 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>
    

    現在,我們可以使用預先定義的 Autocomplete jQuery 函式。

將建議動作新增至控制器

  1. 在首頁控制器中,新增 SuggestAsync 動作(在 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);
    }
    

    Size 參數會指定要傳回的結果數目(如果未指定,預設值為 5)。 建立索引時,會在搜尋索引上指定 建議工具 。 在由 Microsoft 裝載的範例旅館索引中,建議程式名稱為 「sg」,它會在 HotelName 字段中以獨佔方式搜尋建議相符專案。

    模糊比對允許輸出中包含「接近遺漏」,最多一個編輯距離。 如果 醒目提示 參數設定為 true,則會將粗體 HTML 標籤新增至輸出。 我們將在下一節中將這兩個參數設定為 true。

  2. 您可能會遇到一些語法錯誤。 如果是,請將下列兩 個using 語句新增至檔案頂端。

    using System.Collections.Generic;
    using System.Linq;
    
  3. 執行應用程式。 例如,當您輸入 「po」 時,是否會收到一系列選項? 現在試試「pa」。

    輸入 *po* 會顯示兩個建議

    請注意,您輸入的字母 必須 開始一個字,而不只是包含在單字中。

  4. 在檢視腳本中,將 &fuzzy 設定為 true,然後再次執行應用程式。 現在輸入 「po」。 請注意,搜尋假設您可能輸入了一個錯誤的字母。

    輸入 *pa* 並將模糊設定為 true

    如果您有興趣, Azure 認知搜尋中的 Lucene 查詢語法 會詳細說明模糊搜尋中使用的邏輯。

將醒目提示新增至建議

我們可以藉由將 高亮 參數設定為 true,來改善對用戶的建議外觀。 不過,首先,我們需要將一些程式代碼新增至檢視,以顯示粗體文字。

  1. 在檢視(index.cshtml)中,於先前所述的"azureautosuggest"腳本之後新增下列腳本。

    <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. 現在請變更文字框的 ID 使其顯示為如下所示。

    @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azuresuggesthighlights" }) <input value="" class="searchBoxSubmit" type="submit">
    
  3. 再次執行應用程式,您應該會在建議中看到輸入的文字粗體。 請嘗試輸入 「pa」。

    輸入 *pa* 使其醒目

    上述醒目提示文本中使用的邏輯不是萬無一失的。 如果您輸入相同名稱中出現兩次的字詞,則粗體結果不完全是您想要的結果。 請嘗試輸入 「mo」。

    開發人員需要回答的一個問題是,腳本在什麼時候運行得「足夠好」,又在什麼時候需要解決其問題。 我們不會在本教學課程中進一步討論醒目提示,但如果醒目提示對您的數據無效,您可以考慮尋找一個精確的演算法。 如需詳細資訊,請參閱 命中突出顯示。

新增自動完成

另一個與建議稍有不同的變化是自動完成(有時稱為「預先輸入」),可完成查詢字詞。 同樣地,我們會先從最簡單的實作開始,再改善用戶體驗。

  1. 請在檢視中輸入以下腳本,並遵循您先前的腳本。

    <script>
        $("#azureautocompletebasic").autocomplete({
            source: "/Home/Autocomplete",
            minLength: 2,
            position: {
                my: "left top",
                at: "left-23 bottom+10"
            }
        });
    </script>
    
  2. 現在請變更文字框的標識碼,使其顯示為下列內容。

    @Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautocompletebasic" }) <input value="" class="searchBoxSubmit" type="submit">
    
  3. 在主控制器中,輸入 SuggestAsync 動作之後的 AutocompleteAsync 動作。

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

    請注意,我們在自動完成搜尋中使用相同名稱為「sg」的建議工具函式,這與用於建議的函式相同(因此,我們只針對旅館名稱進行自動完成)。

    有一系列 AutocompleteMode 設定,而我們使用 OneTermWithContext。 如需其他選項的描述,請參閱 自動完成 API 。

  4. 執行應用程式。 請注意下拉式清單中顯示的選項範圍是單一單字。 請嘗試從 「re」 開始輸入單字。 請注意,隨著輸入更多字母,選項數目會減少。

    使用基本自動完成輸入

    就目前情況來說,您稍早執行的建議腳本可能比這個自動完成腳本更實用。 若要讓自動完成更容易使用,請考慮將它與建議的結果搭配使用。

結合自動完成和建議

結合自動完成和建議是我們選項中最複雜的,而且可能會提供最佳的用戶體驗。 我們想要顯示的是 Azure 認知搜尋為了自動完成輸入文字所提供的第一選擇,並且內嵌在正在輸入的文字中。 此外,我們想要以下拉式清單的形式提供一系列建議。

有一些函式庫提供這項功能,常被稱為「內嵌自動完成」或類似的名稱。 不過,我們會以原生方式實作這項功能,以便探索 API。 在此範例中,我們會先開始在控制器上工作。

  1. 在控制器中添加一個動作,以返回僅一個自動完成結果以及一些指定建議數目。 我們將呼叫此動作 AutoCompleteAndSuggestAsync。 在主控制器中,新增下列動作,並將其放在其他新動作之後。

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

    在結果清單中,首位顯示一個自動完成選項,後面接著所有的建議。

  2. 在檢視中,首先我們會實作一個技巧,讓淺灰色的自動完成字顯示在使用者輸入的粗體文字正下方。 HTML 包含此用途的相對定位。 將 TextBoxFor 語句(及其周圍的 <div> 語句)變更為下列內容:請注意,我們識別的第二個搜尋方塊位於正常搜尋方塊的正下方,其方法為將此搜尋方塊從預設位置向下拉 39 個像素!

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

    請注意,我們會再次將標識符變更為 azureautocomplete ,在此情況下。

  3. 在檢視中,將下列腳本輸入在您目前已輸入的所有腳本之後。 由於腳本處理的各種輸入行為,腳本相當冗長且複雜。

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

    請注意 間隔函式 的使用方式,即當使用者輸入的內容不再匹配時,如何清除底層文字,以及將大小寫設定為與使用者輸入相同(例如當搜尋“pa”時,其可匹配“PA”、“pA”、“Pa”),使得呈現的文字整齊。

    閱讀文本中的批註,以取得更完整的瞭解。

  4. 最後,我們需要稍微調整兩個 HTML 類別,使其透明。 將下列這一行新增至 hotels.css 檔案中的 searchBoxForm 和 searchBox 類別。

    background: rgba(0,0,0,0);
    
  5. 現在執行應用程式。 在搜尋方塊中輸入 「pa」。 您是否會看到「宮殿」作為自動完成的建議,以及名字中包含「pa」的兩家酒店?

    使用內嵌自動完成和建議輸入

  6. 請嘗試按下Tab鍵以接受自動完成的建議,然後嘗試使用箭頭鍵和Tab鍵來選取建議,接著使用滑鼠單擊嘗試。 確認腳本會整齊地處理所有這些情況。

    您可能會決定載入一個提供此功能的程式庫會比較簡單,但現在您至少知道一種方法能讓內聯自動完成運作。

外賣

請考慮這個專案的下列要點:

  • 自動完成(也稱為「輸入提示」)和建議可讓使用者只輸入幾個鍵,就能找出他們想要的內容。
  • 自動完成和建議一起運作可提供豐富的用戶體驗。
  • 一律使用所有形式的輸入來測試自動完成函式。
  • 使用 setInterval 函式有助於驗證和更正 UI 元素。

後續步驟

在下一個教學課程中,我們將探討另一種改善用戶體驗的方式,使用面向功能以單鍵縮小搜尋範圍。