Sitecore content search and LINQ (part 2)

This is part 2 of my post about Sitecore content search and LINQ. For the first part see

In the first part we set up our custom index and created a piece of code to use it. Now let’s say that we don’t only have products but also services to list on our site. We created a Sitecore template for them and want our Service items to be in the index as well. So we added our Service template ID to the index as we did before with the product template ID:


Don’t forget to regenerate your index and check with Luke the contents are there after publishing your first Service items! Sometimes you may have to restart IIS and regenerate again to get the index working. If it keeps failing clear the whole custom index folder in the Sitecore data folder and try to regenerate again.

We create a class for our service entries in our code:

using Sitecore.ContentSearch.SearchTypes;
namespace ScPredicateBuilderApp.DataObjects
   public class ServiceSearchResult:SearchResultItem
      public string ServiceName { get; set; }
      public string Description { get; set; }
      public double Rate { get; set; }

and some code to retrieve them from the index similar as we did for products but with the above class for type parameter in GetQueryable(). Of course C# generics can be very convenient to prevent a lot of duplicate code so you may want to change the SearchProducts() method of the previous part into something more generic:

/// <summary>
/// Get the index contents using search
/// </summary>
public List<T> SearchIndexContent<T>() where T : SearchResultItem
   ISearchIndex myIndex = ContentSearchManager.GetIndex("sitecore_myindex");
   using (var context = myIndex.CreateSearchContext())
      var query = context.GetQueryable<T>();
      var result = query.ToList();
      return result;

Now when calling this method pass either ProductSearchResult or ServiceSearchResult as type parameter to get a list of the corresponding type.

However… as said before Lucene entries are generic (“documents”) and Sitecore has no way of knowing what entry belongs to what type (class) you specified based on the index alone. It will try to map ALL entries to the type you passed, leaving empty properties for fields it cannot map. So the first thing we have to do when using contentsearch is add a filter on template to the Lucene query.

Adding a template filter

Where and how we pass the template ID to filter on is a matter of conventions in your working environment or personal preference. You can put a base class between SearchResultItem and your content search classes where you retrieve the template ID (and to define common properties), or pass it as a parameter when calling the above method. In this case I do the latter. So we add a parameter to the method:

public List<T> SearchIndexContent<T>(string templateId) where T : SearchResultItem

In this case I defined some configuration somewhere for my project and have the Sitecore IDs as string, passing them as such for parameter. Now we add a line in our using block and extend our query line with a filter on the template. In other words replace the “var query = ....” line with:

var tId=new ID(templateId);
var query = context.GetQueryable<T>().Filter(t=>t.TemplateId==tId);

Filter() is part of the LINQ interface Sitecore has implemented for contentsearch. There is also a Where() implementation. The difference is that Where returns a result taking Lucene’s scoring system into account whereas Filter just returns the result set. Since we don’t do anything with the scoring here we use Filter.

As with any LINQ implementation on a datasource you have to assign external parameters to a local variable before entering them into a LINQ expression to avoid so-called “modified closure” issues. These issues can cause nasty bugs that won’t give you a warning or error but can return incorrect results. Also, if the parameter in the Filter() expression is the result of a function or calculation you have to assign it first to a local variable or it will cause runtime exceptions. For example combining the above into something like:

var query = context.GetQueryable<T>().Filter(t=>t.TemplateId== new ID(templateId));

will NOT work. So always use a local variable as parameter in LINQ expressions.

You can chain LINQ methods just like any other LINQ implementation. Sitecore will take the expression and convert it to a Lucene query, returning the result as an IQueryable. Let’s say we want our products sorted on creation date, so something like:

var query = context.GetQueryable<T>().Filter(t=>t.TemplateId==tId).OrderByDescending(p=>p.CreatedDate);

will work. CreatedDate comes from a computed field that was already defined by Sitecore in our index configuration file as “__smallcreateddate”. It is one of the predefined properties on SearchResultItem.

Since our generic method takes SearchResultItem as type, we don’t have our product- or service specific properties here. However you can refer to fields directly using the Lucene field names and indexer. Let’s say we have a boolean field “Available” added to our Product template, we can do something like:

var query = context.GetQueryable<T>().Filter(t=>t.TemplateId==tId).Filter(p=>p["available"]=="1");

