LibrarySites.Banner

Sitecore 7 Getting Access to the Provider

Sitecore 7: Get access to the provider to run complex queries

In Sitecore 7 we have done a lot of work to abstract away the provider that is powering all the search and indexing that is being done within the system. By doing this we have also abstracted away a lot of the complexity that comes with a search provider. However when there is something you need to talk to the provider directly about then a sudden hack guilt-trip becomes apparent and you wonder if talking to the provider directly is a good idea or not. The answer is - it is not a matter of feeling good or bad about it but talking to the provider only when it is 100% necessary. The team has mentioned it many times that we limited our SOLR support to what was supported in Lucene.net e.g. not supporting Grouping in the LINQ layer because Lucene.net does not have the support. We did this on purpose so moving between providers should be as transparent as possible. However we do not stop you from talking to Lucene.net or SOLR directly. In this post, we wanted to show you one way of doing it which is clean and will not require you to shower to clean away the dirt.

Let's use the Lucene Provider as an example.

The LuceneIndex class has methods to get an IndexReader which you can use to directly search the index.

Together with a little hidden feature you can create a method that extends the native query that the LINQ layer generates. The queryable returned from context.GetQueryable can be cast to IHasNativeQuery where you can get access to the raw LuceneQuery object.

This approach will of cause limit you in which LINQ queries that can be written unless you would manually handle the query methods like Skip, Take, OrderBy etc. You would also need to call the document mapper manually as well if you wanted your Documents to be mapped to your own Type (ExtendedTestDocument).

Continuing with this idea we could just let the built in LinqToLuceneIndex do all this work for us.

    private void Test()
    {
        var index = InitializeIndex();

        using (var context = index.CreateSearchContext())
        {
            var queryable = context.GetQueryable<ExtendedTestDocument>();

            queryable = queryable.Where(d => d.Name == "Martin");

            var query = ExtendNativeQuery((IHasNativeQuery)queryable);

            var linqToLucene = new LinqToLuceneIndex<ExtendedTestDocument>((LuceneSearchContext)context, new IExecutionContext[0]);

            var returnType = queryable.Expression.Type;

            var method = linqToLucene.GetType().GetMethod("Execute", BindingFlags.Instance | BindingFlags.Public);
            var executeMethod = method.MakeGenericMethod(returnType); //linqToLucene.Execute<TResult>(query);

            var results = executeMethod.Invoke(linqToLucene, new[] { query });

            // TODO: Use results
        }
    }

    private Sitecore.ContentSearch.Linq.Lucene.LuceneQuery ExtendNativeQuery(IHasNativeQuery hasNativeQuery)
    {
        var query = (Sitecore.ContentSearch.Linq.Lucene.LuceneQuery)hasNativeQuery.Query;

        // Rewrite/extend lucene query
        // Your raw access to the Lucene Query 
        Lucene.Net.Search.Query luceneQuery = query.Query;
        // ....

        return new LuceneQuery(luceneQuery, query.Filter, query.Methods, query.VirtualFieldProcessors, query.FacetQueries, query.UsedAnalyzers, query.ExecutionContexts);
    }

Another use-case that we have run into is with Boosting. We do not support boosting on all parts of the LINQ layer i.e. StartsWith and Contains. We have had a couple of requests where a client would like to boost exact matches first, then StartsWith and then Contains. This is a fair request and the above example is the kind of code you would use to solve this problem. You would be looking at building a query like title:value^10 title:value*^5 title:*value. For this you could use the strongly typed LuceneQuery objects or you could use the QueryParser.

  • Dev Team
  • Thanks for the post, the Provider model is a great approach for Sitecore Search.  I remember hearing talk about supporting multiple providers in a single Solution.  This type of setup would allow you to run Lucene, SOLR, or ElasticSearch side by side.  Curious if this was still in the works?  

  • It sure is. We held back on a feature release as per our release policy, we are not allowed to introduce new features. These will come in a new version of Sitecore i.e. 7.X

  • This is very helpful article to allow developer to access directly to Lucene API.  Which Rev version of sitecore 7 this code can successfully execute? I try this on 7.1 rev. 130926 but there is no constructor for LinqToLuceneIndex to support this line  var linqToLucene = new LinqToLuceneIndex<SearchResultItem>((LuceneSearchContext)context, new IExecutionContext[0]);  I try again on 7.2 rev. 140314 and I got error on this line  var results = executeMethod.Invoke(linqToLucene, new[] { query }); The error is Exception Details: System.InvalidOperationException: Sequence contains no elements.   Are there any missing code that you didn't provide here? Thanks