Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Узнайте, как реализовать автозавершение (предугадывающие запросы и предлагаемые результаты), когда пользователь начинает вводить текст в строку поиска. В этом руководстве мы покажем автоматически завершенные запросы и предлагаемые результаты отдельно, а затем вместе. Пользователю может потребоваться ввести только два или три символа, чтобы найти все доступные результаты.
В этом руководстве вы узнаете, как:
- Добавление предложений
- Добавление выделения в предложения
- Добавление автозаполнения
- Объедините автозавершение и предложения
Обзор
В этом руководстве добавляются автодополнение и предварительные результаты к предыдущему руководству по добавлению разбиения по страницам в результаты поиска.
Готовую версию кода для этого руководства можно найти в следующем проекте:
Предпосылки
- решение 2a-add-paging (GitHub). Этот проект может быть собственной версией, созданной из предыдущего руководства, или копией из GitHub.
Добавление предложений
Начнем с самого простого случая предложения альтернативных вариантов пользователю: раскрывающийся список предлагаемых результатов.
В файле index.cshtml измените
@idинструкцию TextBoxFor на azureautosuggest.@Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautosuggest" }) <input value="" class="searchBoxSubmit" type="submit">После этого оператора введите этот скрипт после закрытия </div>. Этот скрипт использует мини-приложение autocomplete из библиотеки пользовательского интерфейса jQuery с открытым кодом для представления раскрывающегося списка предлагаемых результатов.
<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 Предложения с двумя параметрами запроса: выделение и нечеткость, оба установлены false в этом экземпляре. Кроме того, для активации поиска требуется не менее двух символов.
Добавление ссылок на скрипты jQuery в представление
Чтобы получить доступ к библиотеке jQuery, измените <головной> раздел файла представления на следующий код:
<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>Так как мы представляем новую ссылку на jQuery, нам также нужно удалить или закомментировать ссылку jQuery по умолчанию в файле _Layout.cshtml (в папке 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>Теперь мы можем использовать предустановленные функции автозаполнения jQuery.
Добавьте действие "Предложить" в контроллер
В контроллере дома добавьте действие 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). Средство предложения указывается в индексе поиска при создании индекса. В примере индекса отелей, размещенного компанией Майкрософт, название подсказчика – "sg", и он ищет предлагаемые совпадения исключительно в поле HotelName.
Нечеткое сопоставление позволяет включать в выходные данные "почти промахи" до одного расстояния редактирования. Если параметр выделения имеет значение true, то в выходные данные добавляются полужирные HTML-теги. Мы зададим оба параметра значение true в следующем разделе.
Вы можете получить некоторые синтаксические ошибки. Если да, добавьте следующие два оператора using в начало файла.
using System.Collections.Generic; using System.Linq;Запустите приложение. Вы получаете диапазон вариантов, когда вводите "po", например? Теперь попробуйте "pa".
Обратите внимание, что буквы, которые вы вводите , должны начинать слово, а не просто включаться в слово.
В скрипте представления установите &fuzzy в значение true, и снова запустите приложение. Теперь введите "po". Обратите внимание, что поиск предполагает, что вы получили одно письмо неправильно.
Если вы хотите, синтаксис запросов Lucene в Когнитивном поиске Azure подробно описывает логику, используемую в нечетких поисках.
Добавить выделение в рекомендации
Мы можем улучшить внешний вид предложений для пользователя, установив для параметра выделения значение true. Однако сначала необходимо добавить в представление код, чтобы отобразить полужирный текст.
В представлении (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>Теперь измените идентификатор текстового поля, чтобы он читался следующим образом.
@Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azuresuggesthighlights" }) <input value="" class="searchBoxSubmit" type="submit">Запустите приложение снова, и вы увидите введенный текст полужирным шрифтом в предложениях. Попробуйте ввести "pa".
Логика, используемая в приведенном выше скрипте выделения, не безошибочна. Если ввести термин, который встречается дважды в одном и том же названии, то результаты, выделенные полужирным шрифтом, могут не соответствовать вашим ожиданиям. Попробуйте ввести "mo".
Один из вопросов, которые разработчик должен ответить, заключается в том, когда сценарий работает "достаточно хорошо", и когда следует решать его причуды. Мы не будем подробно останавливаться на подсветке в этом руководстве, но стоит рассмотреть поиск точного алгоритма, если выделение неэффективно для ваших данных. Дополнительные сведения см. также подсветка результатов поиска.
Добавление автозаполнения
Другим вариантом, немного отличающимся от предложенных вариантов, является автозавершение (иногда называемое "предугадывание ввода"), которое завершает поисковый запрос. Опять же, мы начнем с самой простой реализации, прежде чем улучшить взаимодействие с пользователем.
Введите следующий скрипт в представление, следуя предыдущим скриптам.
<script> $("#azureautocompletebasic").autocomplete({ source: "/Home/Autocomplete", minLength: 2, position: { my: "left top", at: "left-23 bottom+10" } }); </script>Теперь измените идентификатор текстового поля, чтобы он выглядел следующим образом.
@Html.TextBoxFor(m => m.searchText, new { @class = "searchBox", @id = "azureautocompletebasic" }) <input value="" class="searchBoxSubmit" type="submit">В домашнем контроллере введите действие AutocompleteAsync после действия 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); }Обратите внимание, что мы используем ту же функцию суггестера, которая называется "sg", как в поиске автодополнения, так и для предложений (поэтому мы пытаемся автодополнить только названия отелей).
Существует ряд параметров AutocompleteMode , и мы используем OneTermWithContext. Дополнительные параметры см. в API автозаполнения .
Запустите приложение. Обратите внимание, что диапазон параметров, отображаемых в раскрывающемся списке, является одним словом. Попробуйте вводить слова, начинающиеся с "re". Обратите внимание, что количество параметров уменьшается по мере ввода дополнительных букв.
На данный момент скрипт предложений, который вы запустили ранее, вероятно, более полезен, чем этот скрипт автозаполнения. Чтобы сделать автозавершение более понятным для пользователя, рассмотрите возможность использования его с предлагаемыми результатами.
Объединение автодополнения и подсказок
Объединение автозаполнения и предложений является самым сложным из наших вариантов, и, вероятно, обеспечивает лучший пользовательский интерфейс. Мы хотим, чтобы при вводе текста отображался первый вариант автозаполнения из Azure Cognitive Search. Кроме того, мы хотим получить ряд предложений в виде раскрывающегося списка.
Существуют библиотеки, которые предлагают эту функциональность, часто называемую "встроенное автозаполнение" или аналогичным именем. Тем не менее, мы собираемся реализовать эту функцию в собственном коде, чтобы вы могли изучить API. Сначала мы начнем работу с контроллером в этом примере.
Добавьте действие в контроллер, который возвращает только один результат автозаполнения, а также указанное количество предложений. Мы вызовем это действие 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); }Один вариант автозаполнения возвращается в верхней части списка результатов, затем следуют все предложения.
В представлении сначала мы реализуем метод, чтобы светло-серое слово автозаполнения отображалось прямо под более жирным текстом, вводимым пользователем. 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 в этом случае.
Кроме того, в представлении введите следующий скрипт после всех введенных скриптов. Скрипт является длительным и сложным из-за различных действий ввода, которые он обрабатывает.
<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>Обратите внимание, что функция interval используется для очистки базового текста, когда он больше не соответствует тому, что пользователь вводит, а также для задания одного и того же регистра (верхнего или нижнего) во время ввода пользователем (например, "pa" соответствует "PA", "pA", "Pa" при поиске), чтобы наложенный текст выглядел аккуратно.
Прочитайте комментарии в скрипте, чтобы получить более полное представление.
Наконец, необходимо внести незначительные изменения в два класса HTML, чтобы сделать их прозрачными. Добавьте следующую строку в классы searchBoxForm и searchBox в файл hotels.css.
background: rgba(0,0,0,0);Теперь запустите приложение. Введите "pa" в поле поиска. Вы получаете "дворец" как предложение автозаполнения, а также два отеля, которые содержат "pa"?
Попробуйте нажать клавишу табуляции, чтобы принять предложение автозаполнения, и попробуйте выбрать предложения с помощью клавиш со стрелками и клавиши табуляции, а затем повторите попытку, используя мышь и один щелчок. Убедитесь, что скрипт обрабатывает все эти ситуации аккуратно.
Возможно, вы решите, что проще просто использовать библиотеку, которая предлагает эту функцию для вас, но теперь вы знаете по крайней мере один способ, чтобы встроенное автозаполнение работало.
Общие выводы
Рассмотрим следующие выносы из этого проекта:
- Автозаполнение (также известное как "режим предугадывания") и предложения позволяют пользователю вводить только несколько символов, чтобы найти именно то, что они хотят.
- Автоматическое заполнение и предложения, которые работают вместе, могут обеспечить широкий пользовательский интерфейс.
- Всегда тестируйте функции автозаполнения со всеми формами входных данных.
- Использование функции setInterval может оказаться полезным при проверке и исправлении элементов пользовательского интерфейса.
Дальнейшие шаги
В следующем руководстве мы рассмотрим другой способ улучшения взаимодействия с пользователем, используя фасеты для сужения поиска одним кликом.