|
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; |
|
} |
|
} |