Indexing Blocks with EPiServer Search

This week I needed to add Blocks used on a page to EPiServer Search Index as content of the page they were used on. This is supported in Find, but as we are not using Find in this project, I needed a different solution.

First I added an extra property to my base class to hold the content of my Blocks. Note the ScaffoldColumn(false) attribute, which hides it in edit mode.

        [CultureSpecific]
        [ScaffoldColumn(false)]
        [Searchable]
        public virtual string SearchText { get; set; }

Next I created an InitializableModule to attach some functionality to the publishing event

  [ModuleDependency(typeof(InitializationModule))]
    public class SearchInitialization : IInitializableModule
    {
        
        #region Public Methods and Operators

        public void Initialize(InitializationEngine context)
        {
            DataFactory.Instance.PublishingPage += this.OnPublishingPage;
        }

        /// <summary> Raises the page event. </summary>
        /// <param name="sender"> The sender. </param>
        /// <param name="pageEventArgs"> Event information to send to registered event handlers. </param>
        public void OnPublishingPage(object sender, PageEventArgs pageEventArgs)
        {
            StandardPage page = pageEventArgs.Page as StandardPage;

            if (page == null)
            {
                return;
            }

            ContentArea contentArea = page.MainContentArea;

            if (contentArea == null)
            {
                return;
            }

            StringBuilder stringBuilder = new StringBuilder();

            foreach (ContentAreaItem contentAreaItem in contentArea.Items)
            {
                IContent blockData = contentAreaItem.GetContent();

                IEnumerable<string> props = GetSearchablePropertyValues(blockData, blockData.ContentTypeID);

                stringBuilder.AppendFormat(" {0}", string.Join(" ", props));
            }

            page.SearchText= stringBuilder.ToString();
        }

        public void Preload(string[] parameters)
        {
        }

        public void Uninitialize(InitializationEngine context)
        {
            DataFactory.Instance.PublishingPage -= this.OnPublishingPage;
        }

        #endregion

        #region Methods

        private IEnumerable<string> GetSearchablePropertyValues(IContentData contentData, ContentType contentType)
        {
            if (contentType == null)
            {
                yield break;
            }

            foreach (PropertyDefinition current in
                from d in contentType.PropertyDefinitions
                where d.Searchable || typeof(IPropertyBlock).IsAssignableFrom(d.Type.DefinitionType)
                select d)
            {
                PropertyData propertyData = contentData.Property[current.Name];
                IPropertyBlock propertyBlock = propertyData as IPropertyBlock;
                if (propertyBlock != null)
                {
                    foreach (
                        string current2 in
                            this.GetSearchablePropertyValues(
                                propertyBlock.Block,
                                propertyBlock.BlockPropertyDefinitionTypeID))
                    {
                        yield return current2;
                    }
                }
                else
                {
                    yield return propertyData.ToWebString();
                }
            }

            yield break;
        }

        private IEnumerable<string> GetSearchablePropertyValues(IContentData contentData, int contentTypeID)
        {
            IContentTypeRepository contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            return this.GetSearchablePropertyValues(contentData, contentTypeRepository.Load(contentTypeID));
        }

        #endregion
    }

So, what’s happening here?

  • When publishing the page, I loop through all Blocks in the MainContentArea, also defined on my base class.
  • I get all values from string  properties marked "Searchable" in the Blocks and add them to the SearchText property on my base class. Note: If for some reason you don’t want a property on a Block indexed, just add [Seachable(false)] to it.
  • The value of SearchText property is added to the index and you get results also if the term you are looking for is only used in a Block on the page.

The two helper classes are from the "EPiServer.Core.ContentSearchHandler".

That’s it.

7 thoughts on “Indexing Blocks with EPiServer Search

  1. Hi there!

    I’m trying to implement this solution into my episerver site using the Alloy templates, but I keep getting an error on line 66:

    Error 81 Could not find an implementation of the query pattern for source type ‘EPiServer.DataAbstraction.PropertyDefinitionCollection’. ‘Where’ not found. Are you missing a reference or a using directive for ‘System.Linq’?

    Any idea what’s causing this error?

    Thanks in advance.

    Like

    1. Hi Emil,
      Do you have a reference to Linq in your project and a using in your class?
      You should have at least these:

      using EPiServer.Core;
      using EPiServer.DataAbstraction;
      using EPiServer.Framework;
      using EPiServer.Framework.Initialization;
      using EPiServer.ServiceLocation;
      using EPiServer.SpecializedProperties;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Text.RegularExpressions;

      Grtz,
      Jeroen

      Like

Leave a Reply to Jeroen Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s