chore: initial git load of code space

This commit is contained in:
Thom Lamb
2026-05-12 08:52:33 -05:00
parent 9abada692f
commit 5e467bcc9c
384 changed files with 65960 additions and 2 deletions
@@ -0,0 +1,281 @@
using Strata.SqlTools.Breakdowns.LinqToSql;
using Strata.SqlTools.Comparers.LinqToSql;
namespace Strata.SqlTools.Analyzers.LinqToSql;
/// <summary>
/// Statistics about a collection of analyzed queries.
/// </summary>
public record QueryCollectionStatistics(
int TotalQueries,
int UniqueQueries,
List<LinqQueryBreakdown> DuplicateQueries,
Dictionary<string, int> TableUsageFrequency,
Dictionary<string, int> ColumnSelectionFrequency,
int QueriesWithoutWhere,
int QueriesWithoutOrderBy,
int QueriesWithSelectAll,
double AverageComplexity,
int ComplexQueriesCount
)
{
/// <summary>
/// Gets the deduplication rate (unique queries / total queries).
/// </summary>
public double DeduplicationRate => TotalQueries == 0 ? 0.0 : (double)UniqueQueries / TotalQueries;
/// <summary>
/// Gets a formatted statistics report.
/// </summary>
public string GetReport()
{
var report = new System.Text.StringBuilder();
report.AppendLine("Query Collection Analysis Report");
report.AppendLine("================================");
report.AppendLine($"Total Queries: {TotalQueries}");
report.AppendLine($"Unique Queries: {UniqueQueries} ({DeduplicationRate * 100:F1}%)");
report.AppendLine($"Duplicate Queries: {DuplicateQueries.Count}");
report.AppendLine();
report.AppendLine("Query Characteristics:");
report.AppendLine($" Queries without WHERE: {QueriesWithoutWhere}");
report.AppendLine($" Queries without ORDER BY: {QueriesWithoutOrderBy}");
report.AppendLine($" Queries with SELECT *: {QueriesWithSelectAll}");
report.AppendLine();
report.AppendLine("Complexity Analysis:");
report.AppendLine($" Average Complexity Level: {AverageComplexity:F2}");
report.AppendLine($" Complex Queries: {ComplexQueriesCount}");
report.AppendLine();
if (TableUsageFrequency.Count > 0)
{
report.AppendLine("Most Frequently Used Tables:");
foreach (var kvp in TableUsageFrequency.OrderByDescending(x => x.Value).Take(5))
{
report.AppendLine($" {kvp.Key}: {kvp.Value} times");
}
report.AppendLine();
}
if (ColumnSelectionFrequency.Count > 0)
{
report.AppendLine("Most Frequently Selected Columns:");
foreach (var kvp in ColumnSelectionFrequency.OrderByDescending(x => x.Value).Take(5))
{
report.AppendLine($" {kvp.Key}: {kvp.Value} times");
}
}
return report.ToString();
}
}
/// <summary>
/// Analyzes a collection of LinqQueryBreakdown queries for patterns, duplicates, and statistics.
/// </summary>
public class QueryCollectionAnalyzer
{
private readonly List<LinqQueryBreakdown> _queries;
/// <summary>
/// Initializes a new instance of the <see cref="QueryCollectionAnalyzer"/> class.
/// </summary>
/// <param name="queries">The queries to analyze.</param>
public QueryCollectionAnalyzer(IEnumerable<LinqQueryBreakdown> queries)
{
_queries = queries?.ToList() ?? throw new ArgumentNullException(nameof(queries));
}
/// <summary>
/// Analyzes the query collection and returns comprehensive statistics.
/// </summary>
/// <returns>Statistics about the query collection.</returns>
public QueryCollectionStatistics Analyze()
{
if (_queries.Count == 0)
{
return new QueryCollectionStatistics(
0, 0, new List<LinqQueryBreakdown>(),
new Dictionary<string, int>(),
new Dictionary<string, int>(),
0, 0, 0, 0.0, 0);
}
var duplicates = FindDuplicates();
var uniqueCount = _queries.Count - duplicates.Count;
var tableUsage = AnalyzeTableUsage();
var columnUsage = AnalyzeColumnUsage();
var queriesWithoutWhere = _queries.Count(q => string.IsNullOrWhiteSpace(q.WhereClause?.Clause));
var queriesWithoutOrderBy = _queries.Count(q => string.IsNullOrWhiteSpace(q.OrderByClause?.Clause));
var queriesWithSelectAll = _queries.Count(q =>
q.SelectClause?.Clause?.Trim() == "*");
var complexityScores = _queries.Select(q => GetComplexityScore(q)).ToList();
var avgComplexity = complexityScores.Average();
var complexQueries = complexityScores.Count(c => c >= 7);
return new QueryCollectionStatistics(
_queries.Count,
uniqueCount,
duplicates,
tableUsage,
columnUsage,
queriesWithoutWhere,
queriesWithoutOrderBy,
queriesWithSelectAll,
avgComplexity,
complexQueries);
}
/// <summary>
/// Finds duplicate queries in the collection.
/// </summary>
/// <returns>List of queries that are identical to another query in the collection.</returns>
public List<LinqQueryBreakdown> FindDuplicates()
{
var duplicates = new List<LinqQueryBreakdown>();
for (int i = 0; i < _queries.Count; i++)
{
for (int j = i + 1; j < _queries.Count; j++)
{
if (QueryComparator.AreQueriesIdentical(_queries[i], _queries[j]) && !duplicates.Contains(_queries[j]))
{
duplicates.Add(_queries[j]);
}
}
}
return duplicates;
}
/// <summary>
/// Finds similar queries that are not identical but have high similarity.
/// </summary>
/// <param name="minimumSimilarity">Minimum similarity score (0.0-1.0).</param>
/// <returns>Pairs of similar queries and their similarity scores.</returns>
public List<(LinqQueryBreakdown Query1, LinqQueryBreakdown Query2, double Similarity)> FindSimilarQueries(double minimumSimilarity = 0.75)
{
var similarPairs = new List<(LinqQueryBreakdown, LinqQueryBreakdown, double)>();
for (int i = 0; i < _queries.Count; i++)
{
for (int j = i + 1; j < _queries.Count; j++)
{
var similarity = QueryComparator.GetSimilarity(_queries[i], _queries[j]);
if (similarity >= minimumSimilarity && similarity < 1.0)
{
similarPairs.Add((_queries[i], _queries[j], similarity));
}
}
}
return similarPairs.OrderByDescending(x => x.Item3).ToList();
}
/// <summary>
/// Analyzes table usage frequency across all queries.
/// </summary>
/// <returns>Dictionary of table names and their usage counts.</returns>
private Dictionary<string, int> AnalyzeTableUsage()
{
var tableUsage = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
foreach (var query in _queries)
{
var table = query.FromClause?.Clause?.Trim();
if (!string.IsNullOrWhiteSpace(table))
{
if (tableUsage.ContainsKey(table))
{
tableUsage[table]++;
}
else
{
tableUsage[table] = 1;
}
}
}
return tableUsage;
}
/// <summary>
/// Analyzes column selection frequency across all queries.
/// </summary>
/// <returns>Dictionary of column names and their selection frequency.</returns>
private Dictionary<string, int> AnalyzeColumnUsage()
{
var columnUsage = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
foreach (var query in _queries)
{
var selectClause = query.SelectClause?.Clause;
if (string.IsNullOrWhiteSpace(selectClause) || selectClause.Trim() == "*")
{
continue;
}
// Split columns and count them
var columns = selectClause.Split(',');
foreach (var col in columns)
{
var columnName = col.Trim();
if (columnUsage.ContainsKey(columnName))
{
columnUsage[columnName]++;
}
else
{
columnUsage[columnName] = 1;
}
}
}
return columnUsage;
}
/// <summary>
/// Calculates a complexity score for a query (0-10).
/// </summary>
private static int GetComplexityScore(LinqQueryBreakdown query)
{
int score = 1; // Base score for having a query
if (!string.IsNullOrWhiteSpace(query.WhereClause?.Clause))
{ score += 2; }
if (!string.IsNullOrWhiteSpace(query.GroupByClause?.Clause))
{ score += 2; }
if (!string.IsNullOrWhiteSpace(query.HavingClause?.Clause))
{ score += 2; }
if (!string.IsNullOrWhiteSpace(query.OrderByClause?.Clause))
{ score += 1; }
// Bonus points for complex WHERE conditions
var whereClause = query.WhereClause?.Clause ?? string.Empty;
var complexityIndicators = new[] { " AND ", " OR ", "IN (", "BETWEEN", "LIKE" };
var complexParts = complexityIndicators.Count(ind => whereClause.Contains(ind, StringComparison.OrdinalIgnoreCase));
score += Math.Min(complexParts, 2); // Cap at +2
return Math.Min(score, 10); // Cap at 10
}
/// <summary>
/// Creates a new analyzer for the given queries.
/// </summary>
/// <param name="queries">The queries to analyze.</param>
/// <returns>A new QueryCollectionAnalyzer instance.</returns>
public static QueryCollectionAnalyzer Analyze(IEnumerable<LinqQueryBreakdown> queries)
{
return new QueryCollectionAnalyzer(queries);
}
/// <summary>
/// Gets a summary report for the query collection.
/// </summary>
/// <returns>A formatted analysis report.</returns>
public string GetReport()
{
return Analyze().GetReport();
}
}