feat(dal,templateMatcher): уточнена логика UnitFilterService, в TemplateMatcher добавлены методы синхронизации и обновлению существующих шаблонов по группе
This commit is contained in:
@@ -13,7 +13,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
internal class UnitFilterService : IUnitFilterService
|
||||
{
|
||||
#if DEBUG
|
||||
private readonly Guid targetUnitId = Guid.Parse("d4322a08-246b-4380-8953-8ce4a8446235");
|
||||
private readonly Guid targetUnitId = Guid.Parse("6c019569-ecba-4d9a-9cdf-cdc1dea93925");
|
||||
#endif
|
||||
|
||||
private readonly ILogger<UnitFilterService> logger;
|
||||
@@ -123,7 +123,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
logger.LogDebug("Базовый фильтр по Name '{NameFilter}' дал {Count} юнитов", filter.UnitFilter, initialUnitIds.Count);
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в initialUnitIds
|
||||
// Отладка: проверить, есть ли юнит в initialUnitIds
|
||||
if (initialUnitIds.Contains(targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в initialUnitIds.", targetUnitId);
|
||||
@@ -267,7 +267,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в candidateUnits после FieldFilter
|
||||
// Отладка: проверить, есть ли юнит в candidateUnits после FieldFilter
|
||||
var candidateListAfterFieldFilter = candidateUnits.ToList();
|
||||
if (candidateListAfterFieldFilter.Any(u => u.Id == targetUnitId))
|
||||
{
|
||||
@@ -276,7 +276,6 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
else
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в candidateUnits после FieldFilter.", targetUnitId);
|
||||
//continue; // ❌ юнит отсеялся на этом этапе
|
||||
}
|
||||
|
||||
logger.LogDebug("После FieldFilter осталось {Count} юнитов", candidateListAfterFieldFilter.Count);
|
||||
@@ -288,7 +287,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в candidateUnits после RelationshipFilter
|
||||
// Отладка: проверить, есть ли юнит в candidateUnits после RelationshipFilter
|
||||
var candidateListAfterRelFilter = candidateUnits.ToList();
|
||||
if (candidateListAfterRelFilter.Any(u => u.Id == targetUnitId))
|
||||
{
|
||||
@@ -297,7 +296,6 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
else
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} НЕ найден в candidateUnits после RelationshipFilter.", targetUnitId);
|
||||
//continue; // ❌ юнит отсеялся на этом этапе
|
||||
}
|
||||
|
||||
logger.LogDebug("После RelationshipFilter осталось {Count} юнитов", candidateListAfterRelFilter.Count);
|
||||
@@ -311,7 +309,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
logger.LogDebug("После Umbrella-фильтра осталось {Count} юнитов", finalUnits.Count());
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в finalUnits после Umbrella
|
||||
// Отладка: проверить, есть ли юнит в finalUnits после Umbrella
|
||||
if (finalUnits.Any(u => u.Id == targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в finalUnits после Umbrella-фильтра.", targetUnitId);
|
||||
@@ -331,7 +329,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
.ToList();
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в newIds
|
||||
// Отладка: проверить, есть ли юнит в newIds
|
||||
if (newIds.Contains(targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в newIds.", targetUnitId);
|
||||
@@ -359,7 +357,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
job.Id, job.UnitFilters.Count, result.Count);
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, есть ли юнит в result
|
||||
// Отладка: проверить, есть ли юнит в result
|
||||
if (result.Contains(targetUnitId))
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId} найден в финальном результате.", targetUnitId);
|
||||
@@ -398,11 +396,11 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
|
||||
logger.LogDebug("Обработка UnitFilter.Id {FilterId}. Количество RelationshipFilters: {RelFilterCount}", filter.Id, filter.RelationshipFilters.Count());
|
||||
|
||||
// ✅ Получить все связи для юнита
|
||||
// Получить все связи для юнита
|
||||
var parentLinks = await unitInUnitService.GetByChildIdAsync(unitId);
|
||||
var childLinks = await unitInUnitService.GetByParentIdAsync(unitId);
|
||||
|
||||
// ✅ Собрать все UnitId, участвующие в связях
|
||||
// Собрать все UnitId, участвующие в связях
|
||||
var allRelatedUnitIds = parentLinks
|
||||
.Select(l => l.ParentUnitId)
|
||||
.Concat(childLinks.Select(l => l.ChildUnitId))
|
||||
@@ -411,20 +409,20 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
|
||||
if (!allRelatedUnitIds.Any()) continue;
|
||||
|
||||
// ✅ Получить значения для всех связанных юнитов
|
||||
// Получить значения для всех связанных юнитов
|
||||
var allUnitValues = await unitInValueService.GetByUnitIdsAsync(allRelatedUnitIds);
|
||||
|
||||
// ✅ Сгруппировать значения по UnitId
|
||||
// Сгруппировать значения по UnitId
|
||||
var valuesByUnit = allUnitValues
|
||||
.GroupBy(uv => uv.UnitId)
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
// ✅ Найти UnitId, которые проходят все RelationshipFilters
|
||||
// Найти UnitId, которые проходят все RelationshipFilters
|
||||
var matchingUnitIds = new HashSet<Guid>();
|
||||
|
||||
foreach (var relatedUnitId in allRelatedUnitIds)
|
||||
{
|
||||
// ✅ Изменяем на `All` — все фильтры должны подходить
|
||||
// Изменяем на `All` — все фильтры должны подходить
|
||||
bool passesAllFilters = filter.RelationshipFilters.All(rf =>
|
||||
{
|
||||
var values = valuesByUnit.GetValueOrDefault(relatedUnitId, new List<UnitInValue>());
|
||||
@@ -435,14 +433,14 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
|
||||
if (!matchingValues.Any()) // Нет значений по полю
|
||||
{
|
||||
// ✅ Если нет значений, и IsInverse = false → не подходит
|
||||
// ✅ Если нет значений, и IsInverse = true → подходит
|
||||
// Если нет значений, и IsInverse = false → не подходит
|
||||
// Если нет значений, и IsInverse = true → подходит
|
||||
return rf.IsInverse;
|
||||
}
|
||||
|
||||
var hasMatch = matchingValues.Any(uv => uv.Value!.Value!.Contains(rf.ValueMask.Trim('%'), StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// ✅ Учитываем IsInverse
|
||||
// Учитываем IsInverse
|
||||
if (rf.IsInverse)
|
||||
hasMatch = !hasMatch;
|
||||
|
||||
@@ -490,7 +488,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
// ✅ Правильная логика: если IsInverse = true, то юнит подходит, если НЕ проходит фильтр
|
||||
// Правильная логика: если IsInverse = true, то юнит подходит, если НЕ проходит фильтр
|
||||
return fieldFilter.IsInverse ? !hasMatch : hasMatch;
|
||||
}).AsQueryable();
|
||||
}
|
||||
@@ -509,13 +507,13 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
var isFullMatch = relFilter.IsFullMatch;
|
||||
var fieldId = relFilter.FieldId;
|
||||
|
||||
bool isStartsWith = valueMask.EndsWith("%") && !valueMask.EndsWith("%%");
|
||||
bool isEndsWith = valueMask.StartsWith("%") && !valueMask.StartsWith("%%");
|
||||
// Используем Trim('%') для всех случаев
|
||||
var trimmedMask = valueMask.Trim('%');
|
||||
|
||||
return query.AsEnumerable().Where(dto =>
|
||||
{
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, это нужный юнит
|
||||
// Отладка: проверить, это нужный юнит
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug("Проверяем юнит {TargetUnitId} с {ParentCount} родителями и {ChildCount} детьми.",
|
||||
@@ -528,7 +526,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
if (links == null || !links.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
// ✅ Отладка: юнит не имеет связей
|
||||
// Отладка: юнит не имеет связей
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId}: нет связей ({Direction}), результат фильтра: {Result}",
|
||||
@@ -542,7 +540,7 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, какие связи у юнита
|
||||
// Отладка: проверить, какие связи у юнита
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug("Юнит {TargetUnitId}: {Count} связей ({Direction}).", targetUnitId, links.Count, isParent ? "Parent" : "Child");
|
||||
@@ -553,62 +551,72 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
}
|
||||
#endif
|
||||
|
||||
var hasMatchingLinks = links.Any(link =>
|
||||
if (!isFullMatch)
|
||||
{
|
||||
var hasMatch = link.Values.Any(v =>
|
||||
v.FieldId == fieldId &&
|
||||
v.Value != null &&
|
||||
(isStartsWith && isEndsWith ? v.Value.Contains(valueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
isStartsWith ? v.Value.StartsWith(valueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
isEndsWith ? v.Value.EndsWith(valueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, какая связь подходит
|
||||
if (dto.Id == targetUnitId)
|
||||
// Если IsFullMatch = false — проверяем, есть ли хотя бы одна подходящая связь
|
||||
var hasMatchingLinks = links.Any(link =>
|
||||
{
|
||||
logger.LogDebug(" Проверка связи {LinkUnitId}: hasMatch={HasMatch} (ValueMask={ValueMask}, FieldId={FieldId})", link.UnitId, hasMatch, valueMask, fieldId);
|
||||
}
|
||||
#endif
|
||||
var values = link.Values.Where(v => v.FieldId == fieldId).ToList();
|
||||
|
||||
return isInverse ? !hasMatch : hasMatch;
|
||||
});
|
||||
if (!values.Any())
|
||||
{
|
||||
// Если нет значений по полю — связь не подходит, если IsInverse = false
|
||||
// Если нет значений по полю — связь подходит, если IsInverse = true
|
||||
return isInverse;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: результат hasMatchingLinks
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug(" hasMatchingLinks = {HasMatchingLinks}, isInverse = {IsInverse}", hasMatchingLinks, isInverse);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isFullMatch)
|
||||
{
|
||||
var allMatch = links.All(link =>
|
||||
{
|
||||
var hasMatch = link.Values.Any(v =>
|
||||
v.FieldId == fieldId &&
|
||||
var hasMatch = values.Any(v =>
|
||||
v.Value != null &&
|
||||
(isStartsWith && isEndsWith ? v.Value.Contains(valueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
isStartsWith ? v.Value.StartsWith(valueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
isEndsWith ? v.Value.EndsWith(valueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
v.Value.Contains(valueMask, StringComparison.OrdinalIgnoreCase))
|
||||
// Проверка с Trim('%')
|
||||
(valueMask.StartsWith("%") && valueMask.EndsWith("%") ? v.Value.Contains(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
valueMask.StartsWith("%") ? v.Value.EndsWith(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
valueMask.EndsWith("%") ? v.Value.StartsWith(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
string.Equals(v.Value, valueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: проверить, все ли связи подходят
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug(" Проверка связи {LinkUnitId} для FullMatch: hasMatch={HasMatch}", link.UnitId, hasMatch);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Учитываем IsInverse
|
||||
return isInverse ? !hasMatch : hasMatch;
|
||||
});
|
||||
|
||||
#if DEBUG
|
||||
// ✅ Отладка: результат allMatch
|
||||
// Отладка: результат hasMatchingLinks
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug(" hasMatchingLinks = {HasMatchingLinks}, isInverse = {IsInverse}", hasMatchingLinks, isInverse);
|
||||
}
|
||||
#endif
|
||||
|
||||
return hasMatchingLinks;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Если IsFullMatch = true — все связи должны подходить
|
||||
var allMatch = links.All(link =>
|
||||
{
|
||||
var values = link.Values.Where(v => v.FieldId == fieldId).ToList();
|
||||
|
||||
if (!values.Any())
|
||||
{
|
||||
// Если нет значений по полю — связь не подходит, если IsInverse = false
|
||||
// Если нет значений по полю — связь подходит, если IsInverse = true
|
||||
return isInverse;
|
||||
}
|
||||
|
||||
var hasMatch = values.Any(v =>
|
||||
v.Value != null &&
|
||||
// Проверка с Trim('%')
|
||||
(valueMask.StartsWith("%") && valueMask.EndsWith("%") ? v.Value.Contains(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
valueMask.StartsWith("%") ? v.Value.EndsWith(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
valueMask.EndsWith("%") ? v.Value.StartsWith(trimmedMask, StringComparison.OrdinalIgnoreCase) :
|
||||
string.Equals(v.Value, valueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
// Учитываем IsInverse
|
||||
return isInverse ? !hasMatch : hasMatch;
|
||||
});
|
||||
|
||||
#if DEBUG
|
||||
// Отладка: результат allMatch
|
||||
if (dto.Id == targetUnitId)
|
||||
{
|
||||
logger.LogDebug(" allMatch = {AllMatch}, isFullMatch = {IsFullMatch}", allMatch, isFullMatch);
|
||||
@@ -617,8 +625,6 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
|
||||
return allMatch;
|
||||
}
|
||||
|
||||
return hasMatchingLinks;
|
||||
}).AsQueryable();
|
||||
}
|
||||
|
||||
@@ -639,57 +645,171 @@ namespace PARR.DAL.DomainServices.Implementations
|
||||
|
||||
logger.LogDebug("RelationshipCountFilter: Min={Min}, Max={Max}, IsParent={IsParent}", min, max, isParentDirection);
|
||||
|
||||
// Определяем, есть ли фильтры по полям
|
||||
var activeFilters = relationshipFilters
|
||||
.Where(rf => rf.IsParent == isParentDirection && !string.IsNullOrWhiteSpace(rf.ValueMask))
|
||||
// Разделяем фильтры по направлениям
|
||||
var parentFilters = relationshipFilters
|
||||
.Where(rf => rf.IsParent && !string.IsNullOrWhiteSpace(rf.ValueMask))
|
||||
.ToList();
|
||||
|
||||
bool hasFieldFilters = activeFilters.Count > 0;
|
||||
var childFilters = relationshipFilters
|
||||
.Where(rf => !rf.IsParent && !string.IsNullOrWhiteSpace(rf.ValueMask))
|
||||
.ToList();
|
||||
|
||||
logger.LogDebug("RelationshipCountFilter: Найдено {Count} фильтров по полям для направления {Direction}", activeFilters.Count, isParentDirection ? "Parent" : "Child");
|
||||
bool hasParentFieldFilters = parentFilters.Count > 0;
|
||||
bool hasChildFieldFilters = childFilters.Count > 0;
|
||||
|
||||
logger.LogDebug("RelationshipCountFilter: Найдено {ParentCount} фильтров по родителям и {ChildCount} фильтров по детям", parentFilters.Count, childFilters.Count);
|
||||
|
||||
return units.Where(dto =>
|
||||
{
|
||||
var links = isParentDirection ? dto.Parents : dto.Children;
|
||||
|
||||
if (links == null || !links.Any())
|
||||
{
|
||||
var result = min == 0;
|
||||
logger.LogDebug("UnitId {UnitId}: связей нет (null или пусто). Min={Min}, результат фильтра: {Result}", dto.Id, min, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
logger.LogDebug("UnitId {UnitId}: {Count} связей до фильтрации", dto.Id, links.Count);
|
||||
|
||||
int matchingCount = 0;
|
||||
|
||||
if (hasFieldFilters)
|
||||
if (isParentDirection) // Считаем родительские связи
|
||||
{
|
||||
// Есть фильтры по полям → считаем только связанные юниты, подходящие под фильтр
|
||||
foreach (var link in links)
|
||||
var links = dto.Parents;
|
||||
|
||||
if (links == null || !links.Any())
|
||||
{
|
||||
// ✅ Изменяем на `All` — все фильтры должны подходить
|
||||
bool hasMatch = activeFilters.All(f =>
|
||||
{
|
||||
var value = link.Values.FirstOrDefault(v => v.FieldId == f.FieldId);
|
||||
return value != null && value.Value != null && value.Value.Contains(f.ValueMask, StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
var result = min == 0;
|
||||
logger.LogDebug("UnitId {UnitId}: родительских связей нет (null или пусто). Min={Min}, результат фильтра: {Result}", dto.Id, min, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (hasMatch)
|
||||
matchingCount++;
|
||||
logger.LogDebug("UnitId {UnitId}: {Count} родительских связей до фильтрации", dto.Id, links.Count);
|
||||
|
||||
if (matchingCount > max)
|
||||
if (hasParentFieldFilters)
|
||||
{
|
||||
// Есть фильтры по родителям → считаем только подходящие
|
||||
foreach (var link in links)
|
||||
{
|
||||
logger.LogDebug("UnitId {UnitId}: matchingCount ({Count}) > max ({Max}) — прерываем подсчёт", dto.Id, matchingCount, max);
|
||||
break;
|
||||
// Проверяем, проходит ли связь все фильтры (логика как в ApplyRelationshipFilterToQuery)
|
||||
bool passesAllFilters = true;
|
||||
|
||||
foreach (var f in parentFilters)
|
||||
{
|
||||
var values = link.Values.Where(v => v.FieldId == f.FieldId).ToList();
|
||||
|
||||
if (!values.Any())
|
||||
{
|
||||
// Если нет значений по полю — связь не подходит, если IsInverse = false
|
||||
// Если нет значений по полю — связь подходит, если IsInverse = true
|
||||
if (!f.IsInverse)
|
||||
{
|
||||
passesAllFilters = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasMatch = values.Any(v =>
|
||||
v.Value != null &&
|
||||
// Проверка в зависимости от маски
|
||||
(f.ValueMask.StartsWith("%") && f.ValueMask.EndsWith("%") ? v.Value.Contains(f.ValueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
f.ValueMask.StartsWith("%") ? v.Value.EndsWith(f.ValueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
f.ValueMask.EndsWith("%") ? v.Value.StartsWith(f.ValueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
string.Equals(v.Value, f.ValueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
// Учитываем IsInverse
|
||||
if (f.IsInverse)
|
||||
hasMatch = !hasMatch;
|
||||
|
||||
if (!hasMatch)
|
||||
{
|
||||
passesAllFilters = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (passesAllFilters)
|
||||
matchingCount++;
|
||||
|
||||
if (matchingCount > max)
|
||||
{
|
||||
logger.LogDebug("UnitId {UnitId}: matchingCount ({Count}) > max ({Max}) — прерываем подсчёт", dto.Id, matchingCount, max);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Нет фильтров по родителям → считаем общее количество связей (без учёта значений)
|
||||
matchingCount = links.Count;
|
||||
logger.LogDebug("UnitId {UnitId}: нет фильтров по родителям — matchingCount = links.Count = {Count}", dto.Id, matchingCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
else // Считаем дочерние связи
|
||||
{
|
||||
// Нет фильтров по полям → считаем общее количество связей (без учёта значений)
|
||||
matchingCount = links.Count;
|
||||
logger.LogDebug("UnitId {UnitId}: нет фильтров по полям — matchingCount = links.Count = {Count}", dto.Id, matchingCount);
|
||||
var links = dto.Children;
|
||||
|
||||
if (links == null || !links.Any())
|
||||
{
|
||||
var result = min == 0;
|
||||
logger.LogDebug("UnitId {UnitId}: дочерних связей нет (null или пусто). Min={Min}, результат фильтра: {Result}", dto.Id, min, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
logger.LogDebug("UnitId {UnitId}: {Count} дочерних связей до фильтрации", dto.Id, links.Count);
|
||||
|
||||
if (hasChildFieldFilters)
|
||||
{
|
||||
// Есть фильтры по детям → считаем только подходящие
|
||||
foreach (var link in links)
|
||||
{
|
||||
// Проверяем, проходит ли связь все фильтры (логика как в ApplyRelationshipFilterToQuery)
|
||||
bool passesAllFilters = true;
|
||||
|
||||
foreach (var f in childFilters)
|
||||
{
|
||||
var values = link.Values.Where(v => v.FieldId == f.FieldId).ToList();
|
||||
|
||||
if (!values.Any())
|
||||
{
|
||||
// Если нет значений по полю — связь не подходит, если IsInverse = false
|
||||
// Если нет значений по полю — связь подходит, если IsInverse = true
|
||||
if (!f.IsInverse)
|
||||
{
|
||||
passesAllFilters = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasMatch = values.Any(v =>
|
||||
v.Value != null &&
|
||||
// Проверка в зависимости от маски
|
||||
(f.ValueMask.StartsWith("%") && f.ValueMask.EndsWith("%") ? v.Value.Contains(f.ValueMask.Trim('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
f.ValueMask.StartsWith("%") ? v.Value.EndsWith(f.ValueMask.TrimStart('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
f.ValueMask.EndsWith("%") ? v.Value.StartsWith(f.ValueMask.TrimEnd('%'), StringComparison.OrdinalIgnoreCase) :
|
||||
string.Equals(v.Value, f.ValueMask, StringComparison.OrdinalIgnoreCase))
|
||||
);
|
||||
|
||||
// Учитываем IsInverse
|
||||
if (f.IsInverse)
|
||||
hasMatch = !hasMatch;
|
||||
|
||||
if (!hasMatch)
|
||||
{
|
||||
passesAllFilters = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (passesAllFilters)
|
||||
matchingCount++;
|
||||
|
||||
if (matchingCount > max)
|
||||
{
|
||||
logger.LogDebug("UnitId {UnitId}: matchingCount ({Count}) > max ({Max}) — прерываем подсчёт", dto.Id, matchingCount, max);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Нет фильтров по детям → считаем общее количество связей (без учёта значений)
|
||||
matchingCount = links.Count;
|
||||
logger.LogDebug("UnitId {UnitId}: нет фильтров по детям — matchingCount = links.Count = {Count}", dto.Id, matchingCount);
|
||||
}
|
||||
}
|
||||
|
||||
var finalResult = matchingCount >= min && matchingCount <= max;
|
||||
|
||||
Reference in New Issue
Block a user