Categories
Bloggers
Blogs RSS feed

Sorting the Search Results

by Vesselin Vasilev
Scenario

I want to be be able to give certain search results higher precedence than others in the full list of results. Think of it as putting the sponsored / featured search results on top of the others. 
Here is my particular scenario: I have lots of news items. I want to:

1. Order the news by a custom field named Featured in the news list

2. Order the search results so that the featured news are always on top.

The first requirement is very easy to achieve - edit the News widget and go to Advanced mode then ControlDefinition > Views > NewsFrontendList and find the SortExpression property. By default it's set to PublicationDate DESC which puts the newest items on top. Just change the value to Featured DESC and the news list will be sorted / ordered by the Featured custom field.
Figure: sorting the news items by custom field

And here is how the news list looks like (note the featured items are on top):


Figure: the news list is sorted by a custom field

The second requirement is not that easy, because the SearchResults widget does not have a SortExpression property. As a result, the search results are not ordered:


Figure: the search results are not sorted by default

Fortunately, the Search service has a search method that can accept an orderBy parameter and we are going to take advantage of it:

IResultSet Search(string catalogueName, string query, string[] highlightedFields, int skip, int take, string[] orderBy, params object[] additionalParams);

To do that, we are going to extend the SearchResults widget by adding a SortExpression property which will control the order of the search results and override a few methods. 

public class CustomSearchResults : SearchResults
{      
        public string SortExpression { get; set; }
  
...
}

When you register the widget in the toolbox (best done by Sitefinity Thunder) the SortExpression property will automatically appear in the control designer of our CustomSearchResults widget (in Edit mode) and the content authors will be able to set it to the value they want.  

Now to the real work - we need to override a few methods and interfaces of the SearchResults class (it's not that scary as it sounds), but the most important is the Search method override. The complete code is below:

public class CustomSearchResults : SearchResults
{       
    public string SortExpression { get; set; }
 
    protected override SearchResults.ISearcher GetSearcher()
    {
            return new MyOrderedSearcher(this);
    }
 
    private class MyOrderedSearcher : MySearcher
    {
        public MyOrderedSearcher(CustomSearchResults control)
            : base(control)
        {
        }
 
        public override IEnumerable<IDocument> Search(string query, string catalogue, int skip, int take, out int hitCount)
        {
            CustomSearchResults searchResult = this.control;
            ISearchService searchService = searchResult.GetSearchService();
            string str = searchResult.BuildSearchQuery(query, searchService);
 
            var sortExpression = string.IsNullOrEmpty(searchResult.SortExpression) ? null : new[] { searchResult.SortExpression };
            IResultSet resultSets = searchService.Search(catalogue,                       str,                                     searchResult.HighlightedFields,         skip,                                   take,                                 sortExpression,                         new object[0]);
            hitCount = resultSets.HitCount;
            return resultSets.SetContentLinks();
        }
    }
 
    private abstract class MySearcher : CustomSearchResults.ISearcher
    {
        protected CustomSearchResults control;
 
        public MySearcher(CustomSearchResults control)           
        {
            this.control = control;
        }
 
        public abstract IEnumerable<IDocument> Search(string query, string catalogue, int skip, int take, out int hitCount);
    }       
}

and the necessary using statements:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Model;
using Telerik.Sitefinity.DynamicModules;
using Telerik.Sitefinity.Data.Linq.Dynamic;
using Telerik.Sitefinity.DynamicModules.Model;
using Telerik.Sitefinity.GenericContent.Model;
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.Security;
using Telerik.Sitefinity.Lifecycle;
using Telerik.Sitefinity.Services.Search.Web.UI.Public;
using Telerik.Sitefinity.Services.Search.Data;
using System.Collections;
using Telerik.Sitefinity.Services.Search;

What this code does is, if the SortExpression property is empty then we pass the null value to the Search method (no sorting), otherwise we pass the value of the property (which is our sort expression). Here is how it looks like in the backend:


Figure: the custom search results widget with SortExpression property

Just a couple of administrative tasks are left to be done:

0. Register the widget in the toolbox so that you can drag and drop it on a page.
1. Edit your Search Index catalogue and add the custom field into the "Additional fields for indexing" so that the Lucene search provider knows about your field:


Figure: adding the custom field to the search catalogue

2. Reindex the search catalogue.

And you are ready to go. Let's see it in action:


Figure: the sorted search results

This approach can be applied not only to built-in content types (news, blogs, etc.), but also to any custom content types created by the Module Builder.

Here is the full code of the custom search results widget.


3 comments

Leave a comment
  1. Tim Feb 20, 2013

    Nice post Vesselin :)

    Now how do we add an image to each search result item, taken from an image field on the dynamic module? :)

  2. Veselin Feb 20, 2013
  3. Bert Dec 11, 2013
    Hi, I'm doing something very similar (calling the searchService with a string array as the orderBy parameter. In my case the parameter is: 

    new string[] { "Title" }

    When not supplying the parameter, my search results are OK, but not sorted, obviously. When I do supply the parameter (as subscribed above), the search results are always empty: the ResultSet HitCount is 0, and there are no items in the list.

    Any idea what could be going wrong?

    Leave a comment