Search Index with custom fields in sitefinity 4

November 14, 2011 Digital Experience

This is going to be a HOW to configure  the search indexes in sitefinity to index and search on more than the default fields. 

1. You have to add the fields in sitefinity search index. The search system is based on the publishing system so the way to add additional fields is  like this :   


var ContentSearchPipesSettigns = PublishingSystemFactory.GetTemplatePipes("SearchItemTemplate").Where(ps => ps.PipeName == ContentInboundPipe.PipeName && ps.IsInbound).FirstOrDefault();
 
          if (!ContentSearchPipesSettigns.Mappings.Mappings.Any(mp => mp.DestinationPropertyName == "TestField"))
          {
              var mapping = PublishingSystemFactory.CreateMapping("TestField", TransparentTranslator.TranslatorName, false, "TestField");
 
              ContentSearchPipesSettigns.Mappings.Mappings.Add(mapping);
 
              PublishingSystemFactory.RegisterTemplatePipe("SearchItemTemplate", ContentSearchPipesSettigns, ps => ps.PipeName == ContentInboundPipe.PipeName && ps.IsInbound);
          }
 
 
          var searchPipesSettigns = PublishingSystemFactory.GetTemplatePipes("SearchItemTemplate").Where(ps => ps.PipeName == SearchIndexOutboundPipe.PipeName && !ps.IsInbound).FirstOrDefault();
 
          if (!searchPipesSettigns.Mappings.Mappings.Any(mp => mp.DestinationPropertyName == "TestField"))
          {
              var mappingTest = PublishingSystemFactory.CreateMapping("TestField", TransparentTranslator.TranslatorName, false, "TestField");
 
              searchPipesSettigns.Mappings.Mappings.Add(mappingTest);
 
              PublishingSystemFactory.RegisterTemplatePipe("SearchItemTemplate", searchPipesSettigns, ps => ps.PipeName == SearchIndexOutboundPipe.PipeName && !ps.IsInbound);
          }

First we add a  field named "TestField" in the inbound pipe and second  part add the field into the search index pipe.  This is changing the default search index template so all your search indexes should be recreated (deleted and created again) to apply this settings. 

2. Now you have the field in the search index. So now you have to replace the provider to be able to have control over the fields that are part of the search queries : 


var section = Config.Get<SearchConfig>();
           foreach (var provider in section.Providers.Values)
           {
               if (provider.Name == "LuceneSearchProvider")
               {
                   if (provider.ProviderType == typeof(LuceneSearchProvider))
                   {
                       provider.ProviderType = typeof(LucenseCustomSearchProvider);
                       ConfigManager.GetManager().SaveSection(section);
                       break;
                   }
               }
           }

3. You have to implement this Custom lucene  search provider  like this  : 


public class LucenseCustomSearchProvider : LuceneSearchProvider
    {
        public override IResultSet Find(string catalogueName, string query, int skip, int take, params string[] orderBy)
        {
            return new LuceneCustomResultSet(this, catalogueName, query, skip, take, orderBy);
        }
 
        public override IResultSet Find(string catalogueName, string query, params string[] orderBy)
        {
            return new LuceneCustomResultSet(this, catalogueName, query, orderBy);
        }
    }

4. And the last thing is to implement the custom search result set  : 

public class LuceneCustomResultSet : LuceneResultSet
{
 
     public LuceneCustomResultSet(LuceneSearchProvider provider, string catalogueName, string query, params string[] orderBy)
        : this(provider, catalogueName, query, 0, 0, orderBy)
    {
         
    }
 
     string catalogueName = String.Empty;
     public LuceneCustomResultSet(LuceneSearchProvider provider, string catalogueName, string query, int skip, int take, params string[] orderBy)
         : base(provider, catalogueName, query, 0, 0, orderBy)
     {
         this.catalogueName = catalogueName;
         this.query = query;
     }
 
    protected override void ExecuteQuery(bool tryToFix = true)
    {
        try
        {
            var path = ((LuceneSearchProvider)SearchManager.GetManager("").Provider).GetCataloguePath(catalogueName);
            if (System.IO.Directory.Exists(path))
            {
                var defautlOperator = QueryParser.AND_OPERATOR;
                var analyzer = this.GetAnalyzer();
 
                var parser = new MultiFieldQueryParser(new string[] { "Title", "Content", "TestField" }, analyzer);
                parser.SetDefaultOperator(defautlOperator);
 
                //query = QueryParser.Escape(query);
 
                if (searcher != null)
                    searcher.Close();
 
                searcher = new IndexSearcher(path);
                Query queryObj = searcher.Rewrite(parser.Parse(query));
                Query = queryObj;
 
                //Search only for current language
                if (AppSettings.CurrentSettings.Multilingual == true)
                {
                    TermQuery languageQuery = new TermQuery(new Term("Language", CultureInfo.CurrentUICulture.Name));
                    TermQuery languageNullQuery = new TermQuery(new Term("Language", SearchProvider.FieldNullValue));
 
                    BooleanQuery filterQuery = new BooleanQuery();
                    filterQuery.Add(languageQuery, BooleanClause.Occur.SHOULD);
                    filterQuery.Add(languageNullQuery, BooleanClause.Occur.SHOULD);
 
                    QueryFilter languageFilter = new QueryFilter(filterQuery);
 
                    this.hits = searcher.Search(queryObj, languageFilter);
                }
                else
                {
                    this.hits = searcher.Search(queryObj);
                }
 
                for (var iter = 0; iter < this.HitCount; iter++)
                {
                    var doc = this.hits.Doc(iter);
                }
            }
        }
        catch (Exception ex)
        {
            if (tryToFix && ex is ParseException)
            {
                this.query = this.TryFixQuery(this.query);
                this.ExecuteQuery(false);
            }
            else
            {
                this.error = ex;
                this.hits = null;
            }
        }
    }
 
    private Exception error;
    private string query;
    private IndexSearcher searcher;
    public void Dispose()
    {
        if (this.searcher != null)
        {
            this.searcher.Close();
            this.searcher = null;
        }
    }
}



Note that steps  3 and 4  will be changed at some point to be able to achieve this in less steps and less code. 


The Progress Team