in src/Microsoft.Health.Fhir.Core/Features/Search/Expressions/Parsers/SearchParameterExpressionParser.cs [141:266]
private Expression Build(
SearchParameterInfo searchParameter,
SearchModifier modifier,
int? componentIndex,
string value)
{
ReadOnlySpan<char> valueSpan = value.AsSpan();
// By default, the comparator is equal.
SearchComparator comparator = SearchComparator.Eq;
if (searchParameter.Type == SearchParamType.Date ||
searchParameter.Type == SearchParamType.Number ||
searchParameter.Type == SearchParamType.Quantity)
{
// If the search parameter type supports comparator, parse the comparator (if present).
Tuple<string, SearchComparator> matchedComparator = SearchParamComparators.FirstOrDefault(
s => value.StartsWith(s.Item1, StringComparison.Ordinal));
if (matchedComparator != null)
{
comparator = matchedComparator.Item2;
valueSpan = valueSpan.Slice(matchedComparator.Item1.Length);
}
}
// Parse the value.
Func<string, ISearchValue> parser = _parserDictionary[Enum.Parse<SearchParamType>(searchParameter.Type.ToString())];
// Build the expression.
var helper = new SearchValueExpressionBuilderHelper();
// If the value contains comma, then we need to convert it into in expression.
// But in this case, the user cannot specify prefix.
IReadOnlyList<string> parts = value.SplitByOrSeparator();
if (parts.Count == 1)
{
// This is a single value expression.
ISearchValue searchValue = parser(valueSpan.ToString());
searchValue = ApplyTargetTypeModifier(modifier, searchValue);
return helper.Build(
searchParameter.Code,
modifier,
comparator,
componentIndex,
searchValue);
}
else
{
if (comparator != SearchComparator.Eq)
{
throw new InvalidSearchOperationException(Resources.SearchComparatorNotSupported);
}
// This is a multiple value expression.
if (modifier?.SearchModifierCode == SearchModifierCode.Not)
{
Expression[] expressions = parts.Select(part =>
{
ISearchValue searchValue = parser(part);
return helper.Build(
searchParameter.Code,
null,
comparator,
componentIndex,
searchValue);
}).ToArray();
return Expression.Not(Expression.Or(expressions));
}
else
{
Expression[] expressions = parts.Select(part =>
{
ISearchValue searchValue = parser(part);
searchValue = ApplyTargetTypeModifier(modifier, searchValue);
return helper.Build(
searchParameter.Code,
modifier,
comparator,
componentIndex,
searchValue);
}).ToArray();
return Expression.Or(expressions);
}
}
ISearchValue ApplyTargetTypeModifier(SearchModifier modifier, ISearchValue source)
{
var referenceSearchValue = source as ReferenceSearchValue;
if (referenceSearchValue == null || modifier?.SearchModifierCode != SearchModifierCode.Type)
{
return source;
}
if (!string.IsNullOrEmpty(referenceSearchValue.ResourceType))
{
if (string.Equals(referenceSearchValue.ResourceType, modifier.ResourceType, StringComparison.OrdinalIgnoreCase))
{
return source;
}
throw new InvalidSearchOperationException(
string.Format(Core.Resources.ModifierNotSupported, modifier, searchParameter.Code));
}
try
{
return new ReferenceSearchValue(
referenceSearchValue.Kind,
referenceSearchValue.BaseUri,
modifier.ResourceType,
referenceSearchValue.ResourceId);
}
catch (ArgumentException)
{
throw new InvalidSearchOperationException(
string.Format(Core.Resources.ModifierNotSupported, modifier, searchParameter.Code));
}
}
}