Lucene.NET and NHibernate.Search on medium trust

An updated post about Lucene.Net and NHibernate.Search on medium trust has been published.

This is a follow-up of the previously posted articles about deploying an ASP.NET MVC website and setting up NHibernate on a "medium trust" hosting service (using IIS 6 and .NET 2.0).

Setting up Lucene.NET

Lucene logo In order to enable full-text searching on our website, we'll first have to get a copy of the latest version of Lucene.NET from the official project homepage. Download both the latest binaries and the source code.

The binary package can be used to get to the various contrib packages which don't require any change (like the Highlighter, the Similarity library, the Snowball analyzer and so on), while to get the main library "Lucene.Net.dll" to run we'll have to apply a small patch and recompile it.

Without further ado, let's open up the Lucene.NET solution in Visual Studio and go looking for the "/src/Lucene.NET/Store/FSDirectory.cs" file. At line 155 or so you'll find:

/// <summary> Directory specified by <code>Lucene.Net.lockDir</code> /// or <code>java.io.tmpdir</code> system property /// </summary> public static readonly System.String LOCK_DIR = SupportClass.AppSettings.Get("Lucene.Net.lockDir", System.IO.Path.GetTempPath());

As described in the System.IO.Path.GetTempPath documentation, this method call can throw a SecurityException error if you don't have sufficient rights (exactly our problem on a "medium trust" host). Simply change the line into something like:

public static readonly System.String LOCK_DIR = SupportClass.AppSettings.Get("Lucene.Net.lockDir", string.Empty);

We'll ensure this works correctly even after the change by correctly setting up the web.config file. Anyway, build the solution and you should end up with a nice Lucene.Net.dll library.  :)

Configuring Lucene.NET

OK, now include the library inside you website's "Bin" folder and open the web.config file. We need to set all paths to the folder where we'll store our indices:

<appSettings> <add key="Lucene.Net.lockdir" <b>value="FULL path to your indices store"</b> /> </appSettings>

Remember that the Lucene.NET.lockdir value must be the full path to your indices folder, which changes from host to host. To find out the root folder of your website you can create a dummy aspx page somewhere and simply print this:

AppDomain.CurrentDomain.BaseDirectory

Then simply append the subfolder path of your Lucene index. Also remember that you must have full access to the folder.

And now to NHibernate.Search!

NHibernate.Search is a contributed project and is nowhere to be found in the main NHibernate trunk. There aren't any binary compiled files either, therefore we'll have to head to the SVN repository on SourceForge of the NHContrib project and download a tarball of all source files.

Unzip everything and open it with Visual Studio. It should compile fine right out of the box and spit out the NHibernate.Search.dll file that can be included into your website's "Bin" folder, along with Lucene.Net.dll.

The way to communicate with Lucene.NET through NHibernate.Search is to use the IFullTextSession (that extends the default ISession interface) and calling the following methods on it:

  • CreateFullTextQuery: exists in 3 "flavors" and allows you to build a more or less complex full text query and then interrogate the Lucene index like in any other NHibernate HQL query.
  • Index: adds an entity istance to the Lucene index. You can call this method manually when adding an instance to the DB (remember that the database and the Lucene index are two separate "stores" and you'll have to keep them in sync to have correct results).
  • Purge: removes an instance from the Lucene index.
  • PurgeAll: removes all instances of an entity from the index. Can be used to refresh the whole index at once.

The IFullTextSession instance is created by wrapping an ISession, like so:

ISessionFactory factory; //Create session factory... IFullTextSession session = Search.CreateFullTextSession(factory.OpenSession());

Configuration

Your web.config files needs to be updated again with NHibernate.Search settings:

<configSections> <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" requirePermission="false" /> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" requirePermission="false" /> <section name="nhs-configuration" type="NHibernate.Search.Cfg.ConfigurationSectionHandler, NHibernate.Search" requirePermission="false" /> </configSections> <!-- Lucene.NET --> <nhs-configuration xmlns='urn:nhs-configuration-1.0'> <search-factory> <property name='hibernate.search.default.directory_provider'>NHibernate.Search.Store.FSDirectoryProvider, NHibernate.Search</property> <property name='hibernate.search.default.indexBase'><b>FULL path to the index folder</b></property> </search-factory> </nhs-configuration>

Again, you need to paste the full path to the folder where you store Lucene's indices.

Automatic indexing of objects

As said before, you can manually Index and Purge your entities when you store and delete them from the DB, but you can also setup an event listener that will do all work for you. To do so, simply modify the code that sets up the NHibernate configuration:

NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration(); //Load all xml files //... //Add NHibernate.Search listeners cfg.SetListener(NHibernate.Event.ListenerType.PostUpdate, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostInsert, new FullTextIndexEventListener()); cfg.SetListener(NHibernate.Event.ListenerType.PostDelete, new FullTextIndexEventListener()); ISessionFactory factory = cfg.BuildSessionFactory();

Now Lucene.NET should work and you can use it to do full text queries directly through NHibernate, without any loss of abstraction. However, I noticed that automatically updating via FullTextIndexEventListener is not completely reliable: on my own website I still have to refresh the Lucene index periodically to ensure everything is in sync with the database. I shall post a solution if I find one.  :)

Using NHibernate.Search

You'll find some very interesting articles about how to use NHibernate.Search from your code on Dario Quintana's and Simon Green's blog (the latter also focuses on similarity queries and other useful features from the Lucene contrib projects).