Category Archives: EPiServer Find

Modify search in navigation

Searching in the navigation bar with multiple sites, multiple languages and a lot of content rarely gives you the result you want.  Or.. To be honest. Just having the same name on two pages gives you headache. Luckily we have the power to change that!

Two pages with the same name. Tooltip isn’t very helpful.

As you can see, we have two pages with the same name. I know that the page I’m looking for is located somewhere under Campaigns, but which one is it?

Since the search is provider based, we can create our own search provider and tell EPiServer to use that instead. To create your own search provider, all you have to do is implement the interface ISearchProvider and decorate your class with the SearchProvider attribute.

In this case, all we want to do is modify the output of the search, therefore we will just extend the search being used. In this example we will extend the EPiServer Find search provider, but the code will probably work on the EPiServer Search provider as well.

To do this, we create a new class called CustomPageSearchProvider, this class will extend EPiServer.Find.Cms.SearchProviders.EnterprisePageSearchProvider. To get our provider an unique name we need to override Category and give it our name.

Now build your project and navigate to Admin \ Config \ Search Configuration. If everything works as expected you should see your Custom Pages Search there. Enable your newly created provider, and make sure that it’s above the other pages search providers, or disable them.

Here we can decide which search providers to use, and also set in which order they are executed.

Now it’s time to modify the search result before sending it back to the client. Let’s start by overriding Search(Query query). We still want to use the search functionality from EPiServer Finds search, so we execute base.Search(query) into a search result. The result will contain a list with SearchResult. This class contains some properties like Title, Url and also some metadata containing the Id of the hit. We want to modify the Title of the SearchResult to it will contain information of where the page is located in the structure. To do that, we get all ancestors of the content, reverse that list, skip the first two entries (we don’t want the root or start page), and finally we format the title as we want to!

And now we are done! Build you project and head to edit mode and try the search!

Now we can see where the page is located. The text will be truncated, but the tooltip still contains the full path.

Convention for EPiServer Find to ignore large files

EPiServer Find has a limit when indexing files (last I heard this was 50MB), but nothing really happens if you try to index a large files except that you get an error message from the indexing job. But if you have some very large files, you might bump in to performance problems on the server. Therefore it might be a good idea to add a convention that ignores large files.

In this case we will save the files size on the media and then check this property in the convention.

First we add a property to the media type. In this case we have a media type named GenericMedia that inherits from MediaData.

[ContentType(GUID = "EE3BD195-7CB0-4756-AB5F-E5E223CD9820")]
public class GenericMedia : MediaData
{
public virtual int FileSizeInKb { get; set; }
}

 

After that, we create a startup module named ContentInitialization that listens to the CreatingContent event. In the creating content event, we find out how big the file is, and saves it to the property we just created.

[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class ContentInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var eventRegistry = ServiceLocator.Current.GetInstance();
eventRegistry.CreatingContent += OnCreatingContent;
}

private static void OnCreatingContent(object sender, ContentEventArgs e)
{
var content = e.Content as GenericMedia;
if (content != null)
{
var path = (content.BinaryData as FileBlob).FilePath;
var length = new FileInfo(path).Length;
content.FileSizeInKb = (int)(length / 1024);
}
}

public void Preload(string[] parameters) {}
public void Uninitialize(InitializationEngine context) {}
}

 

Now the only thing missing is the convention. For this we create another module named FindInitialization and add the convention there. The reason for creating several startup modules is both for code readability, but they might also have different dependencys to other modules. In this case we only let files smaller than 20000kb to be indexed.

[ModuleDependency(typeof(EPiServer.Find.Cms.Module.IndexingModule))]
public class FindInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
ContentIndexer.Instance.Conventions.ForInstancesOf().ShouldIndex(x => (x.FileSizeInKb < 20000));
}

public void Uninitialize(InitializationEngine context) {}
public void Preload(string[] parameters) {}
}

Some tips if you run into problem

The schedule job fails with thread was being aborted
Make sure that there is no resource limit on the application pool or that the server runs out of memory. When indexing a lot of content, the server will use a lot of CPU and memory.

EPiServer Find tells me that “The remote server returned an error: (413) Request Entity Too Large. Request entity too large.” but I have no file large files.
When the schedule job runs, it sends content in batches, and it seems like a batch cannot be larger than 50MB(?). You can set the size of a batch by

ContentIndexer.Instance.MediaBatchSize = somesize; // Not sure if this is in use anymore though
ContentIndexer.Instance.ContentBatchSize = somesize;

But if you have a decent amount of content, this will fail due to many requests.