LibrarySites.Banner

Sitecore 7 Predicate Builder

In Sitecore 7 we are really pushing LINQ as the data access layer of choice for developers. LINQ is a well known query language in the .net space, is easy to write and understand and is a flexible interface with already so much support for integration with other systems. OData, LINQPad, Breeze.js, LINQ to Object, LINQ to XML - These are all examples of tools that are LINQ enabled and ready to be integrated alongside Sitecore's new LINQ to Provider layer. In saying this, we know that LINQ is not always at your fingertips when you are writing parts of your system. For example, if we had a textbox that we wanted users to create a filter within and then have it run against our LINQ layer then you will either have to convert it to a LINQ query, use CodeDom to compile on the fly or better yet have IQueryable exposed and just allow your filter to be variables passed into the IQueryable interface to filter the results.

We have already come across a few people asking the question of "What if I just want to write a lucene query?". The easy answer is, "OK, then just make a direct refernce to Lucene.Net and use that". This will most certainly work however we really urge you to move away from this and use the LINQ abstraction layer we have provided instead. Not only has the LINQ provider strongly typed the queries, but it is more flexible than using the Lucene.Net QueryParser. Why is the LINQ provider more flexible? There are certain queries that are not supported with the Lucene.Net QueryParser without extending it, the values are all passed in as strings, and you cannot Sort, OrderBy etc in the QueryParser. In the end, the main reason is that it really goes against the whole idea of having the abstraction in the first place. The abstraction is there for you to forget about the provider as much as possible.

Great! But what if I do not want to? Well, if that is the case then we need to talk about predicates. What is the predicate? It is part of our Lambda expression. The lambda expression is a powerful shorthand that can be used to significantly increase query knowledge and abstraction from database languages. The predicate is the filter part of your expression tree that tells the system if a specific object has matched your predicate criteria. We are glad to say that we have met you with all different possibilities in Sitecore 7. Not only do we allow you to use the LINQ abstraction layer, direct Lucene.Net but we have also provided a PredicateBuilder for you. The PredicateBuilder is a way for you to build up an expression tree from string queries. For example, if you passed through a query to your code that looked like this:

Example 1: title:Something +createddate:12122001 -template:Sample

You could parse this query yourself and then use the PredicateBuilder to make it ready to be directly appended onto an IQueryable instance.

NOTE: You could actually achieve the same result as the predicate builder by using the indexers within your LINQ query e.g.

Where(i =i["title"] == "Something" && i[(ObjectIndexerKey)"createddate"] == new DateTime(12, 12, 2001) && i["template"] != "Sample");

Where would the Predicate Builder come in handy?

  • When you have have a list of ID's or Values and want to see if your index has a match on those ID's or Values i.e. "where ID is in the following list of ID's".
  • When you would like to build up a LINQ query from fields that you don't know about at compile time
  • You would like to build a string to LINQ parser from something like a lucene query to a LINQ query or from MSS syntax to LINQ queries
  • You have a foreach, for or some style of loop you will use to build up a LINQ statement and also want to control the boolean combinations that are added i.e. OR, AND, XOR
  • Your client is passing your server side code a built up query that needs to be turned into a predicate.

Example 2: You are given a Dictionary of fields and values with the following structure Dictionary<string, object>

If you need to make a LINQ query out of this, this is where the PredicateBuilder could come in handy.

Expression<Func<T, bool>> predicate = PredicateBuilder.True<T>();

foreach(var dictionaryEntry in fieldValueDictionary) 
{
   predicate = predicate.And(i => i[(ObjectIndexerKey)dictionaryEntry.Key == (object)dictionaryEntry[dictionaryEntry.Key]);
}

The predicate variable now has a 100% ready LINQ query to operate on. To break the code down, first we start the predicate with a true - PredicateBuilder.True<T>() - just to have a stub so that if there is nothing in the Dictionary, we don't return with an empty predicate due to it never entering the foreach loop. Secondly we loop over all elements and append it with either And, Or and then comes the tricky part. The value is of type object, and hence to make the compiler happy we need to make sure that we are comparing object to object. This is where the intelligent ObjectIndexerKey comes in very handy. It will allow you to have the type inferred at a later time by marking up the comparison to tell the Key that it is an object. If you are dealing with a strongly typed Dictionary such as Dictionary<string, Product> when you will not need to do this as the Product should have the proper type on the properties of the class.

So then why do we not supply a way to run Lucene or SOLR or MSS style syntax queries in the API? Our decision to not do this was actually quite easy and straight forward.

We want you to be comfortable with forgetting the underlying provider. We want you to forget which provider you are using when you are building your code.

We still want you to keep some context that this is not LINQ to SQL, it is not a complete replacement for the existing Sitecore Kernel API and that this is working against a Search Provider. A Search Provider is a completely different beast to a database. There are huge advantages of using a database but drastically different advantages to using a Search Provider. To this day there is still not one provider in my mind that does it all very well. With that in mind, we don't stop you from adding a direct reference to your provider and talking directly to it, but we would urge you to move to the LINQ to Provider layer to access your data. This is one of the relief's that we believe Sitecore Developers will receive when starting with Sitecore 7 in that they don't need to learn Lucene or SOLR or a provider, they learn LINQ and that is it!

The Predicate Builder is aimed to meet you half way. It in very LINQ biased and is used to build up an Expression Tree however it has the flexibility to work with string based queries to build up a predicate ready for LINQ.

We use the PredicateBuilder internally in a class called LinqHelper. LinqHelper is what helps with translating a query that is run through the UI of the new Search Screens in the Sitecore UI to a LINQ query that the LINQ to Provider layer understands. We have also exposed this class as public so you too can use it. In the RTP of Sitecore 7 this will return SitecoreUISearchResultsItem objects, however come Update 2, this will return T, allowing you to pass in your own types. We also have a class called UIFilterHelpers which will turn a string like was shown in Example 1 above into a List<SearchStringModel> which is a structure that is just easier to manage and work with.

Dev Team