As a followup to my previous post about gotchas with Sitecore 7’s LINQ provider, here’s another thing to consider.
The indexing providers are very greedy about indexing. This means that unlike with traditional querying with Sitecore.Data.Item, where your results are automatically filtered by the context language and latest item version, no such filtering occurs with LINQ. You will receive all versions and all languages unless you specify otherwise.
As you might imagine, this can result in unexpectedly large quantities of search results in some cases. It can also be extra sneaky since during development you might only have one version in one language - so you wouldn’t even notice the issue.
So how do you fix the issue? First, let’s talk about versions. The default indexing configuration includes a field called _latestversion.
This is a boolean field that is only set to true for items that are the latest version in their language. We can take advantage of this by implementing a property on our mapped objects and mapping it to this index field like so:
[IndexField(“_latestversion”)]
public bool IsLatestVersion { get; set; }
Then, when we write a query we want to limit, we simply add a clause to the query:
.Where(x => x.IsLatestVersion)
// alternatively if you don’t want to be strongly typed and have an indexer, you can use
.Where(x=> x[“_latestversion”] == “1”)
Now you’ll only get the latest version. Now for languages, which are also pretty simple. If you’re inheriting from the SearchResultItem class, you already have a Language property. Otherwise you can add one like so:
[IndexField(“_language”)]
public string Language { get; set; }
Then, we add the following clause to the query:
.Where(x => x.Language == Sitecore.Context.Language.Name)
Now we get results like regular queries. If you’re like me, the next question you’re asking is “how can I just write this once and forget about it?” For example something like:
public static IQueryable<T> GetFilteredQueryable<T>(this IProviderSearchContext context) where T : MyItemBaseClass { return context.GetQueryable<T>().Where(x => x.Language == Sitecore.Context.Language.Name && x.IsLatestVersion); }
Unfortunately, this seems to be nigh impossible with the current revision of Sitecore 7. The issue has to do with how LINQ resolves expressions involving generic types that are not resolved at compile time. Effectively the expression in the example above converts to:
.Where(x=> ((T)x).Language == Sitecore.Context.Language.Name)
Notice the cast to T? That throws the expression parser for a loop. I’ve been told this will be fixed in a later release of Sitecore 7, but will not be part of the RTM release, so for the moment it looks like we’re writing filtering on each query.