How to: Sitecore Content Search – Globale LINQ Filter anwenden

Sitecore Global Linq Filter

Seit der Sitecore Version 7.2. ist es möglich, für die Content Search LINQ Filter zu implementieren, die automatisch bei jedem LINQ Query angewendet werden. Dadurch können “statische” Filter wie z.B. die Einschränkung nach Kontextsprache einmalig angegeben werden und brauchen somit nicht bei jeder Abfrage berücksichtigt zu werden.

Um globale LINQ Filter einzubinden, muss die Pipeline <contentSearch.getGlobalLinqFilters> erweitert werden:

Achtung: Das folgende Beispiel ist bis einschließlich der Sitecore Version 8.1 möglich.

[xml]
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">;
<sitecore>
<pipelines>
<contentSearch.getGlobalLinqFilters>
<processor type="Comspace.Demo.GlobalLinqFilter.ContextLanguageLinqFilter, Comspace.Demo.GlobalLinqFilter"/>
</contentSearch.getGlobalLinqFilters>
</pipelines>
</sitecore>
</configuration>
[/xml]

Die Klasse ContentLanguageLinqFilter erbt von der bereitgestellten Basisklasse ApplyGlobalLinqFilters, welche zwei überschreibbare Funktionen bereitstellt:

  • Process(QueryGlobalFiltersArgs args)
  • GetQuery(QueryGlobalFiltersArgs args)

Um nun nach der aktuellen Sprache zu filtern, muss die Query angepasst werden. Hierfür wird die Funktion GetQuery überschrieben:

[csharp]
namespace Comspace.Demo.GlobalLinqFilter
{
public class ContextLanguageLinqFilter : ApplyGlobalLinqFilters
{
protected override object GetQuery(QueryGlobalFiltersArgs args)
{
var query = (IQueryable<SearchResultItem>)args.Query;
return query.Where(i => i["_language"].Equals(Sitecore.Context.Language.Name));
}
}
}
[/csharp]

In diesem Beispiel wird das Objekt args.Query in den Typen IQueryable<SearchResultItem> umgewandelt.

GlobalLinqFilter ab Sitecore Version 8.2

Mit der Sitecore Version 8.2 ändert sich die Verwendung von Queries im Sitecore, sodass ein allgemeines Casting in den Typen IQueryable<SearchResultItem> nicht mehr möglich ist. Wird ein GlobalLinqFilter wie oben beschrieben eingesetzt, wirft Sitecore bereits bei der Initialisierung bzw. beim Öffnen des Sitecore Content Editors eine Exception:

[text]
Das Objekt des Typs "Sitecore.ContentSearch.Linq.Parsing.GenericQueryable`2[Sitecore.ContentSearch.SearchTypes.SearchResultItem,Sitecore.ContentSearch.Linq.Solr.SolrCompositeQuery]" kann nicht in Typ "System.Linq.IQueryable`1[Sitecore.Social.Search.SearchItem]" umgewandelt werden.

[/text]

Und jetzt?

Zu der Verwendung von GlobalLinqFilter existiert keine nennenswerte Dokumentation. Auch Google liefert nur ein mageres Beispiel von Coveo, das aber nicht für Sitecore ab Version 8.2 geeignet ist.

Der daher konsultierte Sitecore Support stellt folgende Lösung zur Verfügung:

[csharp]
public override void Process(QueryGlobalFiltersArgs args)
{
if ((args != null) && (args.Query != null))
{
Type queryElementType = args.QueryElementType;
if (queryElementType == typeof(SearchResultItem))
{
args.Query = this.GetQuery&lt;SearchResultItem&gt;(args);
}
}
}

protected object GetQuery<T>(QueryGlobalFiltersArgs args) where T : IObjectIndexers
{
var query = (IQueryable<T>)args.Query;

return query.Where(i => i["_language"].Equals(Sitecore.Context.Language.Name));
}
[/csharp]

In diesem Beispiel wird nicht mehr die überschreibbare Funktion GetQuery verwendet, sondern eine eigene, generische Funktion implementiert. Ziel ist es, die Query, die im GlobalLinqFilter als Typ object übergeben wird, in den Zieltypen umzuwandeln. Hierfür liefern die QueryGlobalFiltersArgs auch die QueryElementType-Eigenschaft mit.

Problem: Ein Objekt lässt sich nicht mithilfe einer Variablen typisieren.

Lösung: Um GlobalLinqFilter trotzdem anwenden zu können, muss nun also die Process Methode überschrieben werden. Darin wird der übergebene Typ geprüft und anschließend mit einer expliziten Typisierung die GetQuery<T> Funktion aufgerufen.

Vorteil:

  • Es werden nur solche Queries eingeschränkt, die auch erwünscht sind, da die Abfrage explizit in der Process Methode angegeben muss.

Nachteil:

  • Werden mehrere spezielle Typen von SearchResultItem implementiert, z.B. ExtendedSearchResultItem, DateSearchResultItem, etc. muss für jeden dieser Typen eine Abfrage im GlobalLinqFilter eingerichtet werden.

Der Nachteil äußert sich in der Implementierung dann folgendermaßen:

[csharp]
public override void Process(QueryGlobalFiltersArgs args)
{
if ((args != null) && (args.Query != null))
{
Type queryElementType = args.QueryElementType;
if (queryElementType == typeof(SearchResultItem))
{
args.Query = this.GetQuery<SearchResultItem>(args);
}
if (queryElementType == typeof(ExtendedSearchResultItem))
{
args.Query = this.GetQuery<ExtendedSearchResultItem>(args);
}
}
}
[/csharp]

Zusammenfassung

GlobalLinqFilter sind ein probates Mittel, um Queries standardmäßig nach bestimmten Kriterien zu filtern. Kriterien können neben der hier gezeigten Sprache auch Sicherheitskriterien, Templates oder Sites sein. Wenn nicht exzessiv spezielle SearchResultItem Typen implementiert wurden und somit der oben beschriebene Nachteil eher überwiegt, ist die Nutzung von GlobalLinqFilter zu empfehlen.

Übrigens: Wen das Thema Sitecore Content Search interessiert, findet hier einen Einstieg in das Thema Sitecore Solr Content Search – Facettensuche richtig anwenden.