Peter Nguyen Team : Web Development Tags : Web Development Umbraco

Using Examine to boost your Umbraco front-end performance

Peter Nguyen Team : Web Development Tags : Web Development Umbraco

I’m not a huge fan of Umbraco’s way of looking up content. It’s slow. Clunky. It retrieves content data by reading an XML file with all the content pages in it, extracts which particular page property you were after, and then spits it out. Over time, as more and more content gets populated into Umbraco, the XML file gets bigger and bigger.

Subsequently, the front-end site performance suffers dearly.

The project that I’m currently working on at the moment is highly content driven. There could be potentially thousands of content pages, and Umbraco simply isn’t just designed for it. Doing things the ‘normal’ way wasn’t going to work.

Then I started to play around with Examine, and found it was pretty damn fast at looking up Umbraco content.

But… what’s examine?

Examine is a basic implementation of Lucene, the incredibly fast, full-featured text search engine library that was originally written in Java. It was then ported into C#, with the project name Lucene.net. In short, Examine searches through Umbraco content quickly, and much more efficiently than XML ever will.

The approach I took was fairly straightforward.

  1. Instead of looking up content using the IPublishedContent model that was passed by Umbraco, do an ID lookup in Examine of the content node.
  2. Map properties that Examine has retrieved into some form of clean, basic view model.
  3. Extend Examine to provide the capability of adding or modifying new or existing properties in the index.

Looking up the content

There are many guides on how to do this on the internet, but in short:

1. Look up the searcher needed in the search provider collection.

var searcher = ExamineManager.Instance.SearchProviderCollection['ExternalSearcher'];

2. Build a criteria. 

var criteria = searcher.CreateSearchCriteria();  
(An example to query all document types within a parent content node)
criteria.ParentId(parentId);
criteria.NodeTypeAlias(docType);

3. Get the results, and do whatever you need to do with them (e.g. map into a model, etc.)

var results = searcher.Search(criteria);

Mapping the results

There are multiple ways of which you could do this – but my personal favourite is just creating a basic view model, and creating a bind method to map the ISearchResult that Examine returns into the model.

Here’s some rough example code:

public class ProductModel
    {
        public int Id { getset; }
 
        public string Title { getset; }
        public string Description { getset; }
        public string Body { getset; }
 
        public string Url { getset; }
        public string Sizes { getset; }
        public SearchResult Result { getset; }
 
 
        public void Bind(SearchResult result)
        {
            Result = result;
 
            Id = result.Id;
            Title = result.Fields.ValueOfKeyOrDefault('title');
            Description = result.Fields.ValueOfKeyOrDefault('description');
            Body = result.Fields.ValueOfKeyOrDefault('body');

 

                   }

          }

Injecting more or updating properties when building the Examine index

With Lucene, you can attach multiple events that will be triggered as the index is being built. This is a global event that basically gets triggered every time someone publishes a page in Umbraco, or triggers an index rebuild.

Basically, what I did was lookup the index provider in the collection, and attach an event to it:

var indexer = (LuceneIndexer)ExamineManager.Instance.IndexProviderCollection['ExternalIndexer'];

indexer.DocumentWriting += indexer_DocumentWriting;

Then all you need to do is attach to that event, and change whatever you need. Both the Umbraco document and Lucene document can be easily retrieved:

protected void indexer_DocumentWriting(object sender, DocumentWritingEventArgs e)
{
    var luceneDocument = e.Document;
    var umbracoDocument = new DynamicNode(Convert.ToInt32(e.Fields['__NodeId']));
 
 
    luceneDocument.Add(new Lucene.Net.Documents.Field('someproperty''somevalue',
                                                        Lucene.Net.Documents.Field.Store.YES,
                                                        Lucene.Net.Documents.Field.Index.NOT_ANALYZED));
}

Once all that is complete and hooked up, you’ll be able to start enjoying all the benefits of hooking up an entire search engine library to retrieve content pages on your site.

Which certainly has its perks when needing to extend and build upon it.