Read my blog here
Last active
January 5, 2023 14:07
-
-
Save jstemerdink/208cba7eb16b4589930f740804e2c24b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Class FacetAttribute. This class cannot be inherited. | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Property)] | |
public sealed class FacetAttribute : Attribute | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="FacetAttribute"/> class. | |
/// </summary> | |
/// <param name="index">The index.</param> | |
/// <param name="amount">The amount.</param> | |
/// <param name="facetType">Type of the facet.</param> | |
public FacetAttribute(int index, int amount, FacetType facetType) | |
{ | |
Index = index; | |
Amount = amount; | |
FacetType = facetType; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="FacetAttribute"/> class. | |
/// </summary> | |
/// <param name="index">The index.</param> | |
/// <param name="facetType">Type of the facet.</param> | |
public FacetAttribute(int index, FacetType facetType) | |
{ | |
Index = index; | |
Amount = 10; | |
FacetType = facetType; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="FacetAttribute"/> class. | |
/// </summary> | |
/// <param name="index">The index.</param> | |
public FacetAttribute(int index) | |
{ | |
Index = index; | |
Amount = 10; | |
FacetType = FacetType.TermsFacetFor; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="FacetAttribute"/> class. | |
/// </summary> | |
public FacetAttribute() | |
{ | |
Index = 0; | |
Amount = 10; | |
FacetType = FacetType.TermsFacetFor; | |
} | |
/// <summary> | |
/// Gets the index. | |
/// </summary> | |
/// <value>The index.</value> | |
public int Index { get; } | |
/// <summary> | |
/// Gets the amount of facets to return. | |
/// </summary> | |
/// <value>The amount.</value> | |
public int Amount { get; } | |
/// <summary> | |
/// Gets the type of the facet. | |
/// </summary> | |
/// <value>The type of the facet.</value> | |
public FacetType FacetType { get; } | |
/// <summary> | |
/// Gets a value indicating whether the property is used as a facet in EPiServer Find. | |
/// </summary> | |
/// <value><c>true</c> if [used as a facet in EPiServer Find]; otherwise, <c>false</c>.</value> | |
public static bool Facet | |
{ | |
get | |
{ | |
return true; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Concurrent; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using EPiServer.Find; | |
using EPiServer.Find.Api.Facets; | |
public static class FacetExtensions | |
{ | |
/// <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>The ITypeSearch{T} with facets.</returns> | |
public static ITypeSearch<T> AddFacets<T>(this ITypeSearch<T> query) | |
where T : IContent, new() | |
{ | |
IEnumerable<PropertyInfo> propertyInfoList = GetFacetedPropertiesSortedByIndex<T>(); | |
foreach (PropertyInfo propertyInfo in propertyInfoList) | |
{ | |
try | |
{ | |
FacetAttribute facetAttribute = | |
Attribute.GetCustomAttribute(element: propertyInfo, typeof(FacetAttribute)) as FacetAttribute; | |
if (facetAttribute == null) | |
{ | |
continue; | |
} | |
ParameterExpression expParam = Expression.Parameter(typeof(T), "x"); | |
MemberExpression expProp = Expression.Property(expression: expParam, property: propertyInfo); | |
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); | |
dynamic expression = Expression.Lambda(delegateType: delegateType, body: expProp, expParam); | |
switch (facetAttribute.FacetType) | |
{ | |
case FacetType.TermsFacetFor: | |
query = TypeSearchExtensions.TermsFacetFor( | |
query, | |
expression, | |
FacetRequestActionForField(propertyInfo: propertyInfo, size: facetAttribute.Amount)); | |
break; | |
case FacetType.TermsFacetForWordsIn: | |
query = TypeSearchExtensions.TermsFacetForWordsIn(query, expression, facetAttribute.Amount); | |
break; | |
default: | |
query = TypeSearchExtensions.TermsFacetFor( | |
query, | |
expression, | |
FacetRequestActionForField(propertyInfo: propertyInfo, size: facetAttribute.Amount)); | |
break; | |
} | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
return query; | |
} | |
/// <summary> | |
/// Gets the facet values. | |
/// </summary> | |
/// <typeparam name="T">The content type to retrieve the facet values for.</typeparam> | |
/// <param name="contentResult">The content result.</param> | |
/// <returns>A Dictionary{System.String, IEnumerable{TermCount}}.</returns> | |
public static Dictionary<string, IEnumerable<TermCount>> GetFacetValues<T>( | |
this IHasFacetResults<T> contentResult) | |
where T : IContent, new() | |
{ | |
ConcurrentDictionary<string, IEnumerable<TermCount>> facetResults = new(); | |
IEnumerable<PropertyInfo> propertyInfoList = GetFacetedPropertiesSortedByIndex<T>(); | |
foreach (PropertyInfo propertyInfo in propertyInfoList) | |
{ | |
try | |
{ | |
FacetAttribute facetAttribute = | |
Attribute.GetCustomAttribute(element: propertyInfo, typeof(FacetAttribute)) as FacetAttribute; | |
if (facetAttribute == null) | |
{ | |
continue; | |
} | |
ParameterExpression expParam = Expression.Parameter(typeof(T), "x"); | |
MemberExpression expProp = Expression.Property(expression: expParam, property: propertyInfo); | |
Expression conversion = Expression.Convert(expression: expProp, typeof(object)); | |
Expression<Func<T, object>> expression = Expression.Lambda<Func<T, object>>(body: conversion, expParam); | |
IEnumerable<TermCount> termCounts = contentResult.TermsFacetFor(fieldSelector: expression); | |
facetResults.AddOrUpdate( | |
key: propertyInfo.Name, | |
addValue: termCounts, | |
(s, oldValue) => oldValue.Concat(second: termCounts)); | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
Dictionary<string, IEnumerable<TermCount>> facetResultsDictionary = facetResults.ToDictionary( | |
kvp => kvp.Key, | |
kvp => kvp.Value, | |
comparer: facetResults.Comparer); | |
return facetResultsDictionary; | |
} | |
/// <summary> | |
/// Facets the request action for field. | |
/// </summary> | |
/// <param name="fieldName">Name of the field.</param> | |
/// <param name="size">The size.</param> | |
/// <returns>The Action{TermsFacetRequest{>}.</returns> | |
private static Action<TermsFacetRequest> FacetRequestActionForField(string fieldName, int size) | |
{ | |
return x => | |
{ | |
x.Field = fieldName; | |
x.Size = size; | |
}; | |
} | |
/// <summary> | |
/// Facets the request action for field. | |
/// </summary> | |
/// <param name="propertyInfo">The property information.</param> | |
/// <param name="size">The size.</param> | |
/// <returns>The Action{TermsFacetRequest{>}.</returns> | |
private static Action<TermsFacetRequest> FacetRequestActionForField(PropertyInfo propertyInfo, int size) | |
{ | |
return FacetRequestActionForField(fieldName: propertyInfo.Name, size: size); | |
} | |
/// <summary> | |
/// Gets the properties that has the FacetAttribute sorted by the index. | |
/// </summary> | |
/// <typeparam name="T">The type to get the properties for.</typeparam> | |
/// <returns>An IEnumerable{PropertyInfo}.</returns> | |
private static IEnumerable<PropertyInfo> GetFacetedPropertiesSortedByIndex<T>() | |
where T : IContent | |
{ | |
return GetFacetedPropertiesSortedByIndex(typeof(T)); | |
} | |
/// <summary> | |
/// Gets the properties that has the FacetAttribute sorted by the index. | |
/// </summary> | |
/// <param name="type">The type to get the properties for.</param> | |
/// <returns>An IEnumerable{PropertyInfo}.</returns> | |
private static IEnumerable<PropertyInfo> GetFacetedPropertiesSortedByIndex(Type type) | |
{ | |
PropertyInfo[] allProperties = type.GetProperties().Where(predicate: HasAttribute<FacetAttribute>) | |
.Select( | |
x => new | |
{ | |
Property = x, | |
Attribute = (FacetAttribute)Attribute.GetCustomAttribute( | |
element: x, | |
typeof(FacetAttribute), | |
true) | |
}).OrderBy(x => x.Attribute?.Index ?? -1).Select(x => x.Property).ToArray(); | |
return allProperties; | |
} | |
/// <summary> | |
/// Determines whether the specified property has the specified attribute. | |
/// </summary> | |
/// <typeparam name="T">The attribute type.</typeparam> | |
/// <param name="propertyInfo">The propertyInfo.</param> | |
/// <returns><c>true</c> if the specified self has attribute; otherwise, <c>false</c>.</returns> | |
private static bool HasAttribute<T>(PropertyInfo propertyInfo) | |
where T : Attribute | |
{ | |
T attr = default; | |
try | |
{ | |
attr = (T)Attribute.GetCustomAttribute(element: propertyInfo, typeof(T)); | |
} | |
catch (Exception) | |
{ | |
} | |
return attr != null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public enum FacetType | |
{ | |
TermsFacetForWordsIn, | |
TermsFacetFor | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment