The Episerver Find API is a powerful tool that offers capabilities above and beyond standard search providers. One of the areas that it excels is its flexibility in implementing facets. In particular, we are going to take a look at how you can use Find to index commerce dictionaries so that facet management can be handled by authors through the built in UI without the need for editing XML files.
This post will be utilizing concepts that are covered in a previous post. So you may want to take a quick look at that material before continuing.
Traditional Search Configuration
Before we dig too far into the details, let’s review the traditional Episerver Commerce search provider model. I highly recommend downloading Episerver’s Quicksilver site to follow along if you do not have it already.
Open up the Episerver.Reference.Commerce.Site project:
- Configs/Mediachase.Search.config: Declares the type of search provider the site will use along with any custom indexers that will be used.
- Configs/Mediachase.Search.Filters.config: XML file that declares all search filter groups and individual filter values.
- Infrastructure/Indexing/CatalogIndexer.cs: Not required, but demonstrates how custom indexing can be utilized to add additional document data for items.
As you can see, the traditional model is declarative. This model works very well for commerce sites that have a fairly static set of facets that do not require constant adjustment. But what happens if you are managing a site in which new values are constantly being added or edited? This can be problematic because any edits will require an update to the filters.config file and additional deployments. Furthermore, it will likely require both a programmer to handle the solution edits and an administrator to direct what content is changing. There has to be a better way!
Dictionaries in Commerce
Dictionaries are extremely useful collections in Episerver Commerce. They allow you to define a pool of possible options for a field. But unlike most elements within Commerce, they can be managed directly within the CMS interface. They have the same advantages programatically as an enumeration. However, unlike an enumeration the values are not stored in code.
Dictionary properties can make use of a few different backing types that are straightforward once you determine the way you want authors to manage the data.
PropertyDictionarySingle: Use this type if you want a single selection to be made. It is presented in the form of a dropdown list when editing.
PropertyDictionaryMultiple: This is used when any number of selections can be made. It is presented by default as a collection of checkboxes to the author.
For the purposes of this demo we are going to use 2 fields to demonstrate each.
[Searchable] [CultureSpecific] [Tokenize] [IncludeInDefaultSearch] [Display(Name = "Brand", Order = 100)] [BackingType(typeof(PropertyDictionarySingle))] public virtual string Brand { get; set; } [Searchable] [IncludeInDefaultSearch] [BackingType(typeof(PropertyDictionaryMultiple))] [Display(Name = "Taste", Order = 200)] public virtual ItemCollection<string> Taste { get; set; }
Declaring Facets
Now that we have our dictionary fields declared, we need to let our search query know what fields we would like to be included as facets with our search results. Since both of our properties are strings, we can make use of terms facets by using the TermsFacetFor method.
var results = SearchClient.Instance.Search<BeverageVariation>() .TermsFacetFor(x => x.Brand()) .TermsFacetFor(x => x.Taste());
A few things:
- Note that we have not executed a search query. We have just declared the facets we would like our results to include once the query is executed. We need to do this since we are not relying on the filters config file to declare our facets.
- Both the Brand and Taste properties are making use of projection methods in this example. If you are using a property that is native to the type of object you are searching this is not necessary. The property can be used directly.
- As you can see there are multiple chains that can be included for as many facets are needed. These can also include other facet types as referenced in the official documentation.
Filtering Results
We can now wrap up this example by using our facets as actual filters to narrow search results.
var brand = "Pepsi"; var brandFilter = query.Filter(x => x.Brand().MatchCaseInsensitive(brand)); var brandResults = brandFilter.Select(x => x.Brand()).GetResult(); var taste = "Sweet"; var tasteFilter = query.Filter(x => x.Taste().MatchCaseInsensitive(taste)); var brandResults = tasteFilter.Select(x => x.Taste()).GetResult();
The above is a simplified version of a real world example. In practice you will want to be passing the brand and taste variables as actual facet key values instead of using constants.
Conclusion
This is what brings the dynamic aspect of using dictionaries as facets with Find full circle. Programmers can assign variables as dictionary properties fields and authors can add & edit the values as needed. Indexing of these fields will occur just as it would using the provider model, but we no longer have to declare the facet values statically or worry about managing an XML file.
This is only scratching the surface of what is possible through the Find API. But if you have struggled with maintaining search facets in the past, I encourage you to give the API a shot. You may find that it provides you some additional options to tailor your applications search needs.
Hi John,
Nice post!
Do you have plans to take this a step further?
Now you have dynamic facet values, are you thinking about a blog post explaining fully dynamic facets with Episerver find?
Kind regards,
Sam
Hi Sam,
Thanks for the reply! Can you clarify what you mean by fully dynamic facets? Are you referring to facets in which the values are programmatic as opposed to a value that is administered? I can definitely expand upon this post or create a similar topic. But want to make sure I understand the topic you are requesting.