Note that using the Fields collection for the parameter (p=>p.Fields[“available”]) instead of the indexer uses a function get_Item() internally, causing an exception. It is one of many quirks you have to be aware of when using LINQ for contentsearch. Also you’re referring values as they are in Lucene this way, which means they’re not type-converted and thus might not give the results you expect. The above is therefore a string comparison.

Of course we now also have a problem for our ServiceSearchResult entries since they don’t have a field “available”. It results in a null value for the field and thus the above expression is always false.

Building LINQ expressions dynamically

So what if we want to have expressions depending on our item type? We could go back to making type-specific search functions, having to duplicate the template filtering expression (and probably more) in each one. Moreover, in real-world scenario’s we may not know in advance what expressions may be needed (a user may or may not select a specific search option). In short, we want to build our search expression from separate parts.

In comes Sitecore’s PredicateBuilder. This class resides in the Sitecore.ContentSearch.Linq.Utilities namespace and can be used to create and manipulate (partial) LINQ expressions for a given type. You start by either calling the True() or False() method, where you use True() for operations than need to be combined using logical “And “ and False() for logical “Or”. Then you use the And() or Or() methods for the comparison expression. The current version also has a Create<T>() method but at his point I cannot confirm it works the same as the True/And and False/Or method combinations under all circumstances.

These methods return an object of type System.Linq.Expressions.Expression. To make this all clear it is best to show an example. Say we put the expression for available products in a separate method, it will look like this:

/// <summary>
/// Return an expression for filtering on available products only
/// </summary>
/// <returns></returns>
public Expression<Func<ProductSearchResult, bool>> GetAvailableProductsExpression()
   var predicate = PredicateBuilder.True<ProductSearchResult>();    //True for "And"
   predicate = predicate.And(p => p.Available == true);
   return predicate;

Now we add a parameter of type Expression<Func<T, bool>> to our SearchIndexContent method, and pass the result of the above in when calling the search for products. For services we don’t pass in a specific expression and use a dummy expression in our SearchIndexContent method. It now looks like this:

/// <summary>
/// Get the index contents using search
/// </summary>
public List<T> SearchIndexContent<T>(string templateId, Expression<Func<T, bool>> expression = null) where T : SearchResultItem
   ISearchIndex myIndex = ContentSearchManager.GetIndex(Constants.IndexName);
   using (var context = myIndex.CreateSearchContext())
      var tId = new ID(templateId);
      // Dummy if null
      var exp = expression ?? PredicateBuilder.False<T>().Or(p => true);
      var query = context.GetQueryable<T>().Filter(t => t.TemplateId == tId).Filter(exp);
      var result = query.ToList();
      return result;

So to get our products we call it with:

var products = SearchIndexContent<ProductSearchResult>(Constants.ProductTemplateId, GetAvailableProductsExpression());

And to get our services:

var services = SearchIndexContent<ServiceSearchResult>(Constants.ServiceTemplateId);

(Note for this example code I defined the IDs of the templates as strings in a static Constants class.)

Predicate expressions can be combined and nested. Instead of putting a boolean expression in the And() or Or() methods directly you can put the result of another PredicateBuilder in. This way you can logically combine “And” and “Or” expressions, create large complex expressions from smaller parts and “inject” expressions based on user filter selections for example.

Using PredicateBuilder in general

As mentioned before the resulting expression of a PredicateBuilder operation is of a .NET type and not a Sitecore type. The PredicateBuilder is not tied to Sitecore’s content search itself. As far as I know you can use it with any type that can be cast using AsQueryable(). This can be quite handy, for example to do post-search filtering using the facets from the query.getResults() method mentioned in part 1 of this article. Or to perform post-search operations on the result set that Sitecore’s LINQ parser cannot translate to a search query.

There’s a lot more to Sitecore’s content search, LINQ and the PredicateBuilder than can be covered by this article. Unfortunately Sitecore’s documentation about it is far from complete, so you’ll have to look around on the web and experiment to figure out all that’s there.

Getting the demo code

I have uploaded a sample application to GitHub at It contains a simple Visual Studio 2013 solution, and a folder containing a package you can import into Sitecore to get the templates and some items. Since Sitecore libraries are proprietary software they are not included and you have to add them to the solution yourself. You need to have a working Sitecore 8 on your environment to be able to use this code, and set the demo project to publish to it.

1 thought on “Sitecore content search and LINQ (part 2)

  1. Pingback: Sitecore content search and LINQ (part 1) | Sion-ICT blog

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s