Surendra Sharma

Surendra Sharma

Search This Blog

Thursday, August 20, 2015

Step by Step guide to implement Lucene Search in Sitecore

Do you want to implement Lucene search in Sitecore and unable to get where to start exactly? Fuck all those theory on internet and refer this straight forward guide.

Rename “\Website\App_Config\IncludeSitecore.ContentSearch.Lucene.Index.Master.config” to “IncludeSitecore.ContentSearch.Lucene.Index.Surendra.config

Open and do the following highlighted config changes. Basically add your template IDs which do you want to show as a search result

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <index id="Surendra" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <!-- This initializes index property store. Id has to be set to the index id -->
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration">
              <include hint="list:IncludeTemplate">
                <Template1>{11111111-2222-3333-4444-123456789123}</Template1>
                <Template2>{11111111-2222-3333-4444-123456789124}</Template2>
                <Template3>{11111111-2222-3333-4444-123456789125}</Template3>
                <Template4>{11111111-2222-3333-4444-123456789126}</Template4>
                <Template5>{11111111-2222-3333-4444-123456789127}</Template5>
              </include>
              <IndexAllFields>true</IndexAllFields>
            </configuration>
            <strategies hint="list:AddStrategy">
              <!-- NOTE: order of these is controls the execution order -->
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync" />
            </strategies>
            <!--<commitPolicyExecutor type="Sitecore.ContentSearch.CommitPolicyExecutor, Sitecore.ContentSearch">
              <policies hint="list:AddCommitPolicy">
                <policy type="Sitecore.ContentSearch.TimeIntervalCommitPolicy, Sitecore.ContentSearch" />
              </policies>
            </commitPolicyExecutor>-->
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>master</Database>
                <Root>/sitecore/content/Surendra/Homepage</Root>
              </crawler>
            </locations>
            <!--
            <shardingStrategy type="Sitecore.ContentSearch.LuceneProvider.Sharding.LucenePartitionShardingStrategy, Sitecore.ContentSearch.LuceneProvider">
              <param desc="shardDistribution">4</param>
            </shardingStrategy>
            -->

            <!--
            <shardFolders hint="raw:AddShardFolderPath">
              <shard shardName="shard1" shardRootFolderPath="c:\Data\Indexes" />
              <shard shardName="shard2" shardRootFolderPath="c:\Data\Indexes" />
              <shard shardName="shard3" shardRootFolderPath="c:\Data\Indexes" />
            </shardFolders>
            -->
          </index>
        </indexes>
      </configuration>
    </contentSearch>
  </sitecore>
</configuration>

Open Sitecore Desktop version -> Control Panel -> Indexing – Indexing Manager -> Checked only “Surendra” Instance -> Next -> Finish [There must be some units processed at this stage.]

Goto IIS and recycle application pool of your site.

Create Search content item and set Search control layout with this content item.

Add below HTML and C# code in this Search page

<asp:TextBox runat="server" ID="txtsearch" ></asp:TextBox>

                <asp:Button runat="server" ID="btnSearch" Text="Search" OnClick="btnSearch_Click" CssClass="searchbtn" />



            <ul>
                <asp:Repeater ID="rptEvents" runat="server">
                    <ItemTemplate>
                        <asp:HyperLink NavigateUrl="<%# Sitecore.Links.LinkManager.GetItemUrl((Item)Container.DataItem) %>" runat="server">
                            <li>
                                <strong>
                                    Title
                                   Write your HTML code here
                                </strong>
                            </li>
                        </asp:HyperLink>

                    </ItemTemplate>
                </asp:Repeater>
            </ul>

