< Summary

Information
Class: Pozitron.QuerySpecification.LikeValidator
Assembly: Pozitron.QuerySpecification
File(s): /home/runner/work/QuerySpecification/QuerySpecification/src/QuerySpecification/Validators/LikeValidator.cs
Tag: 52_11740816328
Line coverage
100%
Covered lines: 21
Uncovered lines: 0
Coverable lines: 21
Total lines: 75
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
IsValid(...)100%66100%
IsValid(...)100%88100%
IsValidInOrGroup()100%88100%

File(s)

/home/runner/work/QuerySpecification/QuerySpecification/src/QuerySpecification/Validators/LikeValidator.cs

#LineLine coverage
 1namespace Pozitron.QuerySpecification;
 2
 3/*
 4    public bool IsValid<T>(T entity, Specification<T> specification)
 5    {
 6        foreach (var likeGroup in specification.LikeExpressions.GroupBy(x => x.Group))
 7        {
 8            if (likeGroup.Any(c => c.KeySelectorFunc(entity)?.Like(c.Pattern) ?? false) == false) return false;
 9        }
 10        return true;
 11    }
 12    This was the previous implementation.We're trying to avoid allocations of LikeExpressions, GroupBy and LINQ.
 13    Instead of GroupBy, we have a single array sorted by group, and we slice it to get the groups.
 14    The new implementation preserves the behavior and reduces allocations drastically.
 15    For 1000 entities, the allocations are reduced from 651.160 bytes to ZERO bytes. Refer to LikeValidatorBenchmark res
 16 */
 17
 18/// <summary>
 19/// Represents a validator for "like" expressions.
 20/// </summary>
 21public sealed class LikeValidator : IValidator
 22{
 23    /// <summary>
 24    /// Gets the singleton instance of the <see cref="LikeValidator"/> class.
 25    /// </summary>
 126    public static LikeValidator Instance = new();
 227    private LikeValidator() { }
 28
 29    /// <inheritdoc/>
 30    public bool IsValid<T>(T entity, Specification<T> specification)
 31    {
 1432        var compiledItems = specification.GetCompiledItems();
 1533        if (compiledItems.Length == 0) return true;
 34
 3035        var startIndexLikeItems = Array.FindIndex(compiledItems, item => item.Type == ItemType.Like);
 1436        if (startIndexLikeItems == -1) return true;
 37
 38        // The like items are contiguously placed as a last segment in the array and are already sorted by group.
 1239        return IsValid(entity, compiledItems.AsSpan()[startIndexLikeItems..compiledItems.Length]);
 40    }
 41
 42    private static bool IsValid<T>(T entity, ReadOnlySpan<SpecItem> span)
 43    {
 1244        var groupStart = 0;
 5245        for (var i = 1; i <= span.Length; i++)
 46        {
 47            // If we reached the end of the span or the group has changed, we slice and process the group.
 1848            if (i == span.Length || span[i].Bag != span[groupStart].Bag)
 49            {
 1550                if (IsValidInOrGroup(entity, span[groupStart..i]) is false)
 51                {
 452                    return false;
 53                }
 1154                groupStart = i;
 55            }
 56        }
 857        return true;
 58
 59        static bool IsValidInOrGroup(T entity, ReadOnlySpan<SpecItem> span)
 60        {
 1561            var validOrGroup = false;
 5162            foreach (var specItem in span)
 63            {
 1664                if (specItem.Reference is not SpecLikeCompiled<T> specLike) continue;
 65
 1666                if (specLike.KeySelector(entity)?.Like(specLike.Pattern) ?? false)
 67                {
 1168                    validOrGroup = true;
 1169                    break;
 70                }
 71            }
 1572            return validOrGroup;
 73        }
 74    }
 75}