I am working on a search feature that adds different search facets to the search page depending on where the user is searching in a product catalog. For example, if the user is searching over all products, the user might want to filter by manufacturer. But if the user is searching the products of just a single manufacturer, they don't need a manufacturer filter and might want to filter by brand instead.
To try to reuse code and not write dozens of functions for all possible filters, I have created a generic function to get facets for each filter:
public Dictionary GetFilterCounts(IContentResult results, Expression> fieldSelector)
Dictionary facetResults = new Dictionary();
var facetItems = results.TermsFacetFor(fieldSelector);
foreach (var item in facetItems)
facetResults.Add(item.Term.Replace('-', ' '), item.Count);
However, the facets for each starting catalog node are stored as a list of strings on the node content. To get from the string property name to the fieldSelector that the TermsFacetFor function needs I am trying to use System.Reflection but I cannot find a way to make it get the property in a way that TermsFacetFor can use. Here is a list of what I've tried:
var facetResult = search.GetFilterCounts(dataList, s => s.GetType().GetProperty(facet.Name).GetGetMethod());
var facetResult = search.GetFilterCounts(dataList, s => s.GetType().GetProperty(facet.Name));
Both of these have resulted in null being returned from the TermsFacetFor call. Is what I'm trying even possible? If so, how can I manage it?
Not sure I understand fully what you're trying to do but why not only show filtering groups that have more than one option found in the current resultset?
Hi Johan, thanks for your reply. I forgot that I had posted this and figured out the issue in the meantime. It was less of an Epi issue than it was my improper use of System.Reflection. I'll post my solution here so that it might help anyone else trying to figure this out.
My new generic ApplyFacet call:
this.search.ApplyFacet(ExpressionBuilder.Build<SiteProductVariation, string>(facet.Name), 1000);
This uses the following reflection code to build a lambda just as you normally would if you knew the property at compile time. All I pass is the string name of the property.
public static class ExpressionBuilder
public static Expression<Func<TClass, TProperty>> Build<TClass, TProperty>(string fieldName)
var param = Expression.Parameter(typeof(TClass), "x");
var field = Expression.PropertyOrField(param, fieldName);
return Expression.Lambda<Func<TClass, TProperty>>(field, param);
Here is the final ApplyFacet call, for extra clarity:
public void ApplyFacet(Expression<Func<SiteProductVariation, string>> fieldSelector, int size)
this.EpiFindSearch = this.EpiFindSearch.TermsFacetFor(fieldSelector, r => r.Size = size);
With this, I can ask for any search facet I want by adding the facet name as a property to a catalog node in Epi, it's quite useful!