Update `OptionalSet` implementation to intersect across multiple filters rather than union

This commit is contained in:
Bartłomiej Dach 2024-03-26 12:20:38 +01:00
parent c24eb066dc
commit e52d51cd0a
No known key found for this signature in database
2 changed files with 56 additions and 39 deletions

View File

@ -114,17 +114,18 @@ public string SearchText
public IRulesetFilterCriteria? RulesetCriteria { get; set; } public IRulesetFilterCriteria? RulesetCriteria { get; set; }
public struct OptionalSet<T> : IEquatable<OptionalSet<T>> public readonly struct OptionalSet<T> : IEquatable<OptionalSet<T>>
where T : struct where T : struct, Enum
{ {
public bool HasFilter => Values.Count > 0; public bool HasFilter => true;
public bool IsInRange(T value) => Values.Contains(value); public bool IsInRange(T value) => Values.Contains(value);
public HashSet<T> Values = new HashSet<T>(); public HashSet<T> Values { get; }
public OptionalSet() public OptionalSet()
{ {
Values = Enum.GetValues<T>().ToHashSet();
} }
public bool Equals(OptionalSet<T> other) => Values.SetEquals(other.Values); public bool Equals(OptionalSet<T> other) => Values.SetEquals(other.Values);

View File

@ -69,7 +69,7 @@ private static bool tryParseKeywordCriteria(FilterCriteria criteria, string key,
return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt); return TryUpdateCriteriaRange(ref criteria.BeatDivisor, op, value, tryParseInt);
case "status": case "status":
return TryUpdateCriteriaSet(ref criteria.OnlineStatus, op, value, tryParseEnum); return TryUpdateCriteriaSet(ref criteria.OnlineStatus, op, value);
case "creator": case "creator":
case "author": case "author":
@ -302,54 +302,70 @@ public static bool TryUpdateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T>
/// <summary> /// <summary>
/// Attempts to parse a keyword filter of type <typeparamref name="T"/>, /// Attempts to parse a keyword filter of type <typeparamref name="T"/>,
/// from the specified <paramref name="op"/> and <paramref name="val"/>. /// from the specified <paramref name="op"/> and <paramref name="filterValue"/>.
/// If <paramref name="val"/> can be parsed into <typeparamref name="T"/> using <paramref name="parseFunction"/>, the function returns <c>true</c> /// If <paramref name="filterValue"/> can be parsed successfully, the function returns <c>true</c>
/// and the resulting range constraint is stored into the <paramref name="range"/>'s expected values. /// and the resulting range constraint is stored into the <paramref name="range"/>'s expected values.
/// </summary> /// </summary>
/// <param name="range">The <see cref="FilterCriteria.OptionalSet{T}"/> to store the parsed data into, if successful.</param> /// <param name="range">The <see cref="FilterCriteria.OptionalSet{T}"/> to store the parsed data into, if successful.</param>
/// <param name="op">The operator for the keyword filter.</param> /// <param name="op">The operator for the keyword filter.</param>
/// <param name="val">The value of the keyword filter.</param> /// <param name="filterValue">The value of the keyword filter.</param>
/// <param name="parseFunction">Function used to determine if <paramref name="val"/> can be converted to type <typeparamref name="T"/>.</param> public static bool TryUpdateCriteriaSet<T>(ref FilterCriteria.OptionalSet<T> range, Operator op, string filterValue)
public static bool TryUpdateCriteriaSet<T>(ref FilterCriteria.OptionalSet<T> range, Operator op, string val, TryParseFunction<T> parseFunction)
where T : struct, Enum
=> parseFunction.Invoke(val, out var converted) && tryUpdateCriteriaSet(ref range, op, converted);
private static bool tryUpdateCriteriaSet<T>(ref FilterCriteria.OptionalSet<T> range, Operator op, T pivotValue)
where T : struct, Enum where T : struct, Enum
{ {
var allDefinedValues = Enum.GetValues<T>(); var matchingValues = new HashSet<T>();
foreach (var val in allDefinedValues) if (op == Operator.Equal && filterValue.Contains(','))
{ {
int compareResult = Comparer<T>.Default.Compare(val, pivotValue); string[] splitValues = filterValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
switch (op) foreach (string splitValue in splitValues)
{ {
case Operator.Less: if (!tryParseEnum<T>(splitValue, out var parsedValue))
if (compareResult < 0) range.Values.Add(val);
break;
case Operator.LessOrEqual:
if (compareResult <= 0) range.Values.Add(val);
break;
case Operator.Equal:
if (compareResult == 0) range.Values.Add(val);
break;
case Operator.GreaterOrEqual:
if (compareResult >= 0) range.Values.Add(val);
break;
case Operator.Greater:
if (compareResult > 0) range.Values.Add(val);
break;
default:
return false; return false;
matchingValues.Add(parsedValue);
}
}
else
{
if (!tryParseEnum<T>(filterValue, out var pivotValue))
return false;
var allDefinedValues = Enum.GetValues<T>();
foreach (var val in allDefinedValues)
{
int compareResult = Comparer<T>.Default.Compare(val, pivotValue);
switch (op)
{
case Operator.Less:
if (compareResult < 0) matchingValues.Add(val);
break;
case Operator.LessOrEqual:
if (compareResult <= 0) matchingValues.Add(val);
break;
case Operator.Equal:
if (compareResult == 0) matchingValues.Add(val);
break;
case Operator.GreaterOrEqual:
if (compareResult >= 0) matchingValues.Add(val);
break;
case Operator.Greater:
if (compareResult > 0) matchingValues.Add(val);
break;
default:
return false;
}
} }
} }
range.Values.IntersectWith(matchingValues);
return true; return true;
} }