Skip to content

Commit

Permalink
Implementation for usable order by clause.
Browse files Browse the repository at this point in the history
  • Loading branch information
Puchaczov committed Jun 7, 2024
1 parent 1c6f2bd commit 31b93de
Show file tree
Hide file tree
Showing 30 changed files with 1,036 additions and 734 deletions.
669 changes: 668 additions & 1 deletion Musoq.Evaluator.Tests/OrderByTests.cs

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions Musoq.Evaluator/BaseOperations.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using Musoq.Evaluator.Tables;
using Musoq.Schema.DataSources;

namespace Musoq.Evaluator
{
Expand Down Expand Up @@ -51,5 +52,55 @@ public Table Intersect(Table first, Table second, Func<Row, Row, bool> comparer)

return result;
}

public IOrderedEnumerable<Row> OrderBy<T>(Table table, Func<Row, T> selector)
{
return table.OrderBy(selector);
}

public IOrderedEnumerable<IObjectResolver> OrderBy<T>(RowSource rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.Rows.OrderBy(selector);
}

public IOrderedEnumerable<IObjectResolver> OrderBy<T>(IOrderedEnumerable<IObjectResolver> rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.OrderBy(selector);
}

public IOrderedEnumerable<Row> OrderByDescending<T>(Table table, Func<Row, T> selector)
{
return table.OrderByDescending(selector);
}

public IOrderedEnumerable<IObjectResolver> OrderByDescending<T>(RowSource rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.Rows.OrderByDescending(selector);
}

public IOrderedEnumerable<IObjectResolver> OrderByDescending<T>(IOrderedEnumerable<IObjectResolver> rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.OrderByDescending(selector);
}

public IOrderedEnumerable<Row> ThenBy<T>(IOrderedEnumerable<Row> table, Func<Row, T> selector)
{
return table.ThenBy(selector);
}

public IOrderedEnumerable<IObjectResolver> ThenBy<T>(IOrderedEnumerable<IObjectResolver> rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.ThenBy(selector);
}

public IOrderedEnumerable<Row> ThenByDescending<T>(IOrderedEnumerable<Row> table, Func<Row, T> selector)
{
return table.ThenByDescending(selector);
}

public IOrderedEnumerable<IObjectResolver> ThenByDescending<T>(IOrderedEnumerable<IObjectResolver> rowSource, Func<IObjectResolver, T> selector)
{
return rowSource.ThenByDescending(selector);
}
}
}
104 changes: 101 additions & 3 deletions Musoq.Evaluator/Helpers/SyntaxHelper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Musoq.Parser.Nodes;

namespace Musoq.Evaluator.Helpers
{
Expand Down Expand Up @@ -168,15 +170,111 @@ private static ObjectCreationExpressionSyntax CreateObjectOf(TypeSyntax type,
initializer);
}

public static ForEachStatementSyntax Foreach(string variable, string source, BlockSyntax block)
public static ForEachStatementSyntax Foreach(string variable, string source, BlockSyntax block, (FieldOrderedNode Field, ExpressionSyntax Syntax)[] orderByFields)
{
ExpressionSyntax orderByExpression = SyntaxFactory.IdentifierName(source);

if (orderByFields.Length == 0)
{
return SyntaxFactory.ForEachStatement(
SyntaxFactory.Token(SyntaxKind.ForEachKeyword),
SyntaxFactory.Token(SyntaxKind.OpenParenToken),
SyntaxFactory.IdentifierName("var").WithTrailingTrivia(WhiteSpace),
SyntaxFactory.Identifier(variable).WithTrailingTrivia(WhiteSpace),
SyntaxFactory.Token(SyntaxKind.InKeyword).WithTrailingTrivia(WhiteSpace),
orderByExpression,
SyntaxFactory.Token(SyntaxKind.CloseParenToken),
block);
}

var sourceTable = source.Replace(".Rows", string.Empty);
if (orderByFields[0].Field.Order == Order.Ascending)
{
orderByExpression = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName("OrderBy")
).WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]{
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName(sourceTable)),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier(variable)))
.WithExpressionBody(
orderByFields[0].Syntax))}))
);
}
else
{
orderByExpression = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName("OrderByDescending")
).WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]{
SyntaxFactory.Argument(
SyntaxFactory.IdentifierName(sourceTable)),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier(variable)))
.WithExpressionBody(
orderByFields[0].Syntax))}))
);
}

