This post shows how to implement an autocomplete in an ASP.NET Core Razor Page using Azure Cognitive Search Suggesters.
Code: https://github.com/damienbod/AspNetCoreAzureSearch
Posts in this series
- Implement a full text search using Azure Cognitive Search in ASP.NET Core
- Using Azure Cognitive Search Suggesters in ASP.NET Core and Autocomplete
Create the index with the Suggester
To use Suggesters in Azure Cognitive Search, the index requires a suggester definition. This can be implemented using the Azure.Search.Documents Nuget package. A new SearchIndex can be created and the Suggesters list can be used to add a new suggester. The name of the suggester is required so that it can be used in the search and the fields of the search index which are to be used in the suggester. The suggester fields must exist in the index.
public async Task CreateIndex() { FieldBuilder bulder = new FieldBuilder(); var definition = new SearchIndex( _index, bulder.Build(typeof(PersonCity))); definition.Suggesters.Add( new SearchSuggester( "personSg", new string[] { "Name", "FamilyName", "Info", "CityCountry" } )); await _searchIndexClient.CreateIndexAsync(definition) .ConfigureAwait(false); }
Searching the index using the Suggester
The SearchClient is used to send the search requests to Azure Cognitive Search. The Suggest method prepares the SuggestOptions options and uses the Azure.Search.Documents SuggestAsync method of the SearchClient instance to query the index.
using Azure; using Azure.Search.Documents; using Azure.Search.Documents.Models; using Microsoft.Extensions.Configuration; using System; using System.Threading.Tasks; namespace AspNetCoreAzureSearch { public class SearchProviderAutoComplete { private readonly SearchClient _searchClient; private readonly string _index; public SearchProviderAutoComplete(IConfiguration configuration) { _index = configuration["PersonCitiesIndexName"]; Uri serviceEndpoint = new Uri(configuration["PersonCitiesSearchUri"]); AzureKeyCredential credential = new AzureKeyCredential(configuration["PersonCitiesSearchApiKey"]); _searchClient = new SearchClient(serviceEndpoint, _index, credential); } public async Task<SuggestResults<PersonCity>> Suggest( bool highlights, bool fuzzy, string term) { SuggestOptions sp = new SuggestOptions() { UseFuzzyMatching = fuzzy, Size = 5, }; sp.Select.Add("Id"); sp.Select.Add("Name"); sp.Select.Add("FamilyName"); sp.Select.Add("Info"); sp.Select.Add("CityCountry"); sp.Select.Add("Web"); if (highlights) { sp.HighlightPreTag = "<b>"; sp.HighlightPostTag = "</b>"; } var suggestResults = await _searchClient.SuggestAsync<PersonCity>(term, "personSg", sp) .ConfigureAwait(false); return suggestResults.Value; } } }
Autocomplete in ASP.NET Razor Page
The OnGetAutoCompleteSuggest method of the Razor Page sends a suggest search request using no highlighting and a fuzzy search with the required term. If you were using an Azure Cognitive Search AuthComplete together with the suggester, then maybe the fuzzy could be set to false.
using Azure.Search.Documents.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Threading.Tasks; namespace AspNetCoreAzureSearch.Pages { public class SearchAutoCompleteModel : PageModel { private readonly SearchProviderAutoComplete _searchProviderAutoComplete; private readonly ILogger<IndexModel> _logger; public string SearchText { get; set; } public SuggestResults<PersonCity> PersonCities; public SearchAutoCompleteModel(SearchProviderAutoComplete searchProviderAutoComplete, ILogger<IndexModel> logger) { _searchProviderAutoComplete = searchProviderAutoComplete; _logger = logger; } public void OnGet() { } public async Task<ActionResult> OnGetAutoCompleteSuggest(string term) { PersonCities = await _searchProviderAutoComplete.Suggest(false, true, term); SearchText = term; return new JsonResult(PersonCities.Results); } } }
The razor page view sends the ajax request using the jQuery autocomplete and processes the result. The data is displayed in a bootstrap 4 card.
@page "{handler?}" @model SearchAutoCompleteModel @{ ViewData["Title"] = "Auto complete suggester"; } <fieldset class="form"> <legend>Search for a person in the search engine</legend> <table width="500"> <tr> <th></th> </tr> <tr> <td> <input class="form-control" id="autocomplete" type="text" style="width:500px" /> </td> </tr> </table> </fieldset> <br /> <div class="card"> <h5 class="card-header"> <span id="docName"></span> <span id="docFamilyName"></span> </h5> <div class="card-body"> <p class="card-text"><span id="docInfo"></span></p> <p class="card-text"><span id="docCityCountry"></span></p> <p class="card-text"><span id="docWeb"></span></p> </div> </div> @section scripts { <script type="text/javascript"> var items; $(document).ready(function () { $("input#autocomplete").autocomplete({ source: function (request, response) { $.ajax({ url: "SearchAutoComplete/AutoCompleteSuggest", dataType: "json", data: { term: request.term, }, success: function (data) { var itemArray = new Array(); for (i = 0; i < data.length; i++) { itemArray[i] = { label: data[i].document.name + " " + data[i].document.familyName, value: data[i].document.name + " " + data[i].document.familyName, data: data[i] } } console.log(itemArray); response(itemArray); }, error: function (data, type) { console.log(type); } }); }, select: function (event, ui) { $("#docNameId").text(ui.item.data.id); $("#docName").text(ui.item.data.document.name); $("#docFamilyName").text(ui.item.data.document.familyName); $("#docInfo").text(ui.item.data.document.info); $("#docCityCountry").text(ui.item.data.document.cityCountry); $("#docWeb").text(ui.item.data.document.web); console.log(ui.item); } }); }); </script> }
The required npm packages are loaded using npm and bundled using the bundle Config. The javascript autocomplete is part of the jquery-ui-dist npm package. Or course if you implement a Vue.JS, React or Angular UI, you would use a different autocomplete lib.
{ "version": "1.0.0", "name": "asp.net", "private": true, "devDependencies": { "bootstrap": "4.5.3", "jquery": "3.5.1", "jquery-ui-dist": "^1.12.1", "jquery-validation": "1.19.2", "jquery-validation-unobtrusive": "3.2.11", "jquery-ajax-unobtrusive": "3.2.6", "popper.js": "^1.16.1" }, "dependencies": { } }
When the application is started, the search runs as required.
This could also be implemented using the word autocomplete together with the search but returning the results in a table view or something similar.
Links
https://docs.microsoft.com/en-us/rest/api/searchservice/autocomplete
https://docs.microsoft.com/en-us/azure/search
https://docs.microsoft.com/en-us/azure/search/search-what-is-azure-search
https://docs.microsoft.com/en-us/rest/api/searchservice/
https://github.com/Azure-Samples/azure-search-dotnet-samples/
https://channel9.msdn.com/Shows/AI-Show/Azure-Cognitive-Search-Deep-Dive-with-Debug-Sessions
https://channel9.msdn.com/Shows/AI-Show/Azure-Cognitive-Search-Whats-new-in-security