Quantcast
Viewing latest article 1
Browse Latest Browse All 10

Use NHibernate-Search, Lucene.Net

NHibernate:
NHibernate is a mature, open source object-relational mapper for the .NET framework. It’s actively developed, fully featured and used in thousands of successful projects. You will get the details and download from here http://nhforge.org/Default.aspx.

Lucene.Net:
Apache Lucene(TM) is a high-performance, full-featured text search engine library written entirely in Java. It is a technology suitable for nearly any application that requires full-text search, especially cross-platform.
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users. The Lucene search library is based on an inverted index. Lucene.Net has three primary goals:
1. Maintain the existing line-by-line port from Java to C#, fully automating and commoditizing the process such that the project can easily synchronize with the Java Lucene release schedule;
2. Maintaining the high-performance requirements excepted of a first class C# search engine library;
3. Maximize usability and power when used within the .NET runtime. To that end, it will present a highly idiomatic, carefully tailored API that takes advantage of many of the special features of the .NET runtime.
You will get the details from here http://incubator.apache.org/lucene.net/

NHibernate Search:
NHibernate Search is an extension to NHibernate that allows you to utilize Lucene.NET, a full text search engine as your query engine, instead of putting additional load on the database itself. In a sense, this is a good way outsource your queries from the database. In http://ayende.com/blog/3992/nhibernate-search

You can download NHibernate source via SVN from here https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk/src/NHibernate.Search/

Configure NHibernate
To use NHibernate we must have to configure NHibernate settings in our application.
The diagram below shows a CompanyInformation database tables I have used in my example. We need to create persistence classes, mapping files and setup NHibernate configurations.
Image may be NSFW.
Clik here to view.
Database

Persistent Classes
Below is the persistent classes corresponding to the database tables. Here the attributes like [DocumentId], [Field] are required for NHibernate search. I will describe these later.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sample.Data
{
    public class Company
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual string Address { get; set; }
        public virtual string PhoneNumber { get; set; }
        public virtual string Email { get; set; }
        public virtual string Description { get; set; }     
    }

    [Indexed]
    public class Employee
    {
        [DocumentId]
        public virtual int Id { get; set; }

        [Field(Index.Tokenized, Store = Store.Yes)]
        [Field(Index.UnTokenized, Name = "NameForSort")]
        public virtual string FirstName { get; set; }

        [Field(Index.Tokenized, Store = Store.Yes)]
        public virtual string LastName { get; set; }

        public virtual string Address { get; set; }
        public virtual string Sex { get; set; }
        public virtual string Salary { get; set; }
        public virtual DateTime BirthDate { get; set; }
        public virtual DateTime JoinDate { get; set; }
        public virtual Company Company { get; set; }
        public virtual Grade Grade { get; set; }
    }

    public class Grade
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
    }
}

Mapping Files
Below the corresponding mapping files

Company.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Sample.Data"
                   namespace="Sample.Data">

  <class name ="Company" table="Company">
    <id name="Id" column="Id">
      <generator class="identity" />
    </id>
    <property name="Name" column="Name" />
    <property name="Address" column="Address" />
    <property name="PhoneNumber" column="PhoneNumber" />
    <property name="Email" column="Email" />
    <property name="Description" column="Description" />
  </class>
</hibernate-mapping>

Employee.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Sample.Data"
                   namespace="Sample.Data">

  <class name ="Employee" table="Employee">
    <id name="Id" column="Id">
      <generator class="identity" />
    </id>
    <property name="FirstName" column="FirstName" />
    <property name="LastName" column="LastName" />
    <property name="Address" column="Address" />
    <property name="Sex" column="Sex" />
    <property name="Salary" column="Salary" />
    <property name="BirthDate" column="BirthDate" />
    <property name="JoinDate" column="JoinDate" />
    <many-to-one name="Company" column="CompanyId" class="Company" cascade="none"/>
    <many-to-one name="Grade" column="GradeId" class="Grade" cascade="none"/>    
  </class>
</hibernate-mapping>

Grade.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Sample.Data"
                   namespace="Sample.Data">

  <class name ="Grade" table="Grade">    
    <id name="Id" column="Id">
      <generator class="identity" />
    </id>
    <property name="Name" column="Name" />
    <property name="Description" column="Description" />
  </class>
</hibernate-mapping>

NHibernate Settings
Now we have all the persistent classes and corresponding mappings.
To use NHibernate we need some configuration and below is the configuration in the app.config file of the test project I have used in the example.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>

  <connectionStrings>
    <add name="DefaultConnectionString" connectionString="data source=MIZANPC\SQLEXPRESS;Integrated Security=SSPI;
	    database=CompanyInformation;  MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
  </connectionStrings>

  <hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
    <session-factory name="NHibernate.Test">
      <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
      <property name="adonet.batch_size">10</property>
      <property name="show_sql">true</property>
      <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
      <property name="use_outer_join">true</property>
      <property name="command_timeout">60</property>
      <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
      <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>      
      <property name="current_session_context_class">thread_static</property>
      <mapping assembly="Sample.Data"/>
    </session-factory>
  </hibernate-configuration>

Now all the configurations are ready to use NHibernate.

Lucene.Net
I have introduced Lucene.Net earlier. We can create and search index using Lucenene.Net without NHibernate Search.

Create a Lucene.Net Index
The steps to create a Lucene.Net Index

  • Step 1. Add reference to Lucene.Net dll.
  • Step 2. Initialize the Directory and Index Writer
    Directory directory = FSDirectory.GetDirectory(@"E:\Test\Sample\LuceneIndex");
          Analyzer analyzer = new StandardAnalyzer();
          IndexWriter writer = new IndexWriter(directory, analyzer);  </li>
              
  • Step 3. Add documents to the Index
                Document doc = new Document();
                doc.Add(new Field("Id", "1", Field.Store.YES, Field.Index.NO));
                doc.Add(new Field("Name", "Test", Field.Store.YES, Field.Index.TOKENIZED));
                writer.AddDocument(doc);</li>
    
  • 4. Optimize and close the writer.
                writer.Optimize();
                writer.Flush();
                writer.Close();
    

Below is the whole code to create a Lucene.Net Index.

Directory directory = FSDirectory.GetDirectory(@"E:\Test\Sample\LuceneIndex");
Analyzer analyzer = new StandardAnalyzer();
IndexWriter writer = new IndexWriter(directory, analyzer);            
Document doc = new Document();
doc.Add(new Field("Id", "1", Field.Store.YES, Field.Index.NO));
doc.Add(new Field("Name", "Test", Field.Store.YES, Field.Index.TOKENIZED));
writer.AddDocument(doc);
writer.Optimize();
writer.Flush();
writer.Close();

View Index
Now if you go in the folder “E:\Test\Sample\LuceneIndex” you will see the indexes are created there. To view the index you can use Luke. You will get the details an downloads from here http://code.google.com/p/luke/

Specifying the index directory “E:\Test\Sample\LuceneIndex” (for me) you can view the index created there.

Read Index From Code
Below is the code to read the lucene index

Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser("Name", analyzer);
Query query = parser.Parse("test");

Directory directory = FSDirectory.GetDirectory(@"E:\Test\Sample\LuceneIndex");
IndexSearcher indexSearcher = new IndexSearcher(directory);
Hits hits = indexSearcher.Search(query);

int results = hits.Length();
            
for (int i = 0; i &lt; results; i++)
   {
      Document doc = hits.Doc(i);
      float score = hits.Score(i);                
      var id = doc.Get(&quot;Id&quot;);
      var name = doc.Get(&quot;Name&quot;);
  }

NHibernate Search
We have seen how we can create and view (read) index using Lucene.Net. Now I will show how we can use HNibernate search for the same purpose.

  • Step1: Add reference to NHibernate.Search dll
  • Step2: Add the configuration below in the nhibernate configuration section.
    <listener class="NHibernate.Search.Event.FullTextIndexEventListener, NHibernate.Search" type="post-insert"/>
          <listener class="NHibernate.Search.Event.FullTextIndexEventListener, NHibernate.Search" type="post-update"/>
          <listener class="NHibernate.Search.Event.FullTextIndexEventListener, NHibernate.Search" type="post-delete"/>
          <listener class="NHibernate.Search.Event.FullTextIndexCollectionEventListener, NHibernate.Search" type="post-collection-recreate"/>
          <listener class="NHibernate.Search.Event.FullTextIndexCollectionEventListener, NHibernate.Search" type="post-collection-remove"/>
          <listener class="NHibernate.Search.Event.FullTextIndexCollectionEventListener, NHibernate.Search" type="post-collection-update"/>
    
  • Also add the following configuration in app.config

<nhs-configuration xmlns="urn:nhs-configuration-1.0">
    <search-factory>
      <property name="hibernate.search.default.directory_provider">NHibernate.Search.Store.FSMasterDirectoryProvider, NHibernate.Search</property>
      <property name="hibernate.search.default.sourceBase">E:\Test\Sample\Index_source</property>
      <property name="hibernate.search.default.indexBase">E:\Test\Sample\Index</property>
      <property name="hibernate.search.default.refresh">600</property>
    </search-factory>
  </nhs-configuration>
  • Step3: Annoted your domain classes with required attributes available in NHibernate Search. Below some attributes are used like Indexed, DocumentId, Field etc.

    [Indexed]
        public class Employee
        {
            [DocumentId]
            public virtual int Id { get; set; }
    
            [Field(Index.Tokenized, Store = Store.Yes)]
            [Field(Index.UnTokenized, Name = "NameForSort")]
            public virtual string FirstName { get; set; }
    
            [Field(Index.Tokenized, Store = Store.Yes)]
            public virtual string LastName { get; set; }
    
            public virtual string Address { get; set; }
            public virtual string Sex { get; set; }
            public virtual string Salary { get; set; }
            public virtual DateTime BirthDate { get; set; }
            public virtual DateTime JoinDate { get; set; }
            public virtual Company Company { get; set; }
            public virtual Grade Grade { get; set; }
        }
    
    
  • Now we are ready to create index using NHibernate search.
    Below is a sample code to generate index.

    EmployeeRepository employeeRepository = new EmployeeRepository(SessionFactory);
      IFullTextSession session = new FullTextSessionImpl(SessionFactory.GetCurrentSession());
                ITransaction tx = session.BeginTransaction();
                session.PurgeAll(typeof(Employee));
                tx.Commit();
    
                tx = session.BeginTransaction();
    
                foreach (object entity in session.CreateCriteria(typeof(Employee)).List())
                {
                    session.Index(entity);
                }
    
                tx.Commit();
                session.Close();
    

    Below is sample code for reading the index created

    string searchQuery = "FirstName:Mr";
                QueryParser parser = new QueryParser("FirstName", new Lucene.Net.Analysis.Standard.StandardAnalyzer());
                Query query = parser.Parse(searchQuery);
                IFullTextSession fullTextSession = Search.CreateFullTextSession(SessionFactory.GetCurrentSession());
                IFullTextQuery fullTextQuery = fullTextSession.CreateFullTextQuery(query, typeof(Employee));
                var employeeList = fullTextQuery.List();
    
    

    Image may be NSFW.
    Clik here to view.
    Image may be NSFW.
    Clik here to view.

    Viewing latest article 1
    Browse Latest Browse All 10

    Trending Articles