{"id":23731,"date":"2016-01-07T12:57:39","date_gmt":"2016-01-07T20:57:39","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=23731"},"modified":"2019-04-03T15:57:49","modified_gmt":"2019-04-03T22:57:49","slug":"adding-suggestions-to-mobile-search-with-azure-search","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/adding-suggestions-to-mobile-search-with-azure-search\/","title":{"rendered":"Adding Suggestions to Mobile Search with Azure Search"},"content":{"rendered":"<p>\t\t\t\t<a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Simulator-Screen-Shot-7-Jan-2016-13.53.40.png\"><img decoding=\"async\" class=\"wp-image-23732 alignright\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Simulator-Screen-Shot-7-Jan-2016-13.53.40-576x1024.png\" alt=\"Simulator Screen Shot 7 Jan 2016, 13.53.40\" width=\"200\" height=\"356\" \/><\/a>I\u2019ve recently been developing an iOS app that leverages <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/search\/\">Microsoft\u2019s Azure Search<\/a> to provide search query suggestions to the user as they type. Implementing this feature will reduce the number of search queries that return zero results and help keep users engaged since users often misspell search terms (and thus a search returns no results) even though the item exists in the database. Users have also become accustomed to having suggestions offered to them when using popular search engines like Google or\u00a0Bing, so a product that requires the user to search but cannot offer spelling corrections or suggestions isn&#8217;t creating the best possible user experience, leading to app abandonment.<\/p>\n<p>In this blog post, I\u2019m going to detail how I implemented Azure Search into an existing iOS app and added support for hit highlighting in order to support suggestions. You&#8217;ll be surprised how easy it is to get started and just how much this simple approach can improve your users&#8217; experience with your app.<\/p>\n<h2>Getting Started<\/h2>\n<p>Our\u00a0search service must\u00a0support hit highlighting, which could be anything from Elastic Search to a bespoke implementation. In this example, we\u2019re going to use Azure Search, which provides a generous free tier supporting up to 10,000 documents within its index. First, we need to <a href=\"http:\/\/mikecodes.net\/2016\/01\/04\/fixing-query-spelling-mistakes-with-azure\/\">configure Azure Search<\/a> for our mobile app to display search suggestions.<\/p>\n<p>Once we have provisioned the backend infrastructure, add the <a href=\"https:\/\/www.nuget.org\/packages\/AzureSearchClient\/\">Microsoft Azure Search NuGet package<\/a> to the iOS project\u2019s packages. As the client SDK remains in preview, we need to ensure that we\u2019ve checked the \u2018Show pre-release packages\u2019 checkbox.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Screen-Shot-2016-01-07-at-13.46.02.png\"><img decoding=\"async\" class=\"aligncenter wp-image-23733 \" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Screen-Shot-2016-01-07-at-13.46.02.png\" alt=\"Screen Shot 2016-01-07 at 13.46.02\" width=\"615\" height=\"407\" \/><\/a><\/p>\n<h2>Configuring Azure Search<\/h2>\n<p>To connect to the configured Azure Search instance, all that&#8217;s required on the client side is to add a little boilerplate code to your search ViewController in order to utilize the Azure Search SDK. Create an instance of both the <em>SearchServiceClient<\/em> and the <em>SearchIndexClient<\/em>.<\/p>\n<pre class=\"lang:csharp decode:true\">var serviceClient = new SearchServiceClient(\"serviceName\", new SearchCredentials(Core.Helpers.Keys.AzureSearchKey));\r\nvar indexClient = serviceClient.Indexes.GetClient(\"indexName\");\r\n<\/pre>\n<h2>Implementing a Basic Suggestions UI<\/h2>\n<p>Azure Search provides a very simple API for requesting suggestions based on a search query. We&#8217;ll use this to create a list of strings which we&#8217;ll display within a UITableView.<\/p>\n<pre class=\"lang:csharp decode:true\">var response = await indexClient.Documents.SuggestAsync(searchBar.Text, \"nameSuggester\");\r\nvar results = new List();\r\nforeach(var r in response)\r\n{\r\n    results.Add(r.Text);\r\n}\r\n<\/pre>\n<p>We can then pass the results from Azure to a UITableViewSource to ensure we correctly create the UITableViewCell for display purposes. Our UITableViewSource should look something like the following:<\/p>\n<pre class=\"lang:csharp decode:true\">public class SearchSuggestionDataSource : UITableViewSource\r\n{       \r\n    List results;\r\n    public SearchSuggestionDataSource(List results)\r\n    {\r\n        this.results = results;\r\n    }\r\n\r\n    public override nint RowsInSection(UITableView tableview, nint section)\r\n    {\r\n        return results.Count;\r\n    }\r\n\r\n    public override UITableViewCell GetCell(UITableView tableView, Foundation.NSIndexPath indexPath)\r\n    {\r\n        var cellIdentifier = new NSString(\"suggestion\");\r\n        var cell = tableView.DequeueReusableCell(cellIdentifier) as NoRecentSearchesViewCell ?? new NoRecentSearchesViewCell(cellIdentifier);\r\n\r\n        var text = results[indexPath.Row];\r\n        cell.TextLabel.Text = text;\r\n        return cell;  \r\n    }\r\n}\r\n<\/pre>\n<h2>Improving the Results<\/h2>\n<p>With the current implementation, we&#8217;re able to show a list of results that\u00a0changes as the user edits the characters in the search field. However, the suggestions don&#8217;t take into account misspelled queries or let the user know why a particular result was returned.\u00a0We can easily support this by updating our search suggestion parameters:<\/p>\n<pre class=\"lang:csharp decode:true\">var suggestParameters = new SuggestParameters();\r\nsuggestParameters.UseFuzzyMatching = true;\r\nsuggestParameters.Top = 25;\r\nsuggestParameters.HighlightPreTag = \"[\";\r\nsuggestParameters.HighlightPostTag = \"]\";\r\nsuggestParameters.MinimumCoverage = 100;\r\n<\/pre>\n<p>This code asks Azure Search to enable fuzzy matching, return the top 25 matches, add tags to the matched section of a result, and cover the entire index before returning (with a huge index, these properties would result in more resource consumption and latency). We also need to add the <em>suggestParameters<\/em> variable to the SuggestAsync method arguments.<\/p>\n<pre class=\"lang:csharp decode:true\">var response = await indexClient.Documents.SuggestAsync(searchBar.Text, \"nameSuggester\", suggestParameters);\r\n<\/pre>\n<p>If we looked at an item within our results list, we would see that the pre and post tags have been appended to reflect where a match occurred. For example, if I queried with &#8216;Duv&#8217;, we&#8217;d get back [Duv]el because Azure Search found Duv within Duvel. With fuzzy matching enabled, you can often see some slightly dubious matching, but at least you&#8217;ll always know why it matched.<\/p>\n<h2>Updating our UITableViewCells<\/h2>\n<p>In order to take full advantage of the hit highlighting, we need\u00a0to remove the tags and change the style of that element of the string. We&#8217;ll accomplish this within our UITableViewSource implementation. Update the GetCell method by creating variables for the index position of the pre and post tag.<\/p>\n<pre class=\"lang:csharp decode:true\">var hitStart = text.IndexOf(\"[\");\r\nvar hitEnd = text.LastIndexOf(\"]\");\r\n\r\n\/\/Create our colours for later.\r\nvar hitColor = \"646265\".ToUIColor();\r\nvar textColor = \"989898\".ToUIColor();\r\n<\/pre>\n<p>Next, let&#8217;s use UIStringAttributes to create attributes for different parts of our string. To start, we&#8217;ll create our attributes for the default situation. This will define what our text looks if we have no matches or a particular section of the string doesn&#8217;t match.<\/p>\n<pre class=\"lang:csharp decode:true\">var defaultAttributes = new UIStringAttributes {\r\n    ForegroundColor = textColor,\r\n    Font = UIFont.FromName(\"Avenir\", 18)\r\n };\r\n<\/pre>\n<p>Then we can go ahead and create our hit attributes. These are the attributes we change to reflect that we&#8217;ve matched to a particular part of the string. In this case, we&#8217;re going to simply set the matching text to be bold and tweak the color, while the default remains unchanged.<\/p>\n<pre class=\"lang:csharp decode:true\">var hitAttributes = new UIStringAttributes {\r\n    ForegroundColor = hitColor,\r\n    Font = UIFont.FromName(\"Avenir-Medium\", 18)\r\n};\r\n<\/pre>\n<p>Now that the\u00a0string attributes are defined, we need to put them in place. Before we do this, we\u00a0need\u00a0to remove the pre and post tag. If we didn&#8217;t remove the tags, then our UITableViewCell text would still contain the square brackets we defined earlier as our tags.<\/p>\n<pre class=\"lang:csharp decode:true\">text = text.Remove(hitEnd, 1);\r\ntext = text.Remove(hitStart, 1);\r\n\r\nvar attributedString = new NSMutableAttributedString (text);\r\nattributedString.SetAttributes(defaultAttributes.Dictionary, new NSRange(0, text.Length));\r\nattributedString.SetAttributes(hitAttributes.Dictionary, new NSRange(0, hitEnd -1));\r\n\r\ncell.TextLabel.AttributedText = attributedString;\r\nreturn cell;\r\n<\/pre>\n<p>With this in place, we&#8217;re now able to clearly see where our search query matches the results. It also shows us suggestions for simple spelling mistakes as seen below.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Simulator-Screen-Shot-7-Jan-2016-14.31.001.png\"><img decoding=\"async\" class=\"aligncenter size-medium wp-image-23750\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/Simulator-Screen-Shot-7-Jan-2016-14.31.001-300x155.png\" alt=\"Simulator Screen Shot 7 Jan 2016, 14.31.00\" width=\"300\" height=\"155\" \/><\/a><\/p>\n<p>To see this live in a real app, check out this video where I used Azure Search on iOS to return beer suggestions in response to a user&#8217;s input:<\/p>\n<p><center>\n<iframe width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/vaUfh3sqmdQ\" frameborder=\"0\" allowfullscreen><\/iframe>\n<\/center><\/p>\n<p>&nbsp;<\/p>\n<h2>Wrapping Up<\/h2>\n<p>By using Azure Search\u00a0along with some tweaks to our cells, we are able to create vastly improved search experiences within our mobile apps. Although this post focuses on\u00a0the iOS implementation, the approach for implementing search suggestions in Android would be exactly the same. The key aspect of providing a great search experience is\u00a0to remember that search is about discovering and not just simple string matching. Offering suggestions makes it easy to fix user errors before they become an issue and no results are returned, making our mobile apps more pleasant to use.\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I\u2019ve recently been developing an iOS app that leverages Microsoft\u2019s Azure Search to provide search query suggestions to the user as they type. Implementing this feature will reduce the number of search queries that return zero results and help keep users engaged since users often misspell search terms (and thus a search returns no results) [&hellip;]<\/p>\n","protected":false},"author":1929,"featured_media":23733,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[4],"class_list":["post-23731","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>I\u2019ve recently been developing an iOS app that leverages Microsoft\u2019s Azure Search to provide search query suggestions to the user as they type. Implementing this feature will reduce the number of search queries that return zero results and help keep users engaged since users often misspell search terms (and thus a search returns no results) [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/23731","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/1929"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=23731"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/23731\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=23731"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=23731"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=23731"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}