for (var index = 1; index < orderByFields.Length; index++)
{
var fieldSyntaxTuple = orderByFields[index];
if (fieldSyntaxTuple.Field.Order == Order.Ascending)
{
orderByExpression = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName("ThenBy")
).WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]{
SyntaxFactory.Argument(orderByExpression),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier(variable)))
.WithExpressionBody(
orderByFields[index].Syntax))}))
);
}
else
{
orderByExpression = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName("ThenByDescending")
).WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]{
SyntaxFactory.Argument(orderByExpression),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.Argument(
SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier(variable)))
.WithExpressionBody(
orderByFields[index].Syntax))}))
);
}
}

return SyntaxFactory.ForEachStatement(
SyntaxFactory.Token(SyntaxKind.ForEachKeyword),
SyntaxFactory.Token(SyntaxKind.OpenParenToken),
SyntaxFactory.IdentifierName("var").WithTrailingTrivia(WhiteSpace),
SyntaxFactory.Identifier(variable).WithTrailingTrivia(WhiteSpace),
SyntaxFactory.Token(SyntaxKind.InKeyword).WithTrailingTrivia(WhiteSpace),
SyntaxFactory.IdentifierName(source),
orderByExpression,
SyntaxFactory.Token(SyntaxKind.CloseParenToken),
block);
}
Expand All @@ -188,7 +286,7 @@ public static ArrayCreationExpressionSyntax CreateArrayOf(string typeName, Expre
SyntaxTriviaList.Create(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")));
var syntaxList = new SeparatedSyntaxList<ExpressionSyntax>();

for (var i = 0; i < expressions.Length; i++) syntaxList = syntaxList.Add(expressions[i]);
syntaxList = expressions.Aggregate(syntaxList, (current, expression) => current.Add(expression));

var rankSpecifiers = new SyntaxList<ArrayRankSpecifierSyntax>();

Expand Down
8 changes: 7 additions & 1 deletion Musoq.Evaluator/Musoq.Evaluator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<Platforms>AnyCPU;x64</Platforms>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>5.6.9</Version>
<Version>5.7.0</Version>
<Authors>Jakub Puchała</Authors>
<Product>Musoq</Product>
<PackageProjectUrl>https://github.com/Puchaczov/Musoq</PackageProjectUrl>
Expand Down Expand Up @@ -44,6 +44,12 @@
<AutoGen>True</AutoGen>
<DependentUpon>MetaAttributes.resx</DependentUpon>
</Compile>
<Compile Update="Visitors\RewriteFieldOrderedWithGroupMethodCall.cs">
<DependentUpon>RewriteFieldWithGroupMethodCallBase.cs</DependentUpon>
</Compile>
<Compile Update="Visitors\RewriteFieldWithGroupMethodCall.cs">
<DependentUpon>RewriteFieldWithGroupMethodCallBase.cs</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Musoq.Evaluator/Visitors/CloneQueryVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public virtual void Visit(FieldNode node)
Nodes.Push(new FieldNode(Nodes.Pop(), node.FieldOrder, node.FieldName));
}

public void Visit(FieldOrderedNode node)
public virtual void Visit(FieldOrderedNode node)
{
Nodes.Push(new FieldOrderedNode(Nodes.Pop(), node.FieldOrder, node.FieldName, node.Order));
}
Expand Down
17 changes: 17 additions & 0 deletions Musoq.Evaluator/Visitors/RewriteFieldOrderedWithGroupMethodCall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Musoq.Parser.Nodes;

namespace Musoq.Evaluator.Visitors;

public class RewriteFieldOrderedWithGroupMethodCall : RewriteFieldWithGroupMethodCallBase<FieldOrderedNode>
{
public RewriteFieldOrderedWithGroupMethodCall(FieldNode[] nodes)
: base(nodes)
{
}

public override void Visit(FieldOrderedNode node)
{
base.Visit(node);
Expression = Nodes.Pop() as FieldOrderedNode;
}
}
79 changes: 5 additions & 74 deletions Musoq.Evaluator/Visitors/RewriteFieldWithGroupMethodCall.cs
Original file line number Diff line number Diff line change
@@ -1,87 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using Musoq.Evaluator.Helpers;
using Musoq.Parser;
using Musoq.Parser.Nodes;
using Musoq.Parser.Tokens;
using Musoq.Plugins;
using Musoq.Parser.Nodes;

namespace Musoq.Evaluator.Visitors
{
public class RewriteFieldWithGroupMethodCall : CloneQueryVisitor
public class RewriteFieldWithGroupMethodCall : RewriteFieldWithGroupMethodCallBase<FieldNode>
{
private readonly FieldNode[] _fields;

public RewriteFieldWithGroupMethodCall(FieldNode[] fields)
public RewriteFieldWithGroupMethodCall(FieldNode[] nodes)
: base(nodes)
{
_fields = fields;
}

public FieldNode Expression { get; private set; }


public override void Visit(FieldNode node)
{
base.Visit(node);
Expression = Nodes.Pop() as FieldNode;
}

public override void Visit(AccessColumnNode node)
{
Nodes.Push(new AccessColumnNode(NamingHelper.ToColumnName(node.Alias, node.Name), string.Empty,
node.ReturnType, TextSpan.Empty));
}

public override void Visit(DotNode node)
{
if (!(node.Root is DotNode) && node.Root is AccessColumnNode column)
{
Nodes.Pop();
Nodes.Pop();

var name = $"{NamingHelper.ToColumnName(column.Alias, column.Name)}.{node.Expression.ToString()}";
Nodes.Push(new AccessColumnNode(name, string.Empty, node.ReturnType, TextSpan.Empty));
return;
}

base.Visit(node);
}

public override void Visit(AccessMethodNode node)
{
if (node.IsAggregateMethod())
{
Nodes.Pop();

var wordNode = node.Arguments.Args[0] as WordNode;
var accessGroup = new AccessColumnNode("none", string.Empty, typeof(Group), TextSpan.Empty);
var args = new List<Node> {accessGroup, wordNode};
args.AddRange(node.Arguments.Args.Skip(1));
var extractFromGroup = new AccessMethodNode(
new FunctionToken(node.Method.Name, TextSpan.Empty),
new ArgsListNode(args.ToArray()), node.ExtraAggregateArguments, node.CanSkipInjectSource, node.Method, node.Alias);
Nodes.Push(extractFromGroup);
}
else if (_fields.Select(f => f.Expression.ToString()).Contains(node.ToString()))
{
Nodes.Push(new AccessColumnNode(node.ToString(), string.Empty, node.ReturnType, TextSpan.Empty));
}
else
{
base.Visit(node);
}
}

public override void Visit(AccessCallChainNode node)
{
Nodes.Push(new AccessColumnNode(node.ToString(), string.Empty, node.ReturnType, TextSpan.Empty));
}

public override void Visit(CaseNode node)
{
if (_fields.Select(f => f.Expression.ToString()).Contains(node.ToString()))
Nodes.Push(new AccessColumnNode(node.ToString(), string.Empty, node.ReturnType, TextSpan.Empty));
else
base.Visit(node);
}
}
}
Loading

0 comments on commit 31b93de

Please sign in to comment.