Skip to content

Commit

Permalink
Progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsmith committed Mar 13, 2024
1 parent 39da8f6 commit 1de92ac
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 10 deletions.
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ services:
networks:
- foundatio

sqlserver:
image: mcr.microsoft.com/azure-sql-edge:1.0.7
ports:
- "1433:1433" # login with sa:P@ssword1
environment:
- "ACCEPT_EULA=Y"
- "SA_PASSWORD=P@ssword1"
- "MSSQL_PID=Developer"
healthcheck:
test:
[
"CMD",
"/opt/mssql-tools/bin/sqlcmd",
"-Usa",
"-PP@ssword1",
"-Q",
"select 1",
]
interval: 1s
retries: 20

ready:
image: andrewlock/wait-for-dependencies
command: elasticsearch:9200
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ public static IQueryable<T> LuceneWhere<T>(this IQueryable<T> source, string que
{
var fields = new List<FieldInfo>();
AddFields(fields, entityType);

// lookup and add custom fields
fields.Add(new FieldInfo
{
Field = "age",
Data = {{ "DataDefinitionId", 1 }},
IsNumber = true
});

return fields;
});

Expand Down
80 changes: 78 additions & 2 deletions src/Foundatio.Parsers.SqlQueries/Extensions/SqlNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public static class SqlNodeExtensions
{
public static string ToSqlString(this GroupNode node, ISqlQueryVisitorContext context)
{
// support overriding the generated query
if (node.TryGetQuery(out string query))
return query;

if (node.Left == null && node.Right == null)
return String.Empty;

Expand Down Expand Up @@ -60,6 +64,10 @@ public static string ToSqlString(this ExistsNode node, ISqlQueryVisitorContext c
if (String.IsNullOrEmpty(node.Field))
throw new ArgumentException("Field is required for exists node queries.");

// support overriding the generated query
if (node.TryGetQuery(out string query))
return query;

var builder = new StringBuilder();

if (node.IsNegated.HasValue && node.IsNegated.Value)
Expand All @@ -79,6 +87,10 @@ public static string ToSqlString(this MissingNode node, ISqlQueryVisitorContext
if (!String.IsNullOrEmpty(node.Prefix))
throw new ArgumentException("Prefix is not supported for term range queries.");

// support overriding the generated query
if (node.TryGetQuery(out string query))
return query;

var builder = new StringBuilder();

if (node.IsNegated.HasValue && node.IsNegated.Value)
Expand All @@ -98,6 +110,46 @@ public static string ToSqlString(this TermNode node, ISqlQueryVisitorContext con
if (!String.IsNullOrEmpty(node.Prefix))
throw new ArgumentException("Prefix is not supported for term range queries.");

// TODO: This needs to resolve the field recursively
var field = context.Fields.FirstOrDefault(f => f.Field.Equals(node.Field, StringComparison.OrdinalIgnoreCase));

// TODO: Remove this hard coded
if (field != null && field.Data.TryGetValue("DataDefinitionId", out object value) && value is int dataDefinitionId)
{
var customFieldBuilder = new StringBuilder();

customFieldBuilder.Append("DataValues.Any(DataDefinitionId = ");
customFieldBuilder.Append(dataDefinitionId);
customFieldBuilder.Append(" AND ");
if (field is { IsNumber: true })
customFieldBuilder.Append("NumberValue");
else if (field is { IsBoolean: true })
customFieldBuilder.Append("BooleanValue");
else if (field is { IsDate: true })
customFieldBuilder.Append("DateValue");
else
customFieldBuilder.Append("StringValue");

customFieldBuilder.Append(" = ");
if (field is { IsNumber: true } or { IsBoolean: true })
{
customFieldBuilder.Append(node.Term);
}
else
{
customFieldBuilder.Append("\"");
customFieldBuilder.Append(node.Term);
customFieldBuilder.Append("\"");
}
customFieldBuilder.Append(")");

node.SetQuery(customFieldBuilder.ToString());
}

// support overriding the generated query
if (node.TryGetQuery(out string query))
return query;

var builder = new StringBuilder();

if (node.IsNegated.HasValue && node.IsNegated.Value)
Expand All @@ -109,8 +161,6 @@ public static string ToSqlString(this TermNode node, ISqlQueryVisitorContext con
else
builder.Append(" = ");

// TODO: This needs to resolve the field recursively
var field = context.Fields.FirstOrDefault(f => f.Field.Equals(node.Field, StringComparison.OrdinalIgnoreCase));
if (field != null && (field.IsNumber || field.IsBoolean))
builder.Append(node.Term);
else
Expand All @@ -128,6 +178,10 @@ public static string ToSqlString(this TermRangeNode node, ISqlQueryVisitorContex
if (!String.IsNullOrEmpty(node.Proximity))
throw new ArgumentException("Proximity is not supported for term range queries.");

// support overriding the generated query
if (node.TryGetQuery(out string query))
return query;

var builder = new StringBuilder();

if (node.IsNegated.HasValue && node.IsNegated.Value)
Expand Down Expand Up @@ -171,4 +225,26 @@ public static string ToSqlString(this IQueryNode node, ISqlQueryVisitorContext c
_ => throw new NotSupportedException($"Node type {node.GetType().Name} is not supported.")
};
}

private const string QueryKey = "Query";
public static void SetQuery(this IQueryNode node, string query)
{
node.Data[QueryKey] = query;
}

public static string GetQuery(this IQueryNode node)
{
return node.Data.TryGetValue(QueryKey, out object query) ? query as string : null;
}

public static bool TryGetQuery(this IQueryNode node, out string query)
{
query = null;
return node.Data.TryGetValue(QueryKey, out object value) && (query = value as string) != null;
}

public static void RemoveQuery(this IQueryNode node)
{
node.Data.Remove(QueryKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public class FieldInfo
public bool IsNumber { get; set; }
public bool IsDate { get; set; }
public bool IsBoolean { get; set; }
public IDictionary<string, object> Data { get; set; } = new Dictionary<string, object>();
public List<FieldInfo> Children { get; set; }
}
3 changes: 1 addition & 2 deletions tests/Foundatio.Parsers.SqlQueries.Tests/SampleContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);

// Employee
modelBuilder.Entity<Employee>().HasIndex(e => new { e.FullName, e.Title, e.Age });
modelBuilder.Entity<Employee>().HasIndex(e => new { e.FullName, e.Title });

// Company
modelBuilder.Entity<Company>().HasIndex(e => new { e.Name, e.Description });
Expand All @@ -40,7 +40,6 @@ public class Employee {
public int Id { get; set; }
public string FullName { get; set; }
public string Title { get; set; }
public int Age { get; set; }
public int CompanyId { get; set; }
public Company Company { get; set; }
public List<DataValue> DataValues { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Threading.Tasks;
using Foundatio.Parsers.LuceneQueries.Nodes;
using Foundatio.Parsers.LuceneQueries.Visitors;
using Foundatio.Parsers.SqlQueries.Extensions;
using Foundatio.Parsers.SqlQueries.Visitors;
using Foundatio.Xunit;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -87,13 +86,10 @@ public async Task CanGenerateSql() {
});
await context.SaveChangesAsync();

var parser = new SqlQueryParser();
parser.Configuration.UseFieldMap(new Dictionary<string, string> {{ "age", "DataValues.Any(DataDefinitionId = 1 AND NumberValue" }});

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

var employees = await context.Employees.Where(e => e.Title == "software developer" && e.DataValues.Any(dv => dv.DataDefinitionId == 1 && dv.NumberValue == 30))
Expand Down Expand Up @@ -129,7 +125,7 @@ private async Task ParseAndValidateQuery(string query, string expected, bool isV

string nodes = await DebugQueryVisitor.RunAsync(result);
_logger.LogInformation(nodes);
string generatedQuery = await GenerateSqlVisitor.RunAsync(result);
string generatedQuery = await GenerateSqlVisitor.RunAsync(result, new SqlQueryVisitorContext());
Assert.Equal(expected, generatedQuery);
}
}

0 comments on commit 1de92ac

Please sign in to comment.