Lucene.Net – Customized Model Annotation & Generic Lucene DAO – Design Pattern

Lucene.Net – Customized Model Annotation & Generic Lucene DAO – Design Pattern

 
   Model Annotation Class
 
   using System;
   using Lucene.Net.Documents;
 
   public class LuceneAttribute : Attribute
   {
     public bool key { get; set; }
     public Type type { get; set; }
     public Field.Store store { get; set; }
     public Field.Index index { get; set; }
   }
 
 
   Entity Model
 
   using System;
   using Lucene.Net.Documents;
 
   public class SampleDataX
   {
     [ LuceneAttribute ( key = true, type = typeof(System.Int32), store = Field.Store.YES, index = Field.Index.NOT_ANALYZED ) ]
     public int Id { get; set; }
     [ LuceneAttribute ( type = typeof(System.String), store = Field.Store.YES, index = Field.Index.ANALYZED ) ]
     public string Name { get; set; }
     [ LuceneAttribute ( type = typeof(System.String), store = Field.Store.YES, index = Field.Index.ANALYZED ) ]
     public string Description { get; set; }
     [ LuceneAttribute ( type = typeof(System.String), store = Field.Store.YES, index = Field.Index.ANALYZED ) ]
     public string Details { get; set; }
     [ LuceneAttribute ( type = typeof(System.DateTime), store = Field.Store.YES, index = Field.Index.NOT_ANALYZED ) ]
     public DateTime IndexDate { get; set; }
   }
 
 
   LuceneSearch DAO
 
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Reflection;
   using System.IO;
   using Lucene.Net.Analysis.Standard;
   using Lucene.Net.Documents;
   using Lucene.Net.Linq;
   using Lucene.Net.Index;
   using Lucene.Net.QueryParsers;
   using Lucene.Net.Search;
   using Lucene.Net.Store;
   using Version = Lucene.Net.Util.Version;
 
   /*
         Reference From Lucene.Net Hello World.
         The Class Annotation is for defining the Lucene Field Properties and Field Type from Model Field.
         The Enhanced LuceneSearch DAO Class would get the Generic Class Field Proprties & Annotation
         to define Lucene Field Mapping by using Reflection – PropertyInfo.
   */

   public class LuceneSearch<T> where T : class
   {
     private string _luceneDir;
     private FSDirectory _directoryTemp;
     private FSDirectory _directory
     {
       get
       {
         if (_directoryTemp == null) _directoryTemp = FSDirectory.Open(new DirectoryInfo(_luceneDir));
         if (IndexWriter.IsLocked(_directoryTemp)) IndexWriter.Unlock(_directoryTemp);
         var lockFilePath = Path.Combine(_luceneDir, "write.lock");
         if (File.Exists(lockFilePath)) File.Delete(lockFilePath);
         return _directoryTemp;
       }
     }
 
     /* Constructor */
     public LuceneSearch(string path)
     {
       this._luceneDir = path;
     }
 
     /* Define Lucene Index Field and Document */
     private void _addToLuceneIndex(T Data, IndexWriter writer)
     {
       Document doc = new Document();
       PropertyInfo[] props = typeof(T).GetProperties();
 
       foreach (PropertyInfo prop in props)
       {
         object[] attrs = prop.GetCustomAttributes(true);
 
         foreach(LuceneNet.LuceneAttribute item in attrs)
         {
           if(item.key)
           {
             var searchQuery = new TermQuery(new Term(prop.Name, prop.GetValue(Data).ToString()));
             writer.DeleteDocuments(searchQuery);
 
             doc.Add(new Field(prop.Name, prop.GetValue(Data).ToString(), item.store, item.index));
           }
           else
           {
             if (prop.GetValue(Data) != null)
               doc.Add(new Field(prop.Name, prop.GetValue(Data).ToString(), item.store, item.index));
           }
         }
       }
 
       writer.AddDocument(doc);
     }
 
     /* Pass Data Collection ( List ) to DAO Class and Create Lucene Index */
     public void AddUpdateLuceneIndex(List<T> Datas)
     {
       var analyzer = new StandardAnalyzer(Version.LUCENE_30, new HashSet<string>());
       using (var writer = new IndexWriter(_directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
       {
         foreach (var Data in Datas)
           _addToLuceneIndex(Data, writer);
 
         analyzer.Close();
         writer.Dispose();
       }
     }
 
     /* Define Lucene Document & Class Object Field Mapping ( Return – Search Result ) */
     private T _mapLuceneDocumentToData(Document doc)
     {
       T obj = (T)Activator.CreateInstance(typeof(T));
 
       PropertyInfo[] props = typeof(T).GetProperties();
 
       foreach (PropertyInfo prop in props)
       {
         if(doc != null && doc.Get(prop.Name) != null)
         {
           object[] attrs = prop.GetCustomAttributes(true);
 
           foreach (LuceneNet.LuceneAttribute item in attrs)
           {
             if (item.type == typeof(System.Int32))
               prop.SetValue(obj, Int32.Parse(doc.Get(prop.Name)));
             else if (item.type == typeof(System.DateTime))
               prop.SetValue(obj, DateTime.Parse(doc.Get(prop.Name)));
             else
               prop.SetValue(obj, doc.Get(prop.Name));
           }
         }
       }
 
       return obj;
     }
 
     private IEnumerable<T> _mapLuceneToDataList(IEnumerable<Document> hits)
     {
       return hits.Select(_mapLuceneDocumentToData).ToList();
     }
 
     private IEnumerable<T> _mapLuceneToDataList(IEnumerable<ScoreDoc> hits, IndexSearcher searcher)
     {
       return hits.Select(hit => _mapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList();
     }
 
     private Query parseQuery(string searchQuery, QueryParser parser)
     {
       Query query = null;
 
       try
       {
         query = parser.Parse(searchQuery.Trim());
       }
       catch (ParseException)
       {
         query = parser.Parse(QueryParser.Escape(searchQuery.Trim()));
       }
 
       return query;
     }
 
     /* Clear All Lucene Index */
     public bool ClearLuceneIndex()
     {
       try
       {
         var analyzer = new StandardAnalyzer(Version.LUCENE_30, new HashSet<string>());
 
         using (var writer = new IndexWriter(_directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED))
         {
           writer.DeleteAll();
 
           analyzer.Close();
           writer.Dispose();
         }
       }
       catch (Exception)
       {
         return false;
       }
 
       return true;
     }
 
     /* Lucene Search Function – If searchField Value is not passed, the Search Result is Full-Text Search Index. */
     public IEnumerable<T> _search(string searchQuery, string searchField = "")
     {
       if (string.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", ""))) return new List<T>();
 
       using (var searcher = new IndexSearcher(_directory, false))
       {
         var hits_limit = 1000;
         var analyzer = new StandardAnalyzer(Version.LUCENE_30, new HashSet<string>());
 
         if (!string.IsNullOrEmpty(searchField))
         {
           /* Specific Single Field for Query */
           var parser = new QueryParser(Version.LUCENE_30, searchField, analyzer);
           var query = parseQuery(searchQuery, parser);
           var hits = searcher.Search(query, hits_limit).ScoreDocs;
           var results = _mapLuceneToDataList(hits, searcher);
           analyzer.Close();
           searcher.Dispose();
           return results;
         }
         else
         {
           /* Get All Fields for Query */
           var parser = new MultiFieldQueryParser(Version.LUCENE_30, GetAllFields(), analyzer);
           var query = parseQuery(searchQuery, parser);
           var hits = searcher.Search(query, null, hits_limit, Sort.RELEVANCE).ScoreDocs;
           var results = _mapLuceneToDataList(hits, searcher);
           analyzer.Close();
           searcher.Dispose();
           return results;
         }
       }
     }
 
     private string[] GetAllFields()
     {
       List<string> AllFields = new List<string>();
       PropertyInfo[] props = typeof(T).GetProperties();
 
       foreach (PropertyInfo prop in props)
         AllFields.Add(prop.Name);
 
       return AllFields.ToArray();
     }
   }
 
 
   Main
 
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Reflection;
   using System.IO;
   using Lucene.Net.Analysis.Standard;
   using Lucene.Net.Documents;
   using Lucene.Net.Linq;
   using Lucene.Net.Index;
   using Lucene.Net.QueryParsers;
   using Lucene.Net.Search;
   using Lucene.Net.Store;
   using Version = Lucene.Net.Util.Version;
 
   static void Main(string[] args)
   {
     /* Physical File Path of Lucene Index File. */
     LuceneSearch<SampleDataX> search = new LuceneSearch<SampleDataX>( Path.Combine(@".\", "lucene_index") );
 
     /* Clear Lucene Index. */
     search.ClearLuceneIndex();
     /* Pass Data Collection to Lucene DAO Class for creating Lucene Index. */
     search.AddUpdateLuceneIndex( SampleDataRepository.GetAll() );
 
     /* Execute the Lucene Search Query and Return the Lucene Search Result. */
     List<SampleDataX> list = search._search( "City" ).ToList();
 
     foreach ( var item in list )
       Console.WriteLine( (item.Name + " " + item.Description).Trim() );
 
     Console.WriteLine(list.Count);
     Console.ReadLine();
   }
 

Reference From Lucene.Net Hello World.