Per Magne Skuseth
Jan 14, 2014
  4489
(6 votes)

Unified search and culture specific MediaData

I was creating a search page for a customer using EPiServer Find. As they have plenty of page types, I decided to use unified search. However, they also have a lot of documents (PDFs) that should also be searchable .No problem, unified search takes care of that out of the box. But this site also had several languages, and since MediaData in EPiServer 7.5 is not localizable, I had to write some code as Find has no way of knowing which language the different files should be part of. Here is what I did:
   
First, a string property should be added to a MediaData base class, which will store the language ID.

[ContentType(GUID = "EE3BD195-7CB0-4756-AB5F-E5E223CD9820")]
public class GenericMedia : MediaData
{
    [SelectOne(SelectionFactoryType = typeof(LanguageSelectionFactory))]
    public virtual string LanguageBranch { get; set; }
}


To make it more editor friendly,  the SelectOne attribute could be used on the property(Check out Linus’s blog post for more information regarding the SelectOne attribute). In this case it uses a selection factory that returns all available languages on the site:

public class LanguageSelectionFactory : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        // Get all the languages branches
        IList<LanguageBranch> languageBranches =
            ServiceLocator.Current.GetInstance<ILanguageBranchRepository>().ListEnabled();
        foreach (var branch in languageBranches)
        {
            // and then return them as SelectItem
            yield return new SelectItem()
            {
                Text = branch.Name,
                Value = branch.LanguageID
            };
        }
        // finally, return an option which will be used to make the MediaData searchable across all languages
        yield return new SelectItem()
        {
            Text = "All languages",
            Value = null
        };
    }
}

SNAGHTML708c224

Neato!

Then, an extension method that will use the language property and do the appropriate filtering. The extension method should only filter non-localizable items, so that it will not affect pages.

public static FilterExpression<ISearchContent> ApplyMediaLanguageFilter(this ISearchContent content)
{
    var currentLanguage = ContentLanguage.PreferredCulture.Name;
    return new FilterExpression<ISearchContent>(x => x.MatchTypeHierarchy(typeof(ILocalizable))
                                         | ((GenericMedia)x).LanguageBranch.Match(currentLanguage) 
                                         | !((GenericMedia)x).LanguageBranch.Exists());
}


And finally, use the extension method in the Find query

var query = SearchClient.Instance
    .UnifiedSearchFor(q)
    .Filter(x => x.ApplyMediaLanguageFilter())

That’s it! Culture specific file search is now working. Setting the languages property on the files will be required though.

 

Related super quick tip:In this case I also saw the need to remove image files from the result

.Filter(x => !x.MatchTypeHierarchy(typeof(ImageData)))
Jan 14, 2014

Comments

Jan 14, 2014 11:44 PM

Nice one.

I prefer to search over all languages though, but then maybe have filters for languages instead. I mean, it's more important to find what your're searching for, then get results it in the same language as the visiting page.

Per Magne Skuseth
Per Magne Skuseth Jan 15, 2014 05:58 AM

I guess that depends on the content and the customer, Johan. But I do agree that having filters for languages is preferable in a lot of cases. With that in mind,the extension could easily be modified to accept a language parameter instead of using the current language

Marcus Granström
Marcus Granström Jan 15, 2014 09:23 AM

Nice post. Thanks for sharing.

Sven-Erik Jonsson
Sven-Erik Jonsson Feb 10, 2015 03:31 PM

Don't really know if this has been added later, but EPiServer contains a SelectionFactory that almost replicates this identically

inside EPiServer.Cms.Shell.UI.ObjectEditing.EditorDescriptors.SelectionFactories.
Instead of creating your own "LanguageSelectionFactory", for reduced type ambiguity, I would recommend overriding the existing and

adding an "all languages" option like below.

public override IEnumerable GetSelections(ExtendedMetadata metadata)
{
yield return new SelectItem() { Text = "All languages", Value = null };
var selections = base.GetSelections(metadata);
if (selections == null) yield break;
foreach (var selection in selections)
{
yield return selection;
}
}

Of course naming it something different than the existing one.

Please login to comment.
Latest blogs
Opti ID overview

Opti ID allows you to log in once and switch between Optimizely products using Okta, Entra ID, or a local account. You can also manage all your use...

K Khan | Jul 26, 2024

Getting Started with Optimizely SaaS using Next.js Starter App - Extend a component - Part 3

This is the final part of our Optimizely SaaS CMS proof-of-concept (POC) blog series. In this post, we'll dive into extending a component within th...

Raghavendra Murthy | Jul 23, 2024 | Syndicated blog

Optimizely Graph – Faceting with Geta Categories

Overview As Optimizely Graph (and Content Cloud SaaS) makes its global debut, it is known that there are going to be some bugs and quirks. One of t...

Eric Markson | Jul 22, 2024 | Syndicated blog

Integration Bynder (DAM) with Optimizely

Bynder is a comprehensive digital asset management (DAM) platform that enables businesses to efficiently manage, store, organize, and share their...

Sanjay Kumar | Jul 22, 2024

Frontend Hosting for SaaS CMS Solutions

Introduction Now that CMS SaaS Core has gone into general availability, it is a good time to start discussing where to host the head. SaaS Core is...

Minesh Shah (Netcel) | Jul 20, 2024

Optimizely London Dev Meetup 11th July 2024

On 11th July 2024 in London Niteco and Netcel along with Optimizely ran the London Developer meetup. There was an great agenda of talks that we put...

Scott Reed | Jul 19, 2024