Edit: The below blog post is supported on versions till 7.2. In case using Sitefinity 7.3+ you can refer to the documentation article here.
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.