-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Mongo project with basic builder and visitor * Don't restrict build-test workflow to master * Add some basic resolvers for simple types and strings
- Loading branch information
Showing
15 changed files
with
715 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,7 @@ name: Build & Test | |
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
jobs: | ||
build: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<PackageId>AnQL.Functions.Time</PackageId> | ||
<Authors>Taylor Graham</Authors> | ||
<RepositoryUrl>https://github.com/twgraham/AnQL</RepositoryUrl> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="MongoDB.Driver" Version="2.18.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\AnQL.Core\AnQL.Core.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="Helpers" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using AnQL.Core; | ||
using AnQL.Mongo.Resolvers; | ||
using MongoDB.Driver; | ||
|
||
namespace AnQL.Mongo; | ||
|
||
public static class AnQLBuilderExtensions | ||
{ | ||
public static FilterDefinitionAnQLParserBuilder<T> ForFilterDefinitions<T>(this AnQLBuilder anQlBuilder) | ||
{ | ||
return anQlBuilder.For<FilterDefinitionAnQLParserBuilder<T>, FilterDefinition<T>, T>(Create<T>); | ||
} | ||
|
||
private static FilterDefinitionAnQLParserBuilder<T> Create<T>(AnQLParserOptions options) | ||
{ | ||
var builder = new FilterDefinitionAnQLParserBuilder<T>(options); | ||
|
||
builder.RegisterFactory(typeof(string), new StringResolver<T>.Factory()); | ||
builder.RegisterSimpleType<ushort>() | ||
.RegisterSimpleType<short>() | ||
.RegisterSimpleType<uint>() | ||
.RegisterSimpleType<int>() | ||
.RegisterSimpleType<ulong>() | ||
.RegisterSimpleType<long>() | ||
.RegisterSimpleType<float>() | ||
.RegisterSimpleType<double>() | ||
.RegisterSimpleType<decimal>() | ||
.RegisterSimpleType<DateTime>() | ||
.RegisterSimpleType<DateTimeOffset>() | ||
.RegisterSimpleType<DateOnly>() | ||
.RegisterSimpleType<bool>(); | ||
|
||
return builder; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using AnQL.Core; | ||
using AnQL.Core.Extensions; | ||
using AnQL.Core.Grammar; | ||
using AnQL.Core.Resolvers; | ||
using MongoDB.Driver; | ||
|
||
namespace AnQL.Mongo; | ||
|
||
public class AnQLFilterDefinitionVisitor<T> : AnQLBaseVisitor<FilterDefinition<T>> | ||
{ | ||
private readonly ResolverMap<FilterDefinition<T>, T> _resolverMap; | ||
|
||
public override FilterDefinition<T> SuccessQueryResult => Builders<T>.Filter.Empty; | ||
public override FilterDefinition<T> FailedQueryResult => "{ $expr: false }"; | ||
|
||
public AnQLFilterDefinitionVisitor(ResolverMap<FilterDefinition<T>, T> resolverMap, AnQLParserOptions options) : base(options) | ||
{ | ||
_resolverMap = resolverMap; | ||
} | ||
|
||
public override FilterDefinition<T> VisitExprAND(AnQLGrammarParser.ExprANDContext context) | ||
{ | ||
var left = Visit(context.expr(0)); | ||
var right = Visit(context.expr(1)); | ||
|
||
return left & right; | ||
} | ||
|
||
public override FilterDefinition<T> VisitExprOR(AnQLGrammarParser.ExprORContext context) | ||
{ | ||
var left = Visit(context.expr(0)); | ||
var right = Visit(context.expr(1)); | ||
|
||
return left | right; | ||
} | ||
|
||
public override FilterDefinition<T> VisitNOT(AnQLGrammarParser.NOTContext context) | ||
{ | ||
var inner = Visit(context.expr()); | ||
return !inner; | ||
} | ||
|
||
public override FilterDefinition<T> VisitParens(AnQLGrammarParser.ParensContext context) | ||
{ | ||
return Visit(context.expr()); | ||
} | ||
|
||
public override FilterDefinition<T> VisitEqual(AnQLGrammarParser.EqualContext context) | ||
{ | ||
return BuildFilter(QueryOperation.Equal, context.property_path(), context.value()); | ||
} | ||
|
||
public override FilterDefinition<T> VisitAnyEqual(AnQLGrammarParser.AnyEqualContext context) | ||
{ | ||
var filters = context.value().Select(value => BuildFilter(QueryOperation.Equal, context.property_path(), value)); | ||
return Builders<T>.Filter.Or(filters); | ||
} | ||
|
||
public override FilterDefinition<T> VisitGreaterThan(AnQLGrammarParser.GreaterThanContext context) | ||
{ | ||
return BuildFilter(QueryOperation.GreaterThan, context.property_path(), context.value()); | ||
} | ||
|
||
public override FilterDefinition<T> VisitLessThan(AnQLGrammarParser.LessThanContext context) | ||
{ | ||
return BuildFilter(QueryOperation.LessThan, context.property_path(), context.value()); | ||
} | ||
|
||
private FilterDefinition<T> BuildFilter(QueryOperation operation, AnQLGrammarParser.Property_pathContext propertyPathContext, | ||
AnQLGrammarParser.ValueContext valueContext) | ||
{ | ||
if (!_resolverMap.TryGet(propertyPathContext.GetText(), out var resolver)) | ||
return HandleUnknownProperty(propertyPathContext); | ||
|
||
var (value, type) = valueContext.GetValueAndAnQLType(); | ||
|
||
return resolver.Resolve(operation, value, type); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using AnQL.Core; | ||
using AnQL.Mongo.Resolvers; | ||
using MongoDB.Driver; | ||
|
||
namespace AnQL.Mongo; | ||
|
||
public class FilterDefinitionAnQLParserBuilder<T> : AnQLParserBuilder<FilterDefinition<T>, T> | ||
{ | ||
public FilterDefinitionAnQLParserBuilder(AnQLParserOptions options) : base(options) | ||
{ | ||
} | ||
|
||
public FilterDefinitionAnQLParserBuilder<T> RegisterSimpleType<TType>() | ||
{ | ||
return (FilterDefinitionAnQLParserBuilder<T>)RegisterFactory(typeof(TType), | ||
new SimpleResolver<T, TType>.Factory()); | ||
} | ||
|
||
public override IAnQLParser<FilterDefinition<T>> Build() | ||
{ | ||
return new AnQLParser<FilterDefinition<T>>(new AnQLFilterDefinitionVisitor<T>(ResolverMap, Options)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using AnQL.Core.Helpers; | ||
using AnQL.Core.Resolvers; | ||
using MongoDB.Driver; | ||
|
||
namespace AnQL.Mongo.Resolvers; | ||
|
||
public class SimpleResolver<T, TValue> : IAnQLPropertyResolver<FilterDefinition<T>> | ||
{ | ||
private readonly Options _options = new(); | ||
|
||
protected Expression<Func<T, TValue>> PropertyPath { get; } | ||
|
||
public SimpleResolver(Expression<Func<T, TValue>> propertyPath, Action<Options>? configureOptions = null) | ||
{ | ||
PropertyPath = propertyPath; | ||
configureOptions?.Invoke(_options); | ||
} | ||
|
||
public virtual FilterDefinition<T> Resolve(QueryOperation op, string value, AnQLValueType valueType) | ||
{ | ||
var converter = _options.ValueConverter ?? DefaultConverter; | ||
var convertedValue = converter(value, valueType); | ||
|
||
return op switch | ||
{ | ||
QueryOperation.Equal => BuildEqual(convertedValue), | ||
QueryOperation.GreaterThan => BuildGreaterThan(convertedValue), | ||
QueryOperation.LessThan => BuildLessThan(convertedValue), | ||
_ => throw new ArgumentOutOfRangeException(nameof(op), op, null) | ||
}; | ||
} | ||
|
||
protected FilterDefinition<T> BuildEqual(TValue value) | ||
=> Builders<T>.Filter.Eq(PropertyPath, value); | ||
|
||
protected FilterDefinition<T> BuildGreaterThan(TValue value) | ||
=> Builders<T>.Filter.Gt(PropertyPath, value); | ||
|
||
protected FilterDefinition<T> BuildLessThan(TValue value) | ||
=> Builders<T>.Filter.Lt(PropertyPath, value); | ||
|
||
private TValue DefaultConverter(string queryValue, AnQLValueType valueType) | ||
{ | ||
return (TValue) Convert.ChangeType(queryValue, typeof(TValue)); | ||
} | ||
|
||
public class Factory : IResolverFactory<T, FilterDefinition<T>> | ||
{ | ||
private static BindingFlags _flags = BindingFlags.CreateInstance | | ||
BindingFlags.Public | | ||
BindingFlags.Instance | | ||
BindingFlags.OptionalParamBinding; | ||
|
||
public IAnQLPropertyResolver<FilterDefinition<T>> Build(Expression<Func<T, object>> propertyPath) | ||
{ | ||
var propertyType = ExpressionHelper.GetPropertyPathType(propertyPath); | ||
var resolverType = typeof(SimpleResolver<,>).MakeGenericType(typeof(T), propertyType); | ||
var unconvertedPath = ExpressionHelper.StripConvert(propertyPath); | ||
|
||
var resolver = Activator.CreateInstance(resolverType, _flags, null, new object?[] { unconvertedPath }, null) | ||
?? throw new Exception("Unable to create resolver"); | ||
|
||
return (IAnQLPropertyResolver<FilterDefinition<T>>)resolver; | ||
} | ||
} | ||
|
||
public class Options | ||
{ | ||
public Func<string, AnQLValueType, TValue>? ValueConverter { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using System.Linq.Expressions; | ||
using System.Text.RegularExpressions; | ||
using AnQL.Core.Helpers; | ||
using AnQL.Core.Resolvers; | ||
using MongoDB.Driver; | ||
|
||
namespace AnQL.Mongo.Resolvers; | ||
|
||
public class StringResolver<T> : SimpleResolver<T, string> | ||
{ | ||
private readonly Options _options = new(); | ||
|
||
public StringResolver(Expression<Func<T, string>> propertyPath, Action<Options>? configureOptions = null) | ||
: base(propertyPath) | ||
{ | ||
configureOptions?.Invoke(_options); | ||
} | ||
|
||
public override FilterDefinition<T> Resolve(QueryOperation op, string value, AnQLValueType valueType) | ||
{ | ||
if (_options.RegexMatching && op == QueryOperation.Equal) | ||
return Builders<T>.Filter.Regex(new ExpressionFieldDefinition<T>(PropertyPath), new Regex(value, _options.RegexOptions)); | ||
|
||
return op switch | ||
{ | ||
QueryOperation.Equal => BuildEqual(value), | ||
QueryOperation.GreaterThan => BuildGreaterThan(value), | ||
QueryOperation.LessThan => BuildLessThan(value), | ||
_ => throw new ArgumentOutOfRangeException(nameof(op)) | ||
}; | ||
} | ||
|
||
public new class Factory : IResolverFactory<T, FilterDefinition<T>> | ||
{ | ||
public IAnQLPropertyResolver<FilterDefinition<T>> Build(Expression<Func<T, object>> propertyPath) | ||
{ | ||
var propertyType = ExpressionHelper.GetPropertyPathType(propertyPath); | ||
if (propertyType != typeof(string)) | ||
throw new ArgumentException("Property should be a string", nameof(propertyPath)); | ||
|
||
var stringExpression = (Expression<Func<T, string>>) ExpressionHelper.StripConvert(propertyPath); | ||
|
||
return new StringResolver<T>(stringExpression); | ||
} | ||
} | ||
|
||
public new class Options | ||
{ | ||
public bool RegexMatching { get; set; } = false; | ||
public RegexOptions RegexOptions { get; set; } = RegexOptions.None; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="6.7.0" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> | ||
<PackageReference Include="xunit" Version="2.4.2" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="3.1.2"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\AnQL.Mongo\AnQL.Mongo.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.