< Summary - Jellyfin

Information
Class: Jellyfin.Database.Implementations.JellyfinQueryHelperExtensions
Assembly: Jellyfin.Database.Implementations
File(s): /srv/git/jellyfin/src/Jellyfin.Database/Jellyfin.Database.Implementations/JellyfinQueryHelperExtensions.cs
Line coverage
58%
Covered lines: 25
Uncovered lines: 18
Coverable lines: 43
Total lines: 194
Line coverage: 58.1%
Branch coverage
90%
Covered branches: 9
Total branches: 10
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 7/22/2025 - 12:11:20 AM Line coverage: 73.5% (25/34) Branch coverage: 90% (9/10) Total lines: 16610/14/2025 - 12:11:23 AM Line coverage: 59.5% (25/42) Branch coverage: 90% (9/10) Total lines: 19310/28/2025 - 12:11:27 AM Line coverage: 58.1% (25/43) Branch coverage: 90% (9/10) Total lines: 194 7/22/2025 - 12:11:20 AM Line coverage: 73.5% (25/34) Branch coverage: 90% (9/10) Total lines: 16610/14/2025 - 12:11:23 AM Line coverage: 59.5% (25/42) Branch coverage: 90% (9/10) Total lines: 19310/28/2025 - 12:11:27 AM Line coverage: 58.1% (25/43) Branch coverage: 90% (9/10) Total lines: 194

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
WhereOneOrMany(...)100%11100%
WhereReferencedItem(...)100%210%
WhereReferencedItemMultipleTypes(...)100%210%
ReferencedItemFilterExpressionBuilder(...)100%210%
OneOrManyExpressionBuilder(...)100%88100%
Replace(...)100%11100%
.ctor(...)100%11100%
VisitAndConvert(...)100%11100%
VisitLambda(...)100%11100%
VisitParameter(...)50%22100%

File(s)

/srv/git/jellyfin/src/Jellyfin.Database/Jellyfin.Database.Implementations/JellyfinQueryHelperExtensions.cs

