"Between" filter on computed field in Sitecore 8

"Between" filter on computed field in Sitecore 8

Say you have the following computed field in your index configuration:

<fields hint="raw:AddComputedIndexField">
   <field fieldName="Duration">Your.Namespace.DurationComputed,Your.Assembly</field>
</fields>

And this class to map your results to:

public class MySearchResultItem 
{
    public int Duration { get; set;}    
}

The computed field returns an int, which is stored in the index.

You then might want to get only results with a duration between 5 and 10, both inclusive. First you might try something like this:

using (var context = ContentSearchManager.GetIndex("my_index").CreateSearchContext())
{
    var queryable = context.GetQueryable<MySearchResultItem>()
                           .Where(x => x.Duration >= 5 && x.Duration <= 10);

    var results = queryable.GetResults();

    // We get the wrong results
}

This will return the wrong results, as it is translated to a Lucene query along the lines of:

+duration:[* TO 5] +duration:[* TO 10]

What we want is this:

+duration:[5 TO 10]

To get that we need to use the extension method Between<T>(T from, T to, Inclusion inclusion) from the Sitecore.ContentSearch.Linq.MethodExtensions namespace in the Sitecore.ContentSearch.Linq assembly. This is also where the Like<T> and Boost<T> extension methods reside, among a few other, that you may have used before when working with Sitecore ContentSearch.

Matt Burke has made a post with a lot of useful tips for working with Sitecore ContentSearch

using (var context = ContentSearchManager.GetIndex("my_index").CreateSearchContext())
{
    var queryable = context.GetQueryable<MySearchResultItem>()
                           .Where(x => x.Duration.Between(5, 10, Inclusion.Both));

    var results = queryable.GetResults();

    // Now we get 0 results?!
}

Now we get no results at all. If you go have a look at the search log in the data folder, you will see that it has actually generated the correct Lucene query, but still we get no results.

I raised the issue with Sitecore support and apparently we need to also add the computed field to the field map and set the type to System.Int32 for this to work, when using the Between<T> method on a computed field that is not a string, like this:

<fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
  <fieldNames hint="raw:AddFieldByFieldName">
    <!-- Other fields here -->
    <field fieldName="Duration" 
           storageType="YES" 
           indexType="TOKENIZED" 
           vectorType="NO" 
           boost="1f" 
           type="System.Int32"          
           settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" />
    </fieldNames>
</fieldMap>

They didn’t explain why, but from my own debugging of some of the assemblies it looks like without the field in the field map specifying the type, the evaluation of the query or the mapping gets messed up along the way, when it doesn’t know the expected type.

Instead of getting this (correct) rewritten Lucene query debug output:

IsProhibited: False
IsRequired: True
Occur: MUST
Query Type: Lucene.Net.Search.ConstantScoreQuery
  Boost: 1
  Filter: duration:[5 TO 10]

we get this (incorrect) version:

IsProhibited: False
IsRequired: True
Occur: MUST
Query Type: Lucene.Net.Search.ConstantScoreQuery
  Boost: 1
  Filter: QueryWrapperFilter()

Anyway, you now know how to setup your index correctly, when you want to do a query filtering on a range of values.