Examine Provider for LINQ2Umbraco

FEBRUARY 27 2011

The last couple of months I have been fortunate enough to work on a couple of Umbraco projects, which involved using Examine for searching and in the latest project we are using a Lucene index for caching Umbraco Members. This works surprisingly well and the performance of using Examine as a posed to the standard Member API is without comparison. But performance wasn't the only gain, as we have to retrieve the members based on different properties, which we now can do with a simple search.

After having worked with members through Examine I started thinking about whether there was anything to gain by using Examine to store and retrieve Umbraco documents. There is no doubt that Examine is super fast, but storing a page with its properties would need some modifications: 1. When Examine is indexing content it uses standard Lucene Analyzers, and HTML is stripped from the content, which makes perfect sense in a search scenario, but not so much when using it as an Umbraco document store. 2. Everything isn't indexed by default when using Examine. A few standard properties are not being indexed, and others are being Analyzed by default. So first step is to create a custom indexer, which would enable storing a complete document with all its properties, which is not analyzed upon indexing. Creating a custom Examine indexer is fairly straight forward. Shannon and Aaron have really done a great job with the implementation of Examine, and extending/modifying/creating is possible in every way imaginable.

With storing sorted, retrieval is the next step and creating a provider for LINQ2Umbraco is an obvious choice - well, at least in my opinion. Who doesn't love strongly typed objects :) Again I have to give a shout-out to the black belt Ninja of the Aussie Umbraco Clan for creating a provider based LINQ2Umbraco implementation. After spending a couple of hours reading the source I started to create my own provider, which uses Examine for retrieving Umbraco documents instead of using the XML context like the standard implementation. I made the provider by creating an ExamineDataProvider which implements the abstract UmbracoDataProvider class, an ExamineAssociationTree which implements the abstract AssociationTree class and an ExamineTree which implements the abstract Tree class. I also made an ExamineDataContext, which implements the IUmbracoDataContext interface. Pretty straight forward, right. The hard part was figuring out how to replace the XML context lookups with Examine searches, but it actually worked out pretty good.

Because my initial intension was to create something that performed well, performance testing will be the final examination (no pun intended). In order to test performance of the Examine provider I created a number of test cases, which retrieves and handles data in different ways. For comparison I created the same tests using the LINQ2Umbraco provider. Each test case is run 15 times and the outcome is the average of these test runs.

The Umbraco setup is version 4.6.1 and .NET 4.0 with 7 different Document Types with the following structure:

Home (Number of nodes in Umbraco: 1) - EventOverview (Number of nodes in Umbraco: 1) -- EventType (Number of nodes in Umbraco: 5) --- Year (Number of nodes in Umbraco: 22) ---- Month (Number of nodes in Umbraco: 237) ----- Event (Number of nodes in Umbraco: 2.896) ------ EventSection (Number of nodes in Umbraco: 28.830)

I generated a number of nodes in Umbraco to test different scenarios using the above structure. The scenarios range from getting a specific node by id, get parent of a node, ancestors and traversing children of a node. All common tasks in my opinion, and I could probably have included a lot more.

1. Scenario - DataContext: The first scenario is not all that interesting as its just running a simple get from the DataContext of each provider, and as you can see from the numbers there is no significant difference between the two.

Examine LINQ2Umbraco
GetMonths: 00:00:00.0000506GetYears: 00:00:00.0000477 GetEvents: 00:00:00.0000466 GetVarious: 00:00:00.0001604 GetMonths: 00:00:00.0000508GetYears: 00:00:00.0000472 GetEvents: 00:00:00.0000467 GetVarious: 00:00:00.0001605
Example: var months = _dataContext.Months;

2. Scenario - Get Parent: In this test I get a specific node by id and lookup the parent with type.

Examine LINQ2Umbraco
GetMonthParent: 00:00:00.0045961GetEventParent: 00:00:00.3434473 GetEventSectionParent: 00:00:00.7446487 GetMonthParent: 00:00:00.6830741GetEventParent: 00:00:00.9745453 GetEventSectionParent: 00:00:01.2388632
Example: var month = _dataContext.Months.Where(x => x.Id == 11321).FirstOrDefault(); var year = month.Parent();

