Skip to content
Permalink
Browse files

Code refactoring #171

Break inheritance between sorting aggregators and eliminate multple collection enumeration on sort conditions.
Rename sort criteria to sort conditions, so that singular/plural are not confusing.
Eliminte LINQ from performance-sensitive code path.
  • Loading branch information...
snikolayev committed Feb 25, 2019
1 parent d9eaf6a commit 83dbe7342231223f6996c3cec3d23594ed9e3a4c
@@ -19,18 +19,20 @@ public void Compile(AggregateElement element, IEnumerable<IAggregateExpression>
{
var sourceType = element.Source.ValueType;

var sortCriteriaKeySelectors = compiledExpressions.Where(x => x.Name == AggregateElement.KeySelectorAscendingName || x.Name == AggregateElement.KeySelectorDescendingName).ToArray();
if (sortCriteriaKeySelectors.Any())
var sortConditions = compiledExpressions
.Where(x => x.Name == AggregateElement.KeySelectorAscendingName || x.Name == AggregateElement.KeySelectorDescendingName)
.Select(x => new SortCondition(x.Name, GetSortDirection(x.Name), x)).ToArray();
if (sortConditions.Any())
{
if (sortCriteriaKeySelectors.Length == 1)
if (sortConditions.Length == 1)
{
var keySelector = sortCriteriaKeySelectors[0];
_factory = CreateSingleKeySortedAggregatorFactory(sourceType, GetSortDirection(keySelector.Name), element.Expressions.FindSingleOrDefault(keySelector.Name), keySelector);
var sortCondition = sortConditions[0];
var selectorElement = element.Expressions.FindSingleOrDefault(sortCondition.Name);
_factory = CreateSingleKeySortedAggregatorFactory(sourceType, sortCondition.Direction, selectorElement, sortCondition.KeySelector);
}
else
{
var sortCriteria = compiledExpressions.Select(x => new SortCriteria(x, GetSortDirection(x.Name))).ToArray();
_factory = CreateMultiKeySortedAggregatorFactory(sourceType, sortCriteria);
_factory = CreateMultiKeySortedAggregatorFactory(sourceType, sortConditions);
}
}
else
@@ -49,8 +51,8 @@ private static SortDirection GetSortDirection(string keySelectorName)

private static Func<IAggregator> CreateSingleKeySortedAggregatorFactory(Type sourceType, SortDirection sortDirection, NamedExpressionElement selector, IAggregateExpression compiledSelector)
{
var resultType = selector.Expression.ReturnType;
var aggregatorType = typeof(SortedAggregator<,>).MakeGenericType(sourceType, resultType);
var keyType = selector.Expression.ReturnType;
var aggregatorType = typeof(SortedAggregator<,>).MakeGenericType(sourceType, keyType);

var ctor = aggregatorType.GetTypeInfo().DeclaredConstructors.Single();
var factoryExpression = Expression.Lambda<Func<IAggregator>>(
@@ -59,13 +61,13 @@ private static Func<IAggregator> CreateSingleKeySortedAggregatorFactory(Type sou
return factoryExpression.Compile();
}

private static Func<IAggregator> CreateMultiKeySortedAggregatorFactory(Type sourceType, SortCriteria[] sortCriterias)
private static Func<IAggregator> CreateMultiKeySortedAggregatorFactory(Type sourceType, SortCondition[] sortConditions)
{
var aggregatorType = typeof(MultiKeySortedAggregator<>).MakeGenericType(sourceType);

var ctor = aggregatorType.GetTypeInfo().DeclaredConstructors.Single();
var factoryExpression = Expression.Lambda<Func<IAggregator>>(
Expression.New(ctor, Expression.Constant(sortCriterias)));
Expression.New(ctor, Expression.Constant(sortConditions)));

return factoryExpression.Compile();
}
@@ -5,72 +5,132 @@

namespace NRules.Aggregators
{
internal class SortCriteria
internal class SortCondition
{
public SortCriteria(IAggregateExpression expression, SortDirection direction)
public SortCondition(string name, SortDirection direction, IAggregateExpression expression)
{
Name = name;
KeySelector = expression;
Direction = direction;
}

public string Name { get; }
public IAggregateExpression KeySelector { get; }

public SortDirection Direction { get; }
}

/// <summary>
/// Aggregate that adds matching facts into a collection sorted by a given key selector and sort direction.
/// </summary>
/// <typeparam name="TSource">Type of elements to collect.</typeparam>
internal class MultiKeySortedAggregator<TSource> : SortedAggregatorBase<TSource, object[]>
internal class MultiKeySortedAggregator<TSource> : IAggregator
{
private readonly SortCriteria[] _sortCriterias;
private readonly SortCondition[] _sortConditions;
private readonly SortedFactCollection<TSource, object[]> _sortedFactCollection;
private bool _created = false;

public MultiKeySortedAggregator(IEnumerable<SortCriteria> sortCriterias)
: base(GetComparer(sortCriterias))
public MultiKeySortedAggregator(IEnumerable<SortCondition> sortConditions)
{
_sortCriterias = sortCriterias.ToArray();
_sortConditions = sortConditions.ToArray();
var comparer = CreateComparer(_sortConditions);
_sortedFactCollection = new SortedFactCollection<TSource, object[]>(comparer);
}

private static IComparer<object[]> GetComparer(IEnumerable<SortCriteria> sortCriterias)
private static IComparer<object[]> CreateComparer(IEnumerable<SortCondition> sortConditions)
{
var comparers = new List<IComparer<object>>();
foreach (var sortCriteria in sortCriterias)
foreach (var sortCondition in sortConditions)
{
var defaultComparer = (IComparer<object>)Comparer<object>.Default;
var comparer = sortCriteria.Direction == SortDirection.Ascending ? defaultComparer : new ReverseComparer<object>(defaultComparer);
var comparer = sortCondition.Direction == SortDirection.Ascending ? defaultComparer : new ReverseComparer<object>(defaultComparer);
comparers.Add(comparer);
}

return new MultiKeyComparer(comparers);
}

protected override object[] GetKey(AggregationContext context, ITuple tuple, IFact fact)
private object[] GetKey(AggregationContext context, ITuple tuple, IFact fact)
{
return _sortCriterias.Select(x => x.KeySelector.Invoke(context, tuple, fact)).ToArray();
var key = new object[_sortConditions.Length];
for (int i = 0; i < _sortConditions.Length; i++)
{
key[i] = _sortConditions[i].KeySelector.Invoke(context, tuple, fact);
}
return key;
}
}

internal class MultiKeyComparer : IComparer<object[]>
{
readonly IComparer<object>[] _comparers;
public IEnumerable<AggregationResult> Add(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
AddFacts(context, tuple, facts);
if (!_created)
{
_created = true;
return new[] { AggregationResult.Added(_sortedFactCollection, _sortedFactCollection.GetFactEnumerable()) };
}
return new[] { AggregationResult.Modified(_sortedFactCollection, _sortedFactCollection, _sortedFactCollection.GetFactEnumerable()) };
}

public IEnumerable<AggregationResult> Modify(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
ModifyFacts(context, tuple, facts);
return new[] { AggregationResult.Modified(_sortedFactCollection, _sortedFactCollection, _sortedFactCollection.GetFactEnumerable()) };
}

public IEnumerable<AggregationResult> Remove(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
RemoveFacts(context, tuple, facts);
return new[] { AggregationResult.Modified(_sortedFactCollection, _sortedFactCollection, _sortedFactCollection.GetFactEnumerable()) };
}

private void AddFacts(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
foreach (var fact in facts)
{
var key = GetKey(context, tuple, fact);
_sortedFactCollection.AddFact(key, fact);
}
}

private void ModifyFacts(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
foreach (var fact in facts)
{
_sortedFactCollection.RemoveFact(fact);

var key = GetKey(context, tuple, fact);
_sortedFactCollection.AddFact(key, fact);
}
}

public MultiKeyComparer(IEnumerable<IComparer<object>> comparers)
private void RemoveFacts(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
_comparers = comparers.ToArray();
foreach (var fact in facts)
{
_sortedFactCollection.RemoveFact(fact);
}
}

public int Compare(object[] x, object[] y)
private class MultiKeyComparer : IComparer<object[]>
{
var result = 0;
private readonly IComparer<object>[] _comparers;

for (int i = 0; i < _comparers.Length; i++)
public MultiKeyComparer(IEnumerable<IComparer<object>> comparers)
{
result = _comparers[i].Compare(x[i], y[i]);
if (result != 0) break;
_comparers = comparers.ToArray();
}

return result;
public int Compare(object[] x, object[] y)
{
var result = 0;

for (int i = 0; i < _comparers.Length; i++)
{
result = _comparers[i].Compare(x[i], y[i]);
if (result != 0) break;
}

return result;
}
}
}
}
@@ -9,41 +9,26 @@ namespace NRules.Aggregators
/// </summary>
/// <typeparam name="TSource">Type of source element.</typeparam>
/// <typeparam name="TKey">Type of key used in the key selector for sorting.</typeparam>
internal class SortedAggregator<TSource, TKey> : SortedAggregatorBase<TSource, TKey>
internal class SortedAggregator<TSource, TKey> : IAggregator
{
private readonly SortedFactCollection<TSource, TKey> _sortedFactCollection;
private readonly IAggregateExpression _keySelector;
private bool _created = false;

public SortedAggregator(IAggregateExpression keySelector, SortDirection sortDirection)
: base(GetComparer(sortDirection))
{
_keySelector = keySelector;
var comparer = CreateComparer(sortDirection);
_sortedFactCollection = new SortedFactCollection<TSource, TKey>(comparer);
}

private static IComparer<TKey> GetComparer(SortDirection sortDirection)
private static IComparer<TKey> CreateComparer(SortDirection sortDirection)
{
var defaultComparer = (IComparer<TKey>)Comparer<TKey>.Default;
var comparer = sortDirection == SortDirection.Ascending ? defaultComparer : new ReverseComparer<TKey>(defaultComparer);
return comparer;
}

protected override TKey GetKey(AggregationContext context, ITuple tuple, IFact fact)
{
return (TKey)_keySelector.Invoke(context, tuple, fact);
}
}

internal abstract class SortedAggregatorBase<TSource, TKey> : IAggregator
{
private readonly SortedFactCollection<TSource, TKey> _sortedFactCollection;
private bool _created = false;

protected SortedAggregatorBase(IComparer<TKey> comparer)
{
_sortedFactCollection = new SortedFactCollection<TSource, TKey>(comparer);
}

protected abstract TKey GetKey(AggregationContext context, ITuple tuple, IFact fact);

public IEnumerable<AggregationResult> Add(AggregationContext context, ITuple tuple, IEnumerable<IFact> facts)
{
AddFacts(context, tuple, facts);
@@ -71,7 +56,7 @@ private void AddFacts(AggregationContext context, ITuple tuple, IEnumerable<IFac
{
foreach (var fact in facts)
{
var key = GetKey(context, tuple, fact);
var key = (TKey)_keySelector.Invoke(context, tuple, fact);
_sortedFactCollection.AddFact(key, fact);
}
}
@@ -82,7 +67,7 @@ private void ModifyFacts(AggregationContext context, ITuple tuple, IEnumerable<I
{
_sortedFactCollection.RemoveFact(fact);

var key = GetKey(context, tuple, fact);
var key = (TKey)_keySelector.Invoke(context, tuple, fact);
_sortedFactCollection.AddFact(key, fact);
}
}
@@ -419,14 +419,18 @@ private static MultiKeySortedAggregator<TestFact> CreateTarget_SortWithInt1AndIn
{
var expression1 = new FactExpression<TestFact, int>(x => x.Int1);
var expression2 = new FactExpression<TestFact, int>(x => x.Int2);
return new MultiKeySortedAggregator<TestFact>(new[] { new SortCriteria(expression1, sortDirection1), new SortCriteria(expression2, sortDirection2) });
var sortCondition1 = new SortCondition(AggregateElement.KeySelectorAscendingName, sortDirection1, expression1);
var sortCondition2 = new SortCondition(AggregateElement.KeySelectorAscendingName, sortDirection2, expression2);
return new MultiKeySortedAggregator<TestFact>(new[] { sortCondition1, sortCondition2 });
}

private static MultiKeySortedAggregator<TestFact> CreateTarget_SortWithInt1AndString(SortDirection sortDirectionInt, SortDirection sortDirectionString)
{
var expressionInt = new FactExpression<TestFact, int>(x => x.Int1);
var expressionString = new FactExpression<TestFact, string>(x => x.String);
return new MultiKeySortedAggregator<TestFact>(new[] { new SortCriteria(expressionInt, sortDirectionInt), new SortCriteria(expressionString, sortDirectionString) });
var sortCondition1 = new SortCondition(AggregateElement.KeySelectorAscendingName, sortDirectionInt, expressionInt);
var sortCondition2 = new SortCondition(AggregateElement.KeySelectorAscendingName, sortDirectionString, expressionString);
return new MultiKeySortedAggregator<TestFact>(new[] { sortCondition1, sortCondition2 });
}

private static void AssertAggregationResult(AggregationResult[] results, AggregationAction action, params TestFact[] orderedFacts)

0 comments on commit 83dbe73

Please sign in to comment.
You can’t perform that action at this time.