Those of you who know me a bit better know that I hate doing the same thing over and over again.
When you do a TypedSearch in EPiServer Find you will have to add your facets to the query. If you have multiple types you might end up with a switch, separate queries for different types, … So I wanted a solution where I can add an attribute to a property and add a facet to the query for each property that has the attribute. This way I can write a generic TypedSearch query.
So here’s how you could do that:
/// <summary> /// Adds facets to a Find query base on an attribute. /// </summary> /// <typeparam name="T">The content type to add the facets for.</typeparam> /// <param name="query">The query.</param> /// <returns>ITypeSearch<T>.</returns> public static ITypeSearch<T> AddFacets<T>(this ITypeSearch<T> query) where T : IContent { foreach (PropertyInfo propertyInfo in GetPropertiesSortedByIndex<T>()) { FacetAttribute facetAttribute = Attribute.GetCustomAttribute(propertyInfo, typeof(FacetAttribute)) as FacetAttribute; if (facetAttribute == null) { continue; } ParameterExpression expParam = Expression.Parameter(typeof(T), "x"); MemberExpression expProp = Expression.Property(expParam, propertyInfo); Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); dynamic expression = Expression.Lambda(delegateType, expProp, expParam); try { switch (facetAttribute.FacetType) { case FacetType.TermsFacetFor: query = TypeSearchExtensions.TermsFacetFor(query, expression); break; case FacetType.TermsFacetForWordsIn: query = TypeSearchExtensions.TermsFacetForWordsIn(query, expression); break; default: query = TypeSearchExtensions.TermsFacetFor(query, expression); break; } } catch (Exception) { } } return query; }
This extension gets the properties that has the FacetAttribute added to it and orders them by the Index set on it.
It then creates a dynamic Lambda expression based on Property Info and adds a TermsFacet to the query, the type can be set on the attribute as well.
So you end up with
IClient searchManager = Client.CreateFromConfig(); ITypeSearch<StandardPage> queryTest = searchManager.Search<StandardPage>().For("test"); queryTest = queryTest.AddFacets(); IContentResult<StandardPage> results = queryTest.GetContentResult();
which is a bit shorter then some of the queries I created.
The complete code can be found here
“Those of you who know me a bit better know that I hate doing the same thing over and over again.” -> Love the approach! 🙂
LikeLike
The next challenge is to make a filter for the facets in the same way. Or maybe the approach gets picked up by Find and will be built in 😉
LikeLike
Hi, thank you for very useful nice post! I was also looking for a way to get around the repeating switch approach… I extended the solution with a method to retrieve the facets from the results and I also added a size constructor to the attribute to get more than the default 10. Maybe could be of interest. /Nino
LikeLike
Cool, would like to see that
LikeLike
I made a fork from your gist here: https://gist.github.com/Ninodeluxe/5d1507a35b8f796ddbbb3535d7f57475
LikeLiked by 1 person
Awesome. Any idea on how this can be applied on non-property values by using “.IncludeField”?
LikeLike
For conventions? I never thought about it, but as it uses an attribute to select, it would be kinda hard for non-property values
LikeLike
Imagine that you have:
[ContentType(DisplayName = “DummyContent”)]
public class DummyContent : PageData
{
public IEnumerable IndustryList()
{
IList industryTags = IndustryTags.SplitByComma().ToList();
foreach (ContentAreaItem contentAreaItem in HowItWorksData.CaseStoryAssets.FilteredItems)
{
MarketplaceAssetBaseBlock assetBlock = contentAreaItem.GetContent() as MarketplaceAssetBaseBlock;
if (assetBlock == null)
{
continue;
}
industryTags.Add(assetBlock.Industry);
}
return industryTags.Distinct();
}
}
then:
clientConventions.ForInstancesOf().IncludeField(x => x.IndustryList());
and finally this:
.TermsFacetFor(facets => facets.IndustryList(), facet => facet.Size = facetSize)
We could potentially expend AddFacets to look for methods as well as properties?
Best
Miroslav
LikeLike
I am not a big fan of having logic like this in your model, you can easily turn it into an extension method and have Find index it that way. This method could also be rewritten as a readonly property though, so you would not need to extend AddFacets . That being said, I guess you can always look for methods with the attribute. Haven’t tried it though
LikeLike