< Summary

Information
Class: Pozitron.QuerySpecification.LikeValidator
Assembly: Pozitron.QuerySpecification
File(s): /home/runner/work/QuerySpecification/QuerySpecification/src/QuerySpecification/Validators/LikeValidator.cs
Tag: 67_15587897385
Line coverage
100%
Covered lines: 21
Uncovered lines: 0
Coverable lines: 21
Total lines: 76
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    {
 1433        var compiledItems = specification.GetCompiledItems();
 1534        if (compiledItems.Length == 0) return true;
 35
 3036        var startIndexLikeItems = Array.FindIndex(compiledItems, item => item.Type == ItemType.Like);
 1437        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.
 1240        return IsValid(entity, compiledItems.AsSpan()[startIndexLikeItems..compiledItems.Length]);
 41    }
 42
 43    private static bool IsValid<T>(T entity, ReadOnlySpan<SpecItem> span)
 44    {
 1245        var groupStart = 0;
 5246        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.
 1849            if (i == span.Length || span[i].Bag != span[groupStart].Bag)
 50            {
 1551                if (IsValidInOrGroup(entity, span[groupStart..i]) is false)
 52                {
 453                    return false;
 54                }
 1155                groupStart = i;
 56            }
 57        }
 858        return true;
 59
 60        static bool IsValidInOrGroup(T entity, ReadOnlySpan<SpecItem> span)
 61        {
 1562            var validOrGroup = false;
 5163            foreach (var specItem in span)
 64            {
 1665                if (specItem.Reference is not SpecLikeCompiled<T> specLike) continue;
 66
 1667                if (specLike.KeySelector(entity)?.Like(specLike.Pattern) ?? false)
 68                {
 1169                    validOrGroup = true;
 1170                    break;
 71                }
 72            }
 1573            return validOrGroup;
 74        }
 75    }
 76}