Skip to content

Instantly share code, notes, and snippets.

@Ninodeluxe
Forked from jstemerdink/Example.txt
Last active September 30, 2016 07:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ninodeluxe/5d1507a35b8f796ddbbb3535d7f57475 to your computer and use it in GitHub Desktop.
Save Ninodeluxe/5d1507a35b8f796ddbbb3535d7f57475 to your computer and use it in GitHub Desktop.
Use an attribute to create facets for an EPiServer Find query and extract facets from the search results
/// <summary>
/// Enum MyFacetCategory
/// </summary>
public enum MyFacetCategory
{
CategoryOne = 1,
CategoryTwo = 2,
CategoryThree = 3,
CategoryFour = 4,
CategoryFive = 5
}
[Facet((int)MyFacetCategory.CategoryOne, FacetType.TermsFacetFor)]
public virtual string TermsFacetExample { get; set; }
[Facet((int)MyFacetCategory.CategoryTwo, 20)]
public virtual string TermsFacetExampleTwo { get; set; }
IClient searchManager = Client.CreateFromConfig();
ITypeSearch<StandardPage> queryTest = searchManager.Search<StandardPage>().For("test");
queryTest = queryTest.AddFacets();
IContentResult<StandardPage> results = queryTest.GetContentResult();
Dictionary<int, TermsFacet> facetResults = results.GetFacets();
IEnumerable<string> categoryOneTerms = facetResults.ExtractFacetTermsFor((int)MyFacetCategory.CategoryOne);
IEnumerable<string> categoryTwoTerms = facetResults.ExtractFacetTermsFor((int)MyFacetCategory.CategoryTwo);
/// <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="facetType">Type of the facet.</param>
/// <param name="size">The number of facets</param>
public FacetAttribute(int index, FacetType fascetType, int size)
{
this.Index = index;
this.FacetType = fascetType;
this.Size = size;
}
/// <summary>
/// Initializes a new instance of the <see cref="FacetAttribute"/> class.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="size">The number of facets</param>
public FacetAttribute(int index, int size)
{
this.Index = index;
this.FacetType = FacetType.TermsFacetFor;
this.Size = size;
}
/// <summary>
/// Initializes a new instance of the <see cref="FacetAttribute"/> class.
/// </summary>
/// <param name="index">The index.</param>
public FacetAttribute(int index)
{
this.Index = index;
this.FacetType = FacetType.TermsFacetFor;
this.Size = 10;
}
/// <summary>
/// Initializes a new instance of the <see cref="FacetAttribute"/> class.
/// </summary>
public FacetAttribute()
{
this.Index = 0;
this.FacetType = FacetType.TermsFacetFor;
this.Size = 10;
}
#region Public Properties
/// <summary>
/// Gets the index.
/// </summary>
/// <value>The index.</value>
public int Index { get; }
/// <summary>
/// Gets the type of the facet.
/// </summary>
/// <value>The type of the facet.</value>
public FacetType FacetType { get; }
/// <summary>
/// Gets the number of facets.
/// </summary>
/// <value>The facet size</value>
public int Size { 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;
}
}
#endregion
}
/// <summary>
/// Enum FacetType
/// </summary>
public enum FacetType
{
TermsFacetForWordsIn,
TermsFacetFor
}
/// <summary>
/// Class FindExtensions.
/// </summary>
public static class FindExtensions
{
/// <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&lt;T&gt;.</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);
//set facet Size
Action<TermsFacetRequest> facetRequestAction = (x) =>
{
x.Size = facetAttribute.Size;
};
try
{
switch (facetAttribute.FacetType)
{
case FacetType.TermsFacetFor:
query = TypeSearchExtensions.TermsFacetFor(query, expression, facetRequestAction);
break;
case FacetType.TermsFacetForWordsIn:
query = TypeSearchExtensions.TermsFacetForWordsIn(query, expression);
break;
default:
query = TypeSearchExtensions.TermsFacetFor(query, expression, facetRequestAction);
break;
}
}
catch (Exception)
{
}
}
return query;
}
/// <summary>
/// Returns a Dictionary from IHasFacetResults based on an attribute
/// </summary>
/// <typeparam name="T">The content type to add the facets for</typeparam>
/// <param name="results">The result IHasFacetResults</param>
/// <returns>Dictionary<int, TermsFacet></returns>
public static Dictionary<int, TermsFacet> GetFacets<T>(this IHasFacetResults<T> results)
where T : IContent
{
var termsFacetsDictionary = new Dictionary<int, TermsFacet>();
foreach (PropertyInfo propertyInfo in GetPropertiesSortedByIndex<T>())
{
var facetAttribute = propertyInfo.GetFacetAttribute();
if (facetAttribute == null)
{
continue;
}
ParameterExpression expParam = Expression.Parameter(typeof(T), "x");
MemberExpression expProp = Expression.Property(expParam, propertyInfo);
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(object));
dynamic expression = Expression.Lambda(delegateType, expProp, expParam);
Expression<Func<T, object>> fieldSelector = expression;
try
{
var termsFacet = FacetResultExtraction.TermsFacetFor(results, fieldSelector);
if (termsFacet != null)
{
termsFacetsDictionary.Add(facetAttribute.Index, termsFacet);
}
}
catch (Exception)
{
//add logging
}
}
return termsFacetsDictionary;
}
/// <summary>
/// Example how to e.g. get an IEnumerable of strings from the GetFacets dictionary object
/// </summary>
/// <typeparam name="dict">The dictionary</typeparam>
/// <returns>IEnumerable<string></returns>
public static IEnumerable<string> ExtractFacetTermsFor(this Dictionary<int, TermsFacet> dict, int index)
{
return dict.Where(x => x.Key.Equals(index).SelectMany(y => y.Value.Terms.Select(t => t.Term).OrderBy(t => t)));
}
/// <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>IEnumerable&lt;PropertyInfo&gt;.</returns>
private static IEnumerable<PropertyInfo> GetPropertiesSortedByIndex<T>() where T : IContent
{
PropertyInfo[] allProperties = typeof(T)
.GetProperties()
.Where(HasAttribute<FacetAttribute>)
.Select(x => new
{
Property = x,
Attribute = (FacetAttribute)Attribute.GetCustomAttribute(x, typeof(FacetAttribute), true)
})
.OrderBy(x => x.Attribute != null ? 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(T);
try
{
attr = (T)Attribute.GetCustomAttribute(propertyInfo, typeof(T));
}
catch (Exception)
{
}
return attr != null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment