Skip to content

Commit

Permalink
More refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsmith committed Mar 15, 2024
1 parent 708c8a2 commit f423023
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 38 deletions.
29 changes: 12 additions & 17 deletions src/Foundatio.Parsers.SqlQueries/SqlQueryParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,14 @@ public override async Task<IQueryNode> ParseAsync(string query, IQueryVisitorCon
}

private static readonly ConcurrentDictionary<IEntityType, List<EntityFieldInfo>> _entityFieldCache = new();

public async Task<QueryValidationResult> ValidateAsync(string query, IEntityType entityType)
public async Task<QueryValidationResult> ValidateAsync(string query, SqlQueryVisitorContext context)
{
var context = await GetContextAsync(entityType);
var node = await ParseAsync(query, context);
return await ValidationVisitor.RunAsync(node, context);
}

public async Task<string> ToSqlAsync(string query, IEntityType entityType)
public async Task<string> ToSqlAsync(string query, SqlQueryVisitorContext context)
{
var context = await GetContextAsync(entityType);
var node = await ParseAsync(query, context);
var result = await ValidationVisitor.RunAsync(node, context);
if (!result.IsValid)
Expand All @@ -73,30 +70,28 @@ public async Task<string> ToSqlAsync(string query, IEntityType entityType)
return await GenerateSqlVisitor.RunAsync(node, context);
}

private async Task<SqlQueryVisitorContext> GetContextAsync(IEntityType entityType)
public SqlQueryVisitorContext GetContext(IEntityType entityType)
{
if (!_entityFieldCache.TryGetValue(entityType, out var fields))
{
fields = new List<EntityFieldInfo>();
AddFields(fields, entityType);

if (Configuration.EntityTypeDynamicFieldResolver != null)
{
var dynamicFields = await Configuration.EntityTypeDynamicFieldResolver!.Invoke(entityType) ?? [];
fields.AddRange(dynamicFields);
}

AddEntityFields(fields, entityType);
_entityFieldCache.TryAdd(entityType, fields);
}

var validationOptions = new QueryValidationOptions();
foreach (string field in fields.Select(f => f.Field))
validationOptions.AllowedFields.Add(field);

Configuration.SetValidationOptions(validationOptions);
return new SqlQueryVisitorContext { Fields = fields };
return new SqlQueryVisitorContext
{
Fields = fields,
ValidationOptions = validationOptions
};
}

private void AddFields(List<EntityFieldInfo> fields, IEntityType entityType, List<IEntityType> visited = null, string prefix = null)
private void AddEntityFields(List<EntityFieldInfo> fields, IEntityType entityType, List<IEntityType> visited = null, string prefix = null)
{
visited ??= [];
if (visited.Contains(entityType))
Expand All @@ -123,7 +118,7 @@ private void AddFields(List<EntityFieldInfo> fields, IEntityType entityType, Lis
if (visited.Contains(nav.TargetEntityType))
continue;

AddFields(fields, nav.TargetEntityType, visited, prefix + nav.Name + ".");
AddEntityFields(fields, nav.TargetEntityType, visited, prefix + nav.Name + ".");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Threading.Tasks;
using Foundatio.Parsers.LuceneQueries;
using Foundatio.Parsers.LuceneQueries.Visitors;
using Foundatio.Parsers.SqlQueries.Visitors;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
Expand All @@ -23,10 +22,8 @@ public SqlQueryParserConfiguration() {
public string[] DefaultFields { get; private set; }

public QueryFieldResolver FieldResolver { get; private set; }
public EntityTypeDynamicFieldsResolver EntityTypeDynamicFieldResolver { get; private set; }
public EntityTypePropertyFilter EntityTypePropertyFilter { get; private set; } = static _ => true;
public IncludeResolver IncludeResolver { get; private set; }
//public ElasticMappingResolver MappingResolver { get; private set; }
public QueryValidationOptions ValidationOptions { get; private set; }
public ChainedQueryVisitor SortVisitor { get; } = new();
public ChainedQueryVisitor QueryVisitor { get; } = new();
Expand All @@ -44,11 +41,6 @@ public SqlQueryParserConfiguration SetDefaultFields(string[] fields) {
return this;
}

public SqlQueryParserConfiguration UseEntityTypeDynamicFieldResolver(EntityTypeDynamicFieldsResolver resolver) {
EntityTypeDynamicFieldResolver = resolver;
return this;
}

public SqlQueryParserConfiguration UseEntityTypePropertyFilter(EntityTypePropertyFilter filter) {
EntityTypePropertyFilter = filter;
return this;
Expand Down Expand Up @@ -234,5 +226,4 @@ public SqlQueryParserConfiguration AddAggregationVisitorAfter<T>(IChainableQuery
#endregion
}

public delegate Task<ICollection<EntityFieldInfo>> EntityTypeDynamicFieldsResolver(IEntityType entityType);
public delegate bool EntityTypePropertyFilter(IProperty property);
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Diagnostics;
using Foundatio.Parsers.LuceneQueries.Visitors;
using Microsoft.EntityFrameworkCore.Metadata;

namespace Foundatio.Parsers.SqlQueries.Visitors;

public class SqlQueryVisitorContext : QueryVisitorContext, ISqlQueryVisitorContext {
public List<EntityFieldInfo> Fields { get; set; }
public IEntityType EntityType { get; set; }
}

[DebuggerDisplay("{Field} IsNumber: {IsNumber} IsDate: {IsDate} IsBoolean: {IsBoolean} Children: {Children?.Count}")]
Expand Down
19 changes: 7 additions & 12 deletions tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,6 @@ public async Task CanGenerateSql()
}, ServiceLifetime.Scoped, ServiceLifetime.Singleton);
var parser = new SqlQueryParser();
parser.Configuration.AddQueryVisitor(new DynamicFieldVisitor());
parser.Configuration.UseEntityTypeDynamicFieldResolver(async entityType =>
{
var dynamicFields = new List<EntityFieldInfo>();
if (entityType.ClrType == typeof(Employee))
{
dynamicFields.Add(new EntityFieldInfo { Field = "age", IsNumber = true, Data = {{ "DataDefinitionId", 1 }}});
}
return dynamicFields;
});
parser.Configuration.UseEntityTypePropertyFilter(p =>
{
if (p.DeclaringType.ClrType == typeof(Company) && p.Name == "Description")
Expand Down Expand Up @@ -120,19 +111,23 @@ public async Task CanGenerateSql()
});
await db.SaveChangesAsync();

var context = parser.GetContext(db.Employees.EntityType);
context.Fields.Add(new EntityFieldInfo { Field = "age", IsNumber = true, Data = {{ "DataDefinitionId", 1 }}});
context.ValidationOptions.AllowedFields.Add("age");

string sqlExpected = db.Employees.Where(e => e.Company.Name == "acme" && e.DataValues.Any(dv => dv.DataDefinitionId == 1 && dv.NumberValue == 30)).ToQueryString();
string sqlActual = db.Employees.Where("""company.name = "acme" AND DataValues.Any(DataDefinitionId = 1 AND NumberValue = 30) """).ToQueryString();
Assert.Equal(sqlExpected, sqlActual);
string sql = await parser.ToSqlAsync("company.name:acme age:30", db.Employees.EntityType);
string sql = await parser.ToSqlAsync("company.name:acme age:30", context);
sqlActual = db.Employees.Where(sql).ToQueryString();
Assert.Equal(sqlExpected, sqlActual);

var q = db.Employees.AsNoTracking();
sql = await parser.ToSqlAsync("company.name:acme age:30", db.Employees.EntityType);
sql = await parser.ToSqlAsync("company.name:acme age:30", context);
sqlActual = q.Where(sql, db.Employees).ToQueryString();
Assert.Equal(sqlExpected, sqlActual);

await Assert.ThrowsAsync<ValidationException>(() => parser.ToSqlAsync("company.description:acme", db.Employees.EntityType));
await Assert.ThrowsAsync<ValidationException>(() => parser.ToSqlAsync("company.description:acme", context));

var employees = await db.Employees.Where(e => e.Title == "software developer" && e.DataValues.Any(dv => dv.DataDefinitionId == 1 && dv.NumberValue == 30))
.ToListAsync();
Expand Down

0 comments on commit f423023

Please sign in to comment.