From 1c94c63772eb9a5a56bc12faf6f2504182a5f260 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Mon, 18 Mar 2024 23:34:11 -0500 Subject: [PATCH] Add default field search support --- .../Extensions/SqlNodeExtensions.cs | 29 +++++++++++++++---- .../SqlQueryParserTests.cs | 27 +++++++++++++++-- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/Foundatio.Parsers.SqlQueries/Extensions/SqlNodeExtensions.cs b/src/Foundatio.Parsers.SqlQueries/Extensions/SqlNodeExtensions.cs index 095c14b8..69951bf6 100644 --- a/src/Foundatio.Parsers.SqlQueries/Extensions/SqlNodeExtensions.cs +++ b/src/Foundatio.Parsers.SqlQueries/Extensions/SqlNodeExtensions.cs @@ -115,20 +115,39 @@ public static EntityFieldInfo GetFieldInfo(List fields, string public static string ToSqlString(this TermNode node, ISqlQueryVisitorContext context) { - if (String.IsNullOrEmpty(node.Field)) - context.AddValidationError("Field is required for term node queries."); - if (!String.IsNullOrEmpty(node.Prefix)) context.AddValidationError("Prefix is not supported for term range queries."); + var builder = new StringBuilder(); + + if (String.IsNullOrEmpty(node.Field)) + { + if (context.DefaultFields == null || context.DefaultFields.Length == 0) + { + context.AddValidationError("Field or DefaultFields is required for term queries."); + return String.Empty; + } + + for (int index = 0; index < context.DefaultFields.Length; index++) + { + builder.Append(index == 0 ? "(" : " OR "); + + string defaultField = context.DefaultFields[index]; + builder.Append(defaultField).Append(".Contains(\"").Append(node.Term).Append("\")"); + + if (index == context.DefaultFields.Length - 1) + builder.Append(")"); + } + + return builder.ToString(); + } + // support overriding the generated query if (node.TryGetQuery(out string query)) return query; var field = GetFieldInfo(context.Fields, node.Field); - var builder = new StringBuilder(); - if (node.IsNegated.HasValue && node.IsNegated.Value) builder.Append("NOT "); diff --git a/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs b/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs index 875a0d2c..e2c8c958 100644 --- a/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs +++ b/tests/Foundatio.Parsers.SqlQueries.Tests/SqlQueryParserTests.cs @@ -60,6 +60,24 @@ public Task ValidQueries(string query, string expected) return ParseAndValidateQuery(query, expected, true); } + [Fact] + public async Task CanSearchDefaultFields() + { + var sp = GetServiceProvider(); + await using var db = await GetSampleContextWithDataAsync(sp); + var parser = sp.GetRequiredService(); + + var context = parser.GetContext(db.Employees.EntityType); + parser.Configuration.SetDefaultFields(["FullName", "Title"]); + + string sqlExpected = db.Employees.Where(e => e.FullName.Contains("John") || e.Title.Contains("John")).ToQueryString(); + string sqlActual = db.Employees.Where("""FullName.Contains("John") || Title.Contains("John")""").ToQueryString(); + Assert.Equal(sqlExpected, sqlActual); + string sql = await parser.ToSqlAsync("John", context); + sqlActual = db.Employees.Where(sql).ToQueryString(); + Assert.Equal(sqlExpected, sqlActual); + } + [Fact] public async Task CanUseDateFilter() { @@ -68,8 +86,6 @@ public async Task CanUseDateFilter() var parser = sp.GetRequiredService(); 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.Created > new DateTime(2024, 1, 1)).ToQueryString(); string sqlActual = db.Employees.Where("""created > DateTime.Parse("2024-01-01")""").ToQueryString(); @@ -188,7 +204,12 @@ 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, new SqlQueryVisitorContext()); + var context = new SqlQueryVisitorContext { Fields = + [ + new EntityFieldInfo { Field = "field", IsNumber = true } + ] + }; + string generatedQuery = await GenerateSqlVisitor.RunAsync(result, context); Assert.Equal(expected, generatedQuery); } }