< Summary

Information
Class: Pozitron.QuerySpecification.LikeValidator
Assembly: Pozitron.QuerySpecification
File(s): /home/runner/work/QuerySpecification/QuerySpecification/src/QuerySpecification/Validators/LikeValidator.cs
Tag: 74_17335419374
Line coverage
100%
Covered lines: 19
Uncovered lines: 0
Coverable lines: 19
Total lines: 74
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 Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%11100%
IsValid(...)100%66100%
IsValid(...)100%88100%
IsValidInOrGroup(T,System.ReadOnlySpan`1<Pozitron.QuerySpecification.SpecItem>)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>
 21[ValidatorDiscovery(Order = 20)]
 22public sealed class LikeValidator : IValidator
 23{
 24    /// <summary>
 25    /// Gets the singleton instance of the <see cref="LikeValidator"/> class.
 26    /// </summary>
 227    public static LikeValidator Instance = new();
 428    private LikeValidator() { }
 29
 30    /// <inheritdoc/>
 31    public bool IsValid<T>(T entity, Specification<T> specification)
 32    {
 1533        var compiledItems = specification.GetCompiledItems();
 1634        if (compiledItems.Length == 0) return true;
 35
 3236        var startIndexLikeItems = Array.FindIndex(compiledItems, item => item.Type == ItemType.Like);
 1537        if (startIndexLikeItems == -1) return true;
 38
 39        // The like items are contiguously placed as a last segment in the array and are already sorted by group.
 1340        return IsValid(entity, compiledItems.AsSpan()[startIndexLikeItems..compiledItems.Length]);
 41    }
 42
 43    private static bool IsValid<T>(T entity, ReadOnlySpan<SpecItem> span)
 44    {
 1345        var groupStart = 0;
 5446        for (var i = 1; i <= span.Length; i++)
 47        {
 48            // If we reached the end of the span or the group has changed, we slice and process the group.
 1949            if (i == span.Length || span[i].Bag != span[groupStart].Bag)
 50            {
 1651                if (IsValidInOrGroup(entity, span[groupStart..i]) is false)
 52                {
 553                    return false;
 54                }
 1155                groupStart = i;
 56            }
 57        }
 858        return true;
 59
 60        static bool IsValidInOrGroup(T entity, ReadOnlySpan<SpecItem> span)
 61        {
 5562            foreach (var specItem in span)
 63            {
 1764                if (specItem.Reference is not SpecLikeCompiled<T> specLike) continue;
 65
 1766                if (specLike.KeySelector(entity)?.Like(specLike.Pattern) ?? false)
 67                {
 1168                    return true;
 69                }
 70            }
 571            return false;
 72        }
 73    }
 74}