#LineLine coverage
 1#pragma warning disable RS0030 // Do not use banned APIs
 2
 3using System;
 4using System.Collections.Concurrent;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Linq.Expressions;
 8using System.Reflection;
 9using Jellyfin.Database.Implementations.Entities;
 10using Microsoft.EntityFrameworkCore;
 11
 12namespace Jellyfin.Database.Implementations;
 13
 14/// <summary>
 15/// Contains a number of query related extensions.
 16/// </summary>
 17public static class JellyfinQueryHelperExtensions
 18{
 119    private static readonly MethodInfo _containsMethodGenericCache = typeof(Enumerable).GetMethods(BindingFlags.Public |
 120    private static readonly MethodInfo _efParameterInstruction = typeof(EF).GetMethod(nameof(EF.Parameter), BindingFlags
 121    private static readonly ConcurrentDictionary<Type, MethodInfo> _containsQueryCache = new();
 22
 23    /// <summary>
 24    /// Builds an optimised query checking one property against a list of values while maintaining an optimal query.
 25    /// </summary>
 26    /// <typeparam name="TEntity">The entity.</typeparam>
 27    /// <typeparam name="TProperty">The property type to compare.</typeparam>
 28    /// <param name="query">The source query.</param>
 29    /// <param name="oneOf">The list of items to check.</param>
 30    /// <param name="property">Property expression.</param>
 31    /// <returns>A Query.</returns>
 32    public static IQueryable<TEntity> WhereOneOrMany<TEntity, TProperty>(this IQueryable<TEntity> query, IList<TProperty
 33    {
 19634        return query.Where(OneOrManyExpressionBuilder(oneOf, property));
 35    }
 36
 37    /// <summary>
 38    /// Builds a query that checks referenced ItemValues for a cross BaseItem lookup.
 39    /// </summary>
 40    /// <param name="baseQuery">The source query.</param>
 41    /// <param name="context">The database context.</param>
 42    /// <param name="itemValueType">The type of item value to reference.</param>
 43    /// <param name="referenceIds">The list of BaseItem ids to check matches.</param>
 44    /// <param name="invert">If set an exclusion check is performed instead.</param>
 45    /// <returns>A Query.</returns>
 46    public static IQueryable<BaseItemEntity> WhereReferencedItem(
 47        this IQueryable<BaseItemEntity> baseQuery,
 48        JellyfinDbContext context,
 49        ItemValueType itemValueType,
 50        IList<Guid> referenceIds,
 51        bool invert = false)
 52    {
 053        return baseQuery.Where(ReferencedItemFilterExpressionBuilder(context, itemValueType, referenceIds, invert));
 54    }
 55
 56    /// <summary>
 57    /// Builds a query that checks referenced ItemValues for a cross BaseItem lookup.
 58    /// </summary>
 59    /// <param name="baseQuery">The source query.</param>
 60    /// <param name="context">The database context.</param>
 61    /// <param name="itemValueTypes">The type of item value to reference.</param>
 62    /// <param name="referenceIds">The list of BaseItem ids to check matches.</param>
 63    /// <param name="invert">If set an exclusion check is performed instead.</param>
 64    /// <returns>A Query.</returns>
 65    public static IQueryable<BaseItemEntity> WhereReferencedItemMultipleTypes(
 66        this IQueryable<BaseItemEntity> baseQuery,
 67        JellyfinDbContext context,
 68        IList<ItemValueType> itemValueTypes,
 69        IList<Guid> referenceIds,
 70        bool invert = false)
 71    {
 072        var itemFilter = OneOrManyExpressionBuilder<BaseItemEntity, Guid>(referenceIds, f => f.Id);
 073        var typeFilter = OneOrManyExpressionBuilder<ItemValue, ItemValueType>(itemValueTypes, iv => iv.Type);
 74
 075        return baseQuery.Where(item =>
 076            context.ItemValues
 077                .Where(typeFilter)
 078                .Join(context.ItemValuesMap, e => e.ItemValueId, e => e.ItemValueId, (itemVal, map) => new { itemVal, ma
 079                .Any(val =>
 080                    context.BaseItems.Where(itemFilter).Any(e => e.CleanName == val.itemVal.CleanValue)
 081                    && val.map.ItemId == item.Id) == EF.Constant(!invert));
 82    }
 83
 84    /// <summary>
 85    /// Builds a query expression that checks referenced ItemValues for a cross BaseItem lookup.
 86    /// </summary>
 87    /// <param name="context">The database context.</param>
 88    /// <param name="itemValueType">The type of item value to reference.</param>
 89    /// <param name="referenceIds">The list of BaseItem ids to check matches.</param>
 90    /// <param name="invert">If set an exclusion check is performed instead.</param>
 91    /// <returns>A Query.</returns>
 92    public static Expression<Func<BaseItemEntity, bool>> ReferencedItemFilterExpressionBuilder(
 93        this JellyfinDbContext context,
 94        ItemValueType itemValueType,
 95        IList<Guid> referenceIds,
 96        bool invert = false)
 97    {
 98        // Well genre/artist/album etc items do not actually set the ItemValue of thier specitic types so we cannot matc
 99        /*
 100        "(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=@Ge
 101        */
 102
 0103        var itemFilter = OneOrManyExpressionBuilder<BaseItemEntity, Guid>(referenceIds, f => f.Id);
 104
 0105        return item =>
 0106          context.ItemValues
 0107              .Join(context.ItemValuesMap, e => e.ItemValueId, e => e.ItemValueId, (item, map) => new { item, map })
 0108              .Any(val =>
 0109                  val.item.Type == itemValueType
 0110                  && context.BaseItems.Where(itemFilter).Any(e => e.CleanName == val.item.CleanValue)
 0111                  && val.map.ItemId == item.Id) == EF.Constant(!invert);
 112    }
 113
 114    /// <summary>
 115    /// Builds an optimised query expression checking one property against a list of values while maintaining an optimal
 116    /// </summary>
 117    /// <typeparam name="TEntity">The entity.</typeparam>
 118    /// <typeparam name="TProperty">The property type to compare.</typeparam>
 119    /// <param name="oneOf">The list of items to check.</param>
 120    /// <param name="property">Property expression.</param>
 121    /// <returns>A Query.</returns>
 122    public static Expression<Func<TEntity, bool>> OneOrManyExpressionBuilder<TEntity, TProperty>(this IList<TProperty> o
 123    {
 196124        var parameter = Expression.Parameter(typeof(TEntity), "item");
 196125        property = ParameterReplacer.Replace<Func<TEntity, TProperty>, Func<TEntity, TProperty>>(property, property.Para
 196126        if (oneOf.Count == 1)
 127        {
 114128            var value = oneOf[0];
 114129            if (typeof(TProperty).IsValueType)
 130            {
 55131                return Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(property.Body, Expression.Constant(value)
 132            }
 133            else
 134            {
 59135                return Expression.Lambda<Func<TEntity, bool>>(Expression.ReferenceEqual(property.Body, Expression.Consta
 136            }
 137        }
 138
 82139        var containsMethodInfo = _containsQueryCache.GetOrAdd(typeof(TProperty), static (key) => _containsMethodGenericC
 140
 82141        if (oneOf.Count < 4) // arbitrary value choosen.
 142        {
 143            // if we have 3 or fewer values to check against its faster to do a IN(const,const,const) lookup
 70144            return Expression.Lambda<Func<TEntity, bool>>(Expression.Call(null, containsMethodInfo, Expression.Constant(
 145        }
 146
 12147        return Expression.Lambda<Func<TEntity, bool>>(Expression.Call(null, containsMethodInfo, Expression.Call(null, _e
 148    }
 149
 150    internal static class ParameterReplacer
 151    {
 152        // Produces an expression identical to 'expression'
 153        // except with 'source' parameter replaced with 'target' expression.
 154        internal static Expression<TOutput> Replace<TInput, TOutput>(
 155                        Expression<TInput> expression,
 156                        ParameterExpression source,
 157                        ParameterExpression target)
 158        {
 196159            return new ParameterReplacerVisitor<TOutput>(source, target)
 196160                        .VisitAndConvert(expression);
 161        }
 162
 163        private sealed class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
 164        {
 165            private readonly ParameterExpression _source;
 166            private readonly ParameterExpression _target;
 167
 196168            public ParameterReplacerVisitor(ParameterExpression source, ParameterExpression target)
 169            {
 196170                _source = source;
 196171                _target = target;
 196172            }
 173
 174            internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
 175            {
 196176                return (Expression<TOutput>)VisitLambda(root);
 177            }
 178
 179            protected override Expression VisitLambda<T>(Expression<T> node)
 180            {
 181                // Leave all parameters alone except the one we want to replace.
 196182                var parameters = node.Parameters.Select(p => p == _source ? _target : p);
 183
 196184                return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
 185            }
 186
 187            protected override Expression VisitParameter(ParameterExpression node)
 188            {
 189                // Replace the source with the target, visit other params as usual.
 196190                return node == _source ? _target : base.VisitParameter(node);
 191            }
 192        }
 193    }
 194}

Methods/Properties

.cctor()
WhereOneOrMany(System.Linq.IQueryable`1<TEntity>,System.Collections.Generic.IList`1<TProperty>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
WhereReferencedItem(System.Linq.IQueryable`1<Jellyfin.Database.Implementations.Entities.BaseItemEntity>,Jellyfin.Database.Implementations.JellyfinDbContext,Jellyfin.Database.Implementations.Entities.ItemValueType,System.Collections.Generic.IList`1<System.Guid>,System.Boolean)
WhereReferencedItemMultipleTypes(System.Linq.IQueryable`1<Jellyfin.Database.Implementations.Entities.BaseItemEntity>,Jellyfin.Database.Implementations.JellyfinDbContext,System.Collections.Generic.IList`1<Jellyfin.Database.Implementations.Entities.ItemValueType>,System.Collections.Generic.IList`1<System.Guid>,System.Boolean)
ReferencedItemFilterExpressionBuilder(Jellyfin.Database.Implementations.JellyfinDbContext,Jellyfin.Database.Implementations.Entities.ItemValueType,System.Collections.Generic.IList`1<System.Guid>,System.Boolean)
OneOrManyExpressionBuilder(System.Collections.Generic.IList`1<TProperty>,System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
Replace(System.Linq.Expressions.Expression`1<TInput>,System.Linq.Expressions.ParameterExpression,System.Linq.Expressions.ParameterExpression)
.ctor(System.Linq.Expressions.ParameterExpression,System.Linq.Expressions.ParameterExpression)
VisitAndConvert(System.Linq.Expressions.Expression`1<T>)
VisitLambda(System.Linq.Expressions.Expression`1<T>)
VisitParameter(System.Linq.Expressions.ParameterExpression)