3. Scenario - Traverse: In this test I get Months from the DataContext and loop through all months.

Examine LINQ2Umbraco
TraverseMonths: 00:00:00.0038782 TraverseMonths: 00:00:00.6199738
Example: var months = _examineDataContext.Months; foreach (var month in months) { var name = month.NodeName; }

4. Scenario - Traverse Children: In this test I get i.e. Events from the DataContext, loop through all events and its children

Examine LINQ2Umbraco
GetYearMonthTraverseChildren: 00:00:00.0662222GetEventsTraverseChildren: 00:00:10.7358665 GetYearMonthTraverseChildren: 00:00:00.8251347GetEventsTraverseChildren: 00:00:23.0377022
Example: var events = _examineDataContext.Events; foreach (var @event in events) { if (@event == null) continue; foreach (var child in @event.Children) { string name = child.NodeName; } }

5. Scenario - Count: In this test I simply count the number of items of different types from in the DataContext

Examine LINQ2Umbraco
GetMonthsCount: 00:00:00.0031931GetYearsCount: 00:00:00.0004367 GetEventCounts: 00:00:00.3420866 GetEventSectionsCount: 00:00:00.7414970 GetMonthsCount: 00:00:00.6178589GetYearsCount: 00:00:00.5692922 GetEventsCount: 00:00:00.9023448 GetEventSectionsCount: 00:00:01.1714143
Example: var events = _examineDataContext.Events; int count = events.Count();

6. Scenario - Ancestors: In this test I get a specific Event from the DataContext, and get different Ancestors based on type

Examine LINQ2Umbraco
GetEventAncestorAsYear: 00:00:00.3837243 GetEventAncestorAsHome: 00:00:00.0011323 GetEventAncestorAsYear: 00:00:01.0451501GetEventAncestorAsHome: 00:00:00.0014134
Example: var firstEvent = _examineDataContext.Events.Where(x => x.Id == 1210).FirstOrDefault(); var home = firstEvent.AncestorOrDefault();

Note: All numbers are calculated averages from 15 test runs in the following format: hh:mm:ss:ms

For now I'll let the numbers speak for themselves. But I would like to include some tests for the new Query options that comes with Umbraco 4.7 for a little more perspective, and comment some more on the numbers from the various test cases.

If you have some test cases that you think would be valuable for this comparison, please feel free to add a comment with your test case.

I will try to wrap up the code in a solution and package it up with an Umbraco site and test cases, so others can have a go. So check back for updates.

All in all I'm pretty happy with the performance tests so far.

Here is links to the solution containing LINQ2Umbraco with my addition of an Examine Provider (please note that the solution contains the original LINQ2Umbraco source as it has internal dependencies):

Download solution Download assemblies only

The solution and assembly has an Examine indexer, which you should use for indexing your content before trying to use the provider to get strongly typed objects from your index. Set type="LINQ2Examine.Examine.Indexer.ExamineNodeIndexer, LINQ2Examine" for your ExamineIndexProvider in your ExamineSettings.config in order to use it for indexing.

Reference your generated datacontext like this:

public partial class UmbracoDataContext : LINQ2Examine.ExamineDataContext, IUmbracoDataContext public interface IUmbracoDataContext : LINQ2Examine.IUmbracoDataContext

When using the Examine DataContext you initialize like this:

ExamineDataProvider dataProvider = new ExamineDataProvider("MyIndexSearcher"); Generated.IUmbracoDataContext dataContext = new Generated.UmbracoDataContext(dataProvider);

I will add the test solution over the coming weekend, as I need to clean up the current solution and move it to a demo site instead.

Examine LINQ Lucene

Discussion

