#OpenToWorkOpen to new opportunities: Technical Lead or DevRel let's chat and
explore possibilities!
Contact Elio
AlertThis post is over a year old, some of this information may be out of date.
Part 7: Working with Ranges in the Search Refiner Control
December 10, 2013
This is an extra post for the search refiner control series. In this post I’ll explain how you could build a search refiner control that makes use of ranges.
The range operator can be used for the following search data types:
DateTime
Decimal
Integer
The out-of-the-box refiner controls use this range operator for its slider controls, but it can easily be used within your custom refiner.
First let me explain the range operator.
Range operator
The range operator requires minimal two values, a start and an end value (Range Operator).
range(start, end)
It’s also possible to apply two optional parameters, an open and close start or end interval.
With these optional start and end interval parameters, you could specify to include or exclude the start / end values.
From
GE: Greater than or equal to the start value; This includes the start value (Default);
GT: Greater than the start value. This excludes the start value.
To
LE: Less than or equal to the end value; This includes the end value (Default);
LT: Less than the end value. This excludes the end value.
The range operator can be used with numeric values (integer and decimal) and DateTime values. There are two important things were you should pay attention for:
When using the range operator for DateTime refinement, you should format the dates in ISO format “2013-11-01T00:01:01Z”.
When using the range operator for Decimal refinement, you should let the range operator know that it’s a decimal value. This can be done by using the decimal operator “decimal(10.10)”.
Another useful thing, if you don’t want to specify a lower or upper bound, you could use the min or max reserved words as value.
Examples
Integer
1
2
3
4
5
6
7
8
9
10
// Results with the upper bound set to 94
range(min,94)// Results between 20 and 120 (20 and 120 included)
range(20,120)// Results starting from 20 with no upper bound
range(20,max)// Results greater than 20
range(20,max,from="GT")// Results greater than 20 and less than 120
range(20,120,from="GT",to="LT")
Decimal
1
2
3
4
5
6
// Results with the upper bound set to 100.21
range(min,100.21)// Results between 21.21 and 100.21 (21.21 and 100.21 included)
range(21.21,100.21)// Results starting from 100.21 with no upper bound
range(100.21,max)
DateTime
1
2
3
4
5
6
// Results greater than or equal to November 1st
range(2013-11-01T00:01:01Z,max)// Results less than or equal to December 2nd
range(min,2013-12-02T00:01:01Z)// Results from November 1st, until December 2nd
range(2013-11-01T00:01:01Z,2013-12-02T00:01:01Z)
Creating Your Own Range Search Refiner Control
Now that you got a little bit of background information about how the range operator works, it’s time to build our own range search refinement control.
The end result will look like this:
Range End Result
For this search refiner we’ll start from scratch, because the code will be much different than the ones that were created in the previous posts.
<htmlxmlns:mso="urn:schemas-microsoft-com:office:office"xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><head><title>Range Filter</title><!--[if gte mso 9]>
<xml>
<mso:CustomDocumentProperties>
<mso:CompatibleManagedProperties msdt:dt="string"></mso:CompatibleManagedProperties>
<mso:TemplateHidden msdt:dt="string">0</mso:TemplateHidden>
<mso:CompatibleSearchDataTypes msdt:dt="string">;#DateTime;#Integer;#Decimal;#</mso:CompatibleSearchDataTypes>
<mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription>
<mso:ContentTypeId msdt:dt="string">0x0101002039C03B61C64EC4A04F5361F385106604</mso:ContentTypeId>
<mso:TargetControlType msdt:dt="string">;#Refinement;#</mso:TargetControlType>
<mso:HtmlDesignAssociated msdt:dt="string">1</mso:HtmlDesignAssociated>
<mso:CrawlerXSLFile msdt:dt="string"></mso:CrawlerXSLFile>
<mso:HtmlDesignPreviewUrl msdt:dt="string"></mso:HtmlDesignPreviewUrl>
</xml>
<![endif]--><script></script></head><body><divid="RangeFilter"><!--#_
var listData = ctx.ListData;
var hasControl = true;
// Check if the current Refinement Control exists
if ($isNull(ctx.RefinementControl) '' $isNull(ctx.ClientControl)) hasControl = false;
if (hasControl) {
// Check if listdata contains data
if(!$isNull(listData) && !$isEmptyArray(listData)) {
// ALL CODE WILL BE WRITTEN HERE
}
}
_#--></div></body></html>
In this starter’s template you’ll find the CompatibleSearchDataTypes property, this property specifies for which kind of search data types that the control can be used. In this control the property is set to be used for DateTime, Integer, and Decimal search data types.
Next step is to create the ID variables for the elements that will be used, and the default refinement control classes.
1
2
3
4
5
6
7
8
9
10
11
12
13
// Element IDs
varcontrolID=ctx.RefinementControl.containerId+"_"+ctx.RefinementControl.propertyName;varfromInput=controlID+"_from";vartoInput=controlID+"_to";varrefineLink=controlID+"_refine";varclearLink=controlID+"_clear";// Show the refinement title
varisExpanded=Srch.Refinement.getExpanded(ctx.RefinementControl.propertyName);variconClass=(isExpanded=="true"?"ms-ref-uparrow":"ms-ref-downarrow");varrefinerCatTitle=Srch.Refinement.getRefinementTitle(ctx.RefinementControl);// Display style > needed to hide the refinement list when collapsed
vardisplayStyle=(isExpanded=="true"?"":"none");
Now that the variables are created, you can add the HTML mark-up for the refiner. This looks like this:
The output after this will look the same as the end result, but without the functionality.
Range Refiner
Adding the Click Events to the Hyperlinks
For the refinement and clear hyperlinks, we’ll add click events via JavaScript. This needs to be done from the moment the search refinement control is loaded. This can be done by using the ctx.OnPostRender functionality.
ctx.OnPostRender=[];ctx.OnPostRender.push(function(){// Retrieve the two hyperlinks
varrefineLinkElm=document.getElementById(refineLink);varclearLinkElm=document.getElementById(clearLink);// Create an onclick event for the refinement link
refineLinkElm.onclick=function(){// Retrieve the two input fields
varfromInputElm=document.getElementById(fromInput);vartoInputElm=document.getElementById(toInput);// Check if the two input fields contains a value, otherwise nothing needs to be done
if(fromInputElm.value.trim()!==""''toInputElm.value.trim()!==""){// Retrieve the input values
varfromValue=fromInputElm.value.trim()===""?"min":fromInputElm.value.trim();vartoValue=toInputElm.value.trim()===""?"max":toInputElm.value.trim();// Refinement value
if(ctx.ListData[0].filterTokenType==="decimal"){fromValue=fromValue==="min"?fromValue:'decimal('+fromValue+')';toValue=toValue==="max"?toValue:'decimal('+toValue+')';}// Create the refinement JSON value string
varrefinement='{"'+ctx.RefinementControl.propertyName+'":["range('+fromValue+','+toValue+')"]}';$getClientControl(this).updateRefinersJSON(refinement);}};// Create a onclick event to remove the refinement
clearLinkElm.onclick=function(){// NULL Refinement value to remove the refinement
varrefinement='{"'+ctx.RefinementControl.propertyName+'":null}';$getClientControl(this).updateRefinersJSON(refinement);};});
Showing the Refiner Values in the Input Fields
The last step is to show the used refiner values in the input fields after you refined the results. Right now when you do a refinement, the input fields will get cleared.
To add this kind of functionality, we’ll have to retrieve these values from the current refinement category that is in place. This can be done the same way as explained in the previous post (multi-value search refiner control).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Set input form values
varfrom="";varto="";varcurrentRefinementCategory=ctx.ClientControl.getCurrentRefinementCategory(ctx.RefinementControl.propertyName);// Check if the current control refinement is in use
if(!Srch.U.n(currentRefinementCategory)&¤tRefinementCategory.get_tokenCount()==1){// Parse the current filter token to find the current range condition
varmatchResults=Srch.U.parseTypedRangeToken(currentRefinementCategory.t[0],null);// check for a range token match with corresponding min and max
if(matchResults!=null&&matchResults.length>2){varfrom=matchResults[1]==="min"?"":matchResults[1];varto=matchResults[2]==="max"?"":matchResults[2];}}
This code needs to be added just before the HTML mark-up. The refinement values (start and end) that it retrieves will be stored in the from and to variables, and can be used for the input field values by adding a value attribute to the fields.
1
2
3
4
<labelfor='_#= fromInput =#_'>From</label><inputtype='text'id='_#= fromInput =#_'name='from'value='_#= from =#_'/><labelfor='_#= toInput =#_'>To</label><inputtype='text'id='_#= toInput =#_'name='to'value='_#= to =#_'/>