I currently have the following method:
private FilterDefinition<BsonDocument> BuildFilters(GenericQueryParameters parameters)
{
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
foreach (string filterString in parameters.Filters)
{
Match match = Regex.Match(filterString, @"(\w+)\s*([<>=]+)\s*(.*)", RegexOptions.IgnoreCase);
if (match.Success)
{
string fieldName = match.Groups[1].Value;
string condition = match.Groups[2].Value;
string targetValue = match.Groups[3].Value;
if ("==".Equals(condition))
{
filters = filters & filterBuilder.Eq(fieldName, targetValue);
}
else if ("!=".Equals(condition))
{
filters = filters & filterBuilder.Ne(fieldName, targetValue);
}
else if ("<=".Equals(condition))
{
filters = filters & filterBuilder.Lte(fieldName, targetValue);
}
else if ("<".Equals(condition))
{
filters = filters & filterBuilder.Lt(fieldName, targetValue);
}
else if (">=".Equals(condition))
{
filters = filters & filterBuilder.Gte(fieldName, targetValue);
}
else if (">".Equals(condition))
{
filters = filters & filterBuilder.Gt(fieldName, targetValue);
}
else
{
throw new MalformedDatabaseFilterException($"The condition {condition} could not be applied.");
}
}
else
{
throw new MalformedDatabaseFilterException($"The filter {filterString} could not be parsed.");
}
}
return filters;
}
This works, but it is very long and repetative. I'd like to make it something more like:
private static readonly Dictionary<string, Action> FilterBySymbols = new Dictionary<string, Action>() {
{"==", FilterDefinitionBuilder<BsonDocument>.Eq },
{"!=", FilterDefinitionBuilder<BsonDocument>.Ne },
{"<=", FilterDefinitionBuilder<BsonDocument>.Lte },
{"<", FilterDefinitionBuilder<BsonDocument>.Lt },
{">=", FilterDefinitionBuilder<BsonDocument>.Gte },
{">", FilterDefinitionBuilder<BsonDocument>.Gt }
};
private FilterDefinition<BsonDocument> BuildFilters(GenericQueryParameters parameters)
{
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
foreach (string filterString in parameters.Filters)
{
Match match = Regex.Match(filterString, @"(\w+)\s*([<>=]+)\s*(.*)", RegexOptions.IgnoreCase);
if (match.Success)
{
string fieldName = match.Groups[1].Value;
string condition = match.Groups[2].Value;
string targetValue = match.Groups[3].Value;
if (FilterBySymbols.TryGetValue(condition, out Action action))
{
filters = filters & action(fieldName, targetValue);
}
else
{
throw new MalformedDatabaseFilterException($"The condition {condition} could not be applied.");
}
}
else
{
throw new MalformedDatabaseFilterException($"The filter {filterString} could not be parsed.");
}
}
return filters;
}
... but this won't compile since the actions are all instance methods. Also, once I get the method out of the map, there is an open question of how I might apply it to the instance.
Any ideas whether/how I could make the later version work?
You could have a Dictionary<string, Action<FilterDefinitionBuilder<BsonDocument>, string, string>>
. Initialize it as:
{ "==", (builder, fieldName, targetValue) => builder.Eq(fieldName, targetValue) },
{ "!=", (builder, fieldName, targetValue) => builder.Neq(fieldName, targetValue) }
and call it as:
if (FilterBySymbols.TryGetValue(condition, out Action action))
{
action(filterBuilder, fieldName, targetValue);
}
That said, I'd probably write it using a switch expression: that's going to be both clearer and cheaper:
filters = filters & condition switch
{
"==" => filterBuilder.Eq(fieldName, targetValue),
"!=" => filterBuilder.Neq(fieldName, targetValue),
...
_ => throw new MalformedDatabaseFilterException($"The condition {condition} could not be applied.");
}