November Happy Hour will be moved to Thursday December 5th.

Manoj Kumawat
Jul 26, 2019
  1961
(5 votes)

Making use of TypeConventionBuilder to include a field with Find

The prerequisite for this post is a basic knowledge of Episerver.Find.

Yesterday I came accross with an issue with Episerver.Find filters. If you've ever faced issues with XhtmlString values with Episerver.Find then this blog post may help you.

I had an XhtmlString as one of the properties within the block as follows - 

[CultureSpecific]
[Display(
 Name = "Title",
 Description = "Title to display.",
 GroupName = SystemTabNames.Content,
 Order = 400
)]
public virtual XhtmlString Title { get; set; }

And I wanted to sort the blocks based on Title (typeof(XhtmlString)) with Find query - 

SearchClient.Instance.Search<MyCustomBlock>()
.For(searchTerm)
.OrderBy(c => c.Title)
.GetContentResult<MyCustomBlock>();

I experienced this wouldn't sort the blocks based on OrderBy Title. Because the string type here would return Xhtml elements and ultimately this would not sort it properly. 

In case if I had the Title as plain string property in block then it should have sorted it as expected. But I didn't like the idea to add any additional field of type string instead of XhtmlString to existing block.

So what should have been done?

Keeping this question in mind, I posted a forum post and as expected this finally put the results in. I can't thank enough to Paul to sort this out for me. 

Here is how it worked

If you could get rid of those HTML elements anyhow within the XhtmlString then it would definitely be a way to go - 

Step 1

Creating an extension method to strip out HTML from XhtmlString - 

 public static class SearchExtensions
    {
        public static string StripHtml(this XhtmlString htmlField)
        {
            return htmlField == null ? string.Empty : EPiServer.Core.Html.TextIndexer.StripHtml(htmlField.ToHtmlString(), 1000);
        }
    }

This is using an existing method TextIndexer class to strip out those HTML elements. Which returns up to 1000 characters (which should be easily sufficient for sorting).

Step 2

Create an InitializableModule that would use TypeConventionBuilder to include a field in Episerver.Find Indexed contents and use the extension method created above - 

[InitializableModule]
    [ModuleDependency((typeof(EPiServer.Web.InitializationModule)))]
    public class InitialiseSearch : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            SearchClient.Instance.Conventions.ForInstancesOf<MyCustomBlock>().IncludeField(x => x.Title.StripHtml());
       
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }

This will include field to find index with the key - Title.StripHtml$$string and this is the key that would do our job done.

Step 3

Modifying Find query to use this extension method - 

....
.OrderBy(c => c.Title.StripHtml())
Step 4

Now you would need to reindex your contents with the scheduled job from Episerver's Admin area - EPiServer Find Content Indexing Job

After you've reindexed, It modifies the way the content is indexed, adding in a field to the content items in the index called "Title.StripHtml$$string". When you search or order on Title.StripHtml(), it doesn't run the extension method, instead it looks for the "Title.StripHtml$$string" field. If you haven't reindexed, that field won't be there.

Verifying the index - 

Go to Episerver Find/overview and click on Explore and then search for your block. This should have the field added to the Find index and look closely it is a plain string now.

Have a great day!

Jul 26, 2019

Comments

Praful Jangid
Praful Jangid Jul 26, 2019 10:21 AM

Good Job Manoj. I appreciate you shared this finding as a blog post. That will help others too and will save their time. :)

Manoj Kumawat
Manoj Kumawat Jul 26, 2019 10:27 AM

Sure thing Praful!

Appreciate your time as well :)

Jul 29, 2019 12:28 PM

I would suggest reading my blog post on GetContent vs GetContentResult https://world.episerver.com/blogs/scott-reed/dates/2019/3/episerver-find---performance-reloading-custom-data-and-getcontent-vs-getcontentresult/. GetContentResult under the covers reloads data using the IContentLoader service to get back fields from IContent. This means that custom data is not reloaded including extension methods, so you'll find when you're querying the HtmlStrip method will be being called for every result again when getting the data back from find which is not ideal from a performance point of view, you just want it to come back from the index where as you've show it's being indexed. I'd suggest projections as the simplest way to get it working then you can switch to GetResult which fully uses everything from the index.

Paul Gruffydd
Paul Gruffydd Jul 29, 2019 05:11 PM

Glad you got it working and good to see you taking the time to write it up.

@Scott - That's a useful tip for situations when you need to retrieve the additional data though in this instance the data provided by the extension method is only used for sorting the results so HtmlStrip() should only be called when the content is being indexed, not when it's being retrieved.

Manoj Kumawat
Manoj Kumawat Jul 30, 2019 07:26 AM

Hey @Scott,

Thanks for pointing that out and a great blog post resource. Pretty much everything I wanted to know. 

However, In this instance as Paul suggested and tested while debugging, It never hits the extension method at the time of data being retrieved. Instead when I am going to index the contents it hits it for every result (I guess this is the moment when it is adding the key for Title in Find Index using extension method). 

Just want to be sure if this won't impact performance?

Jul 30, 2019 08:13 AM

Yes sorry I think Paul is right, this will only affect if the data is coming back. As you've got this in the Sort part this will be evaluated in Elasticsearch. Apologies for the confusion although it's always good to know about these things if you ever need to bring back the custom data.

Thanks, Scott

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog