+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Extending Sitefinity search functionality - Autocomplete

by Boyan Barnev

NOTE: The following blog post has been updated for compatibility with the latest Sitefinity versions. 

Autocomplete functionality has been one of the most useful things implemented on a search box. Not only the displayed suggestions can speed up your search by simply clicking on one of them instead of typing the full term, but also they can give you a really handy "on the fly search for search terms" while typing, and give you a better search term hint.

There are several factors that need to be taken into account when making your own autocomplete functionality:

  • Speed - an autocomplete that returns the suggestions after you've finished typing the whole word is practically useless, so the call for retrieving the suggestions should return the results quickly
  • Relevance - the returned results should be among the most frequent matches in the search index
  • Styling and extensibility - the returned results should look like a natural part of the search box, and not interfere with the page overall design;  the autocomplete functionality should be easily configurable by the end user

Most autocomplete samples that you'll find rely on a predefined array of data to pull suggestions from, which is not the best option for our use case scenario, that's why we've taken a slightly different approach. Since Sitefinity is using the Lucene engine for it search indexing functionality we can take advantage of the generated search index  and search for keyword suggestions on the fly making an AJAX call to a WebService that will pull the data for us. The service consists of a single method that does all the work for us:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class SearchAutoComplete : ISearchCompleteService
    {
        public string[] GetKeywords(string catalogueName, string term)
        {
            //Adding * allows wildcard search. For more info check http://www.lucenetutorial.com/lucene-query-syntax.html
            var searchTerm = term + '*';
            //these are the Fields we'll search in
            var searchFields = new string[] { "Title" };
            //get the Sitefinity Search service
            var searchService = Telerik.Sitefinity.Services.ServiceBus.ResolveService<ISearchService>();
            //Build the query
            var compiledQuery = searchService.BuildQuery(searchTerm, searchFields, SystemManager.CurrentContext.AppSettings.Multilingual);
            //Get the search results
            var result = searchService.Search(catalogueName, compiledQuery, null, 0, 10, null);
            var strResult = result.Select(res => res.GetValue("Title"));
 
            return strResult.ToArray();
        }
 

 

We're passing as parameters the user typed string (the text currently being entered in the search box) and the name of the search index specified for this search box. 
We then resolve the Sitefinity Search service,and build the search query.
As we're interested in pulling just the Title of the items, the specified search field include only "Title". What's interesting is the way Lucene allows us to do wildcard searches, which is very useful in the current case as we can simply search for "Sitef*" which will return all results in the search index whose title starts with "Sitef".
In the call to searchService.Search the returned results are sorted by default by relevance, and we're taking the top 10 results.

For the frontend representation of the autocomplete suggestions we'll be using jQuery UI autocomplete plugin which offers a convenient solution to several problems you might have to deal with if implementing your own autocomplete suggestions visualization, namely:

  • How many symbols do we allow the user to type before calling the service for the first time - an option  minLength can be specified
  • How to call your service upon text changed and then display the returned results
  • Clicking on a result sets its value in the search box automatically

If you want to implement this from scratch maybe the easiest approach would be to define a <div> element which you'll populate with the returned keywords and then add them to a list, each item having an onClick implementation that would set its value as a value in the search box.

Here's how you can very easily accomplish this with jQueryUI autocomplete on the default Sitefinity SearchBox template:

<%@ Control Language="C#" %>
<%@ Register TagPrefix="sitefinity" Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" %>
 
<sitefinity:ResourceLinks ID="resourcesLinks" runat="server">
    <sitefinity:ResourceFile JavaScriptLibrary="JQuery" />
    <sitefinity:ResourceFile JavaScriptLibrary="JQueryUI" />
</sitefinity:ResourceLinks>
 
<%--remove the below style if you want to have the loading icon in the searchbox--%>
<style type="text/css">
    .ui-autocomplete-loading {
        background: white right center no-repeat !important;
    }
</style>
 
 
<fieldset id="main" class="sfsearchBox" runat="server">
    <asp:TextBox ID="searchTextBox" runat="server" CssClass="sfsearchTxt" />
    <asp:Button ID="searchButton" runat="server" Text="<%$Resources:SearchResources, Search %>" OnClientClick="return false;" CssClass="sfsearchSubmit" />
    <asp:HiddenField runat="server" ID="searchIndexName" />
</fieldset>
 
<script type="text/javascript">
    $(document).ready(function () {
        var id = "#" + "<%= searchTextBox.ClientID %>";
        var indexId = "#" + "<%= searchIndexName.ClientID %>";
        var indexName = $(indexId).val();
        var url = "/Sitefinity/Public/Services/SearchCompletion/AutoCompleteService.svc/GetKeywords?catalogueName=" + indexName;
        $(id).autocomplete({
            minLength: 2,
            source: url
        });
    });
</script>

 

If you prefer to implement your own styling for the autocomplete suggestions you don't need to load the default ones, or simply override some of them like shown in the above sample. Please make sure to specify the correct path to where your service is hosted, in our case this is under /Sitefinity/Public/Services/SearchCompletion/ folder. For your convenience I'll be attaching the service hosting to the sample as well.

Since we need to pass the search index name to the service, we have added a HiddenField control above, which we'll be setting in a custom implementation of the SearchBox widget, inheriting from the default one like this:

public class SearchBoxCustom:SearchBox
    {
        public override string LayoutTemplatePath
        {
            get
            {
                return layoutTemplatePath;
            }
            set
            {
                base.LayoutTemplatePath = value;
            }
        }
 
        protected virtual HiddenField SearchIndexName
        {
            get
            {
                return this.Container.GetControl<HiddenField>("searchIndexName", true);
            }
        }
 
        protected override void InitializeControls(Telerik.Sitefinity.Web.UI.GenericContainer container)
        {
            base.InitializeControls(container);
            this.SearchIndexName.Value = IndexCatalogue;
        }
 
        public static readonly string layoutTemplatePath = "~/Controls/Lucene_AutoComplete/SearchBoxTemplate.ascx";
    }

The only other thing worth noting is the specified layoutTemplatePath which you can either change to reflect your folder structure where you've placed the modified template or use Virtual path Provider if you'll be using it as an embedded resource.

Please find attached an archive of the sample folder - you can simply extract it in your SitefinityWebApp solution, add the service hosting under the /Sitefinity/Public/Services folder and adjust any paths wherever necessary (if you'll be placing the template or service in a different folder). Then simply build the project and register the SearchResultsCustom custom control in your PageControls toolbox - you'll be able to use it as any other widget on any Sitefinity page. Here's a short demonstrative video on setting this functionality up, and using the modified SearchBox widget with the autocomplete functionality.

 

Attachments:  SF_Search_Autocomplete

20 comments

Leave a comment
  1. KingKong Feb 27, 2012
    Thanks for the great example. I will implement it right now.
  2. Gunnar Snellman May 11, 2012
    Hi, great example. Implementation went smooth in a SF 5.2800.0 environment. Thanks again.
    Rgds/Gunnar
  3. Sitefinity Developer May 16, 2012
    Hello,
    Thanks for sharing the code snippet, Unfortunatly this functionality is not working on my machine. I have followed the same steps as mentioned in forum and attached video. I guess something is wrong with "AutoCompleteService.svc" file. Is there any additional setting which I have to add?
    Please confirm.
  4. Sitefinity Developer May 16, 2012
    Hello,
    Thanks for sharing the code snippet, Unfortunatly this functionality is not working on my machine. I have followed the same steps as mentioned in forum and attached video. I guess something is wrong with "AutoCompleteService.svc" file. Is there any additional setting which I have to add?
    Please confirm.
  5. Gunnar May 16, 2012

     Hi All, during build I receive the following warning message:

     

    ------ Build started: Project: SitefinityWebApp, Configuration: Debug Any CPU ------

    C:\_B2M_CMS_05.00.00_SiteFinity\WebProjects\CMS5_SiteDevelopment_SF-v5.0\Controls\Lucene_AutoComplete\SearchAutoComplete.cs(20,23): warning CS0618: 'Telerik.Sitefinity.Utilities.Lucene.Net.Store.FSDirectory.GetDirectory(string)' is obsolete: 'Use Open(File)'

    Compile complete -- 0 errors, 1 warnings

    SitefinityWebApp -> C:\_B2M_CMS_05.00.00_SiteFinity\WebProjects\CMS5_SiteDevelopment_SF-v5.0\bin\SitefinityWebApp.dll

    ========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

    Is this normal ? Does it really work ? I experience that the autocomplete function in this implementation is very random, Sometimes it works sometimes it just does not show any words even though the setup is correct. Do somebody else experience this behaviour ?
    Rgds/Gunnar

     

  6. Sitefinity Dev May 17, 2012
    Hello Gunnar,
    Can you please share your code. So that I can look into this.
  7. Sitefinity Dev May 17, 2012
    Hello Gunnar,
    Can you please share your code. So that I can look into this.
  8. Gunnar Oct 26, 2012
    I installed this nice little widget on SF 5.2.3700 and everything went fine apart from the warning message I am still getting:

    ------ Build started: Project: SitefinityWebApp, Configuration: Debug Any CPU ------
    C:\_B2M_CMS5_Telerik-SiteFinity\v52\CMS5-Development (Port 49252)\Controls\Lucene_AutoComplete\SearchAutoComplete.cs(20,23): warning CS0618: 'Telerik.Sitefinity.Utilities.Lucene.Net.Store.FSDirectory.GetDirectory(string)' is obsolete: 'Use Open(File)'

    Compile complete -- 0 errors, 1 warnings
    SitefinityWebApp -> C:\_B2M_CMS5_Telerik-SiteFinity\v52\CMS5-Development (Port 49252)\bin\SitefinityWebApp.dll
    ========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

    I know we have been through this before but I never received a solution for this little issue.

    Rgds/Gunnar
  9. Gunnar Oct 26, 2012
    Hi all, installed the widget in SF 5.2.3700 and ran into the same problem as earlier:

    ------ Build started: Project: SitefinityWebApp, Configuration: Debug Any CPU ------
    C:\_B2M_CMS5_Telerik-SiteFinity\v52\CMS5-Development (Port 49252)\Controls\Lucene_AutoComplete\SearchAutoComplete.cs(20,23): warning CS0618: 'Telerik.Sitefinity.Utilities.Lucene.Net.Store.FSDirectory.GetDirectory(string)' is obsolete: 'Use Open(File)'

    Compile complete -- 0 errors, 1 warnings
    SitefinityWebApp -> C:\_B2M_CMS5_Telerik-SiteFinity\v52\CMS5-Development (Port 49252)\bin\SitefinityWebApp.dll
    ========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

    anyone have an idea on how we can solve this ?

    Rgds/Gunnar
  10. pp Jun 14, 2013
    I have same error,too
  11. Emma Jun 18, 2013
    The Auto completion doesn't work for me :( When I use a web development tool, I got this error "TypeError:$(...).autocomplete is not a function"

    Is there something I'm missing with AutoCompleteService.svc?


  12. Emma Jun 18, 2013
    The Auto completion doesn't work for me :( When I use a web development tool, I got this error "TypeError:$(...).autocomplete is not a function"

    Is there something I'm missing with AutoCompleteService.svc?


  13. Emma Jun 19, 2013
    The Auto completion doesn't work for me :( When I use a web development tool, I got this error "TypeError:$(...).autocomplete is not a function"

    Is there something I'm missing with AutoCompleteService.svc?


  14. Sean Molam Sep 05, 2013
    If you are getting an Autocomplete with the newer versions of Sitefinity than this was written for make sure that you are using the latest version of jqueryui that ships with Sitefinity.  I used JustDecompile to look in the Sitefinity.Resources.dll to find that the latest jquery UI in use is instead referenced with:

    <sitefinity:ResourceFile Name="Telerik.Sitefinity.Resources.Scripts.jquery-ui-1.9.2.custom.min.js" Static="True" />

    This is for Sitefinity 6.1.
  15. Ramesh Jun 10, 2014
    Thanks very much. I am going to try this now.
  16. Ramesh Jun 11, 2014
    Worked very well. Thanks again.
  17. Jason Robertson Jul 17, 2014
    Nice love your work
  18. Madona Jun 02, 2015

    This is very helpful!

    Thanks!!!

  19. Thiru Oct 24, 2015

    Getting error on 

    var compiledQuery = searchService.BuildQuery(searchTerm, searchFields, SystemManager.CurrentContext.AppSettings.Multilingual);

    Telerik.Sitefinity.Services.Search.ISearchService does not contain a definition for 'BuildQuery' and no extension method 'BuildQuery' accepting a first argument of type 'Telerik.Sitefinity.Services.Search.ISearchService' could be found (are you missing a using directive or an assembly reference?)

  20. Joseph Dec 02, 2015
    Getting the same issue as Thiru. But I doubt we'll be getting support

    Leave a comment