Shannon Deminick
01
Shannon Deminick 01 Mar, 2011 03:33 AM
Nice work! Looking forward to checking out some code :)
Lee
02
Lee 01 Mar, 2011 07:27 AM
This is awesome Morten... I'd love to get my hands on this source and try to implement into nforum? Is it available anywhere? Some of the queries in that would be a good test, especially doing a topic count from a category as they are nested in date folders.
Lee
03
Lee 03 Mar, 2011 12:24 PM
Just opened up the solution... And closed it a few seconds later!! Wooaa.. Thats some complex stuff. If you get 5 mins any chance giving me a few helpers on getting this setup as the default nforum L2U provider?
Morten Christensen
04
Morten Christensen 03 Mar, 2011 01:56 PM
Well, most of that should be credited to Aaron ;-) But you don't need to look in the source of the currently attached solution. I will include a test project over the weekend that you can use as a reference on how to use/test the provider. You can however just download the assemblies, setup your search index with the custom indexer and then use the ExamineDataContext like its shown in the end of the above post, and just reuse the code from your nForum package.
Lee
05
Lee 03 Mar, 2011 02:12 PM
An example would be great... I'll have a go with just the assemblies though when you say >> Setup your search index with the custom indexer Do you mean just setup an indexer in Examine.config? Does it have to have a specific name?
Morten Christensen
06
Morten Christensen 03 Mar, 2011 02:20 PM
Yes, setup the indexer in ExamineSettings.config using the indexer shown in the post. You can give it any name you just have to specify it when you new up the datacontext (the name of the index that is).
Lee
07
Lee 03 Mar, 2011 02:51 PM
Do I need to create a searcher too? If so what do I use for the type? UmbracoExamine.UmbracoExamineSearcher
Lee
08
Lee 03 Mar, 2011 04:23 PM
Think I'll wait for the example solution as its just not happening for me today..
Lee
09
Lee 03 Mar, 2011 08:49 PM
Getting there.. Although I get the following error Parser Error Message: Value cannot be null. Parameter name: indexSet on LuceneExamineIndexer provider has not been set in configuration and/or the IndexerData property has not been explicitly set I have put nothing in the ExamineIndex.config? As the above says the assembly has an Examine indexer? But this error is saying I should have an indexset in the ExamineIndex.config? Here what I have in the settings
Lee
10
Lee 03 Mar, 2011 08:50 PM
Damn.. stripped the code.. without the tags name="nForumIndexer" type="LINQ2Examine.Examine.Indexer.ExamineNodeIndexer, LINQ2Examine" supportUnpublished="false" supportProtected="true" interval="10" analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"
Morten Christensen
11
Morten Christensen 03 Mar, 2011 10:27 PM
You need to have a corresponding IndexSet for your Indexer. Here is an example config: <a href="http://cdn.sitereactor.dk/Files/Examine/ExamineIndex.config" rel="nofollow">ExamineIndex.config</a> <a href="http://cdn.sitereactor.dk/Files/Examine/ExamineSettings.config" rel="nofollow">ExamineSettings.config</a>
Lee
12
Lee 04 Mar, 2011 07:50 AM
Cool thanks.. Thought it needed those, just wanted to check.. At the last point now of calling the dataContext but VS doesn't understand what 'Generated.' is? &gt;&gt;Generated.IUmbracoDataContext dataContext And if I remove Generated, I get this error... Cannot access protected constructor 'UmbracoDataContext' here Sorry for the backwards and forwards, appreciate you help fella - Keen to get this working and see how it performs :)
Lee
13
Lee 04 Mar, 2011 07:53 AM
Ignore above I'm being a copy a paste idiot and not reading what I'm doing...
Phortepifania
14
Phortepifania 23 Dec, 2011 01:53 PM
view for less for promotion code
plopeterry
15
plopeterry 02 Jan, 2012 03:42 AM
you love this? <a href=http://hermeschina.yolasite.com/>hermes china</a> with low price <a href=http://hermeschina.yolasite.com/>hermes china</a> for promotion code
abuthdeon
16
abuthdeon 02 Jan, 2012 10:48 PM
buy best <a href=http://www.knockoff-lv-purses.com>knock off louis vuitton purses</a> for less <a href=http://www.knockoff-lv-purses.com>knock off louis vuitton</a> <a href=http://www.knockoff-lv-purses.com>lv knockoffs</a> and get big save
plopebrian
17
plopebrian 05 Jan, 2012 05:44 AM
I'm sure the best for you <a href=http://chanel-bags-cheap.weebly.com/>chanel bags cheap</a> to get new coupon <a href=http://chanel-bags-cheap.weebly.com/>chanel bags cheap</a> , for special offer
Phorttony
18
Phorttony 05 Jan, 2012 07:16 PM
I am sure you will love <a href=http://www.replica-lvbags.com/purchase-louis-vuitton-knockoff-handbags-discount-purses-ezp-5.html>knockoff louis vuitton handbags</a> to get new coupon <a href=http://www.replica-lvbags.com/>replica louis vuitton bag</a> and get big save
Scaltlola
19
Scaltlola 07 Jan, 2012 05:01 AM
must look at this <a href=http://dvd-to-lg-converter.weebly.com/>dvd to lg converter</a> for more detail with confident
plopecole
20
plopecole 08 Jan, 2012 11:29 AM
buy a <a href=http://dvd-to-creative-zen.weebly.com/>dvd to creative zen</a> for more <a href=http://dvd-to-creative-zen.weebly.com/>dvd to creative zen</a> for gift
Phorthenry
21
Phorthenry 10 Jan, 2012 06:33 AM
click <a href=http://sale-chanel.weebly.com/>sale chanel</a> suprisely <a href=http://sale-chanel.weebly.com/>sale chanel</a> to get new coupon
Gracewilbur
22
Gracewilbur 10 Jan, 2012 07:37 AM
I'm sure the best for you <a href=http://gucciaaa.eklablog.com/>gucci aaa</a> to take huge discount , for special offer
Vampwalton
23
Vampwalton 16 Jan, 2012 03:35 AM
buy best <a href=http://uobconnect.org/blog.php?user=sigcili&blogentry_id=215774>chanelbags</a> , for special offer <a href=http://uobconnect.org/blog.php?user=sigcili&blogentry_id=215678>chanelbags</a> online
Shubyjena
24
Shubyjena 20 Jan, 2012 01:13 PM
I'm sure the best for you <a href=http://dvd-audio-extractor.weebly.com/>dvd audio extractor</a> to get new coupon to your friends
web site optimization
25
web site optimization 25 Jan, 2012 07:58 PM
Just thought I'd drop you a line to tell you your blog.sitereactor.dk really rocks! I have been looking for this sort of information for a long time.. I don't usually reply to posts but I will in this case. WoW terrific great.
testosterone
26
testosterone 26 Jan, 2012 12:18 PM
I just book marked your blog blog.sitereactor.dk on Digg and Stumble Upon. I enjoy reading your commentaries.
DeaTtlakeisha
27
DeaTtlakeisha 28 Jan, 2012 06:17 PM
you must read <a href=http://rip-dvd-to-h264.weebly.com/>rip dvd to h.264</a> , just clicks away <a href=http://rip-dvd-to-h264.weebly.com/>rip dvd to h.264</a> with low price
Insundainsink
28
Insundainsink 03 Feb, 2012 04:50 AM
Zesyveibe <a href=http://napechke.com>Anrielelin</a> sowTopeteft http://napechke.com - Senanamaso Gowsorics http://napechke.com
DeaTttruman
29
DeaTttruman 03 Feb, 2012 05:02 AM
click <a href=http://dvd-to-iphone-4.weebly.com/>dvd to iphone 4</a> for more and get big save
Phortginny
30
Phortginny 11 Feb, 2012 03:14 PM
look at <a href=http://dvd-to-mkv.weebly.com/>dvd to mkv</a> for less <a href=http://dvd-to-mkv.weebly.com/>dvd to mkv</a> with low price
tours in Russia
31
tours in Russia 16 Feb, 2012 02:10 PM
I think other site proprietors should take blog.sitereactor.dk as an model, very clean and excellent user friendly style and design, let alone the content. You are an expert in this topic!

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview

Powered by FunnelWeb 1.0.1.517