using Lucene.Net.Analysis.Standard;
using Lucene.Net.Analysis;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Data.Items;

        /// <summary>
    /// Search Page
    /// </summary>
    public partial class Search : System.Web.UI.UserControl
    {
        #region Properties

        public string LeftPartClass { get; set; }

        private int RowCount
        {
            get
            {
                int result = 0;
                if (ViewState["RowCount"] != null)
                {
                    result = (int)ViewState["RowCount"];
                }
                return result;
            }
            set
            {
                ViewState["RowCount"] = value;
            }
        }

       
        #endregion

        #region Page Load

        /// <summary>
        /// Page Load
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            LeftPartClass = "Show";

            if (!IsPostBack)
            {

                RowCount = 0;


                if (Request.QueryString != null && Request.QueryString.Count > 0 && !string.IsNullOrEmpty(Request.QueryString["q"]))
                {
                    Session["q"] = Request.QueryString["q"].Trim();
                }

                SearchContent(Session["q"], 10, 0);
            }
            else
            {
                plcPagingTop.Controls.Clear();
                plcPagingBottom.Controls.Clear();
                CreatePagingControl();
            }   

        }

        #endregion

        #region ASP.NET Event

        /// <summary>
        /// Search button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void btnSearch_Click(object sender, EventArgs e)
        {
            Session["q"] = txtsearch.Text.Trim();

            Response.Redirect(Request.UrlReferrer.AbsolutePath);
        }

        #endregion

        #region Paging Section

        /// <summary>
        /// Create Page number
        /// </summary>
        private void CreatePagingControl()
        {
            //Get total pages
            int totalPage = (RowCount / 10) + 1;

            //Check initiliase boundry coditions
            if (totalPage == 1)
            {
                totalPage = 0;
            }

            //Check last boundry coditions
            if(RowCount % 10 == 0)
            {
                totalPage = RowCount / 10;
            }

            GeneratePageNumber(totalPage, "Top", plcPagingTop);
            GeneratePageNumber(totalPage, "Bottom", plcPagingBottom);
        }

        /// <summary>
        /// Generate page number for all pager
        /// </summary>
        /// <param name="totalPage"></param>
        /// <param name="pagerLocation"></param>
        /// <param name="placeHolder"></param>
        private void GeneratePageNumber(int totalPage, string pagerLocation, PlaceHolder placeHolder)
        {
            for (int iCounter = 0; iCounter < totalPage; iCounter++)
            {
                LinkButton lnk = new LinkButton();
                lnk.Click += new EventHandler(lbl_Click);
                lnk.ID = "lnkPage" + pagerLocation + (iCounter + 1).ToString();
                lnk.Text = (iCounter + 1).ToString();
               
                placeHolder.Controls.Add(lnk);
               
                Label spacer = new Label();
                spacer.ID = "lblSpace" + pagerLocation +(iCounter + 1).ToString();
                spacer.Text = "&nbsp;";
                placeHolder.Controls.Add(spacer);
            }
        }

        /// <summary>
        /// Show selected page number search
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void lbl_Click(object sender, EventArgs e)
        {
            LinkButton lnk = sender as LinkButton;
            int currentPage = int.Parse(lnk.Text);
            hdnPageNum.Value = currentPage.ToString();
            int take = currentPage * 10;
            int skip = currentPage == 1 ? 0 : take - 10;
            SearchContent(Session["q"], take, skip);
        }

        #endregion

        #region Search Block

        /// <summary>
        /// get search content and render accordingly
        /// </summary>
        /// <param name="input"></param>
        private void SearchContent(string input, int take, int skip)
        {
            if (string.IsNullOrEmpty(input))
            {
                if (Request.QueryString != null && Request.QueryString.Count > 0 && !string.IsNullOrEmpty(Request.QueryString["q"]))
                {
                    Session["q"] = Request.QueryString["q"].Trim();
                    input = Request.QueryString["q"].Trim();
                }
                else
                {
                    LeftPartClass = "Hide";
                    return;
                }
            }

            ltMessage.Text = string.Format("0 results found for <strong>\"{0}\"</strong>", input);

            string textToSearch = input;
            List<string> terms = TokenizeByStandardAnalyzer(textToSearch);

            ISearchIndex index = Sitecore.ContentSearch.ContentSearchManager.GetIndex("Surendra");

            using (IProviderSearchContext context = index.CreateSearchContext())
            {
                var expression = BuildSearchExpression(terms);

                var query = context.GetQueryable<SearchResultItem>().Where(expression);

                List<SearchResultItem> results = query.ToList();

                if (results.Count > 0)
                {
                    var searchItemList = results.Select(x => Sitecore.Context.Database.GetItem(x.ItemId)).Select(x => new
                    {
                        DisplayName = x.DisplayName,
                        ItemURL = Sitecore.Links.LinkManager.GetItemUrl(x)
                    }).Distinct().OrderBy(x=> x.DisplayName).ToList();

                    int totalRecords = searchItemList.Count();

                    var pagingRecords = from p in searchItemList.ToList()
                       .Take(take)
                       .Skip(skip)
                                        select p;

                    PagedDataSource page = new PagedDataSource();
                    page.AllowCustomPaging = true;
                    page.AllowPaging = true;
                    page.DataSource = pagingRecords;
                    page.PageSize = 10;

                    rptEvents.DataSource = page;
                    rptEvents.DataBind();

                    if (!IsPostBack)
                    {
                        RowCount = totalRecords;
                        CreatePagingControl();
                    }

                    int toRecords = (totalRecords < take) ? totalRecords : take;
                    ltMessage.Text = string.Format("<strong>{0}-{1}</strong> of about <strong>{2}</strong> results found for <strong>\"{3}\"</strong>",
                        skip + 1, toRecords, totalRecords, input);

                }
            }
        }

        /// <summary>
        /// Builds a search Expression for querying the search index
        /// </summary>
        /// <param name="terms">IEnumerable List of search terms</param>
        /// <returns></returns>
        System.Linq.Expressions.Expression<Func<Sitecore.ContentSearch.SearchTypes.SearchResultItem, bool>> BuildSearchExpression(IEnumerable<string> terms)
        {
            var expression = Sitecore.ContentSearch.Linq.Utilities.PredicateBuilder.False<SearchResultItem>();
            return terms.Aggregate(expression, (current, term) => Sitecore.ContentSearch.Linq.Utilities.PredicateBuilder.Or(current, item => item.Content.Contains(term) || item.Name.Contains(term)));
        }

        /// <summary>
        /// Tokenizes the search term string in order to allow multi term search
        /// </summary>
        /// <param name="fieldValue">Search Field Value</param>
        /// <returns></returns>
        List<string> TokenizeByStandardAnalyzer(string fieldValue)
        {
            var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
            TokenStream stream = analyzer.TokenStream("dummy", new System.IO.StringReader(fieldValue));
            var attribute = stream.AddAttribute<Lucene.Net.Analysis.Tokenattributes.ITermAttribute>();
            var list = new List<string>();
            while (stream.IncrementToken())
            {
                string term = attribute.Term;
                list.Add(term);
            }
            stream.End();
            return list;
        }

        #endregion

    }

Please leave your comments or share this tip if it’s useful for you.