Skip to content

Commit

Permalink
added GraphExpressionNode
Browse files Browse the repository at this point in the history
  • Loading branch information
ra0o0f committed Oct 10, 2016
1 parent 581631d commit ff5ef25
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 40 deletions.
1 change: 1 addition & 0 deletions src/ArangoDB.Client/ArangoDB.Client.Portable.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
<Compile Include="Query\Clause\FilterClause.cs" />
<Compile Include="Query\Clause\FilterExpressionNode.cs" />
<Compile Include="Query\Clause\GraphClause.cs" />
<Compile Include="Query\Clause\GraphExpressionNode.cs" />
<Compile Include="Query\Clause\GroupByClause.cs" />
<Compile Include="Query\Clause\GroupByExpressionNode.cs" />
<Compile Include="Query\Clause\IgnoreModificationSelectExpressionNode.cs" />
Expand Down
12 changes: 10 additions & 2 deletions src/ArangoDB.Client/Extentions/QueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,15 +391,23 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
source.Expression,
Expression.Quote(predicate)));
}

public static ITraversalQueryable<TraversalData<TVertex, TEdge>> Graph<TVertex, TEdge>(this IQueryable source, string graphName)
{
return Graph<TVertex, TEdge>(source, graphName, typeof(TVertex), typeof(TEdge));
}

[ExtentionIdentifier("Graph")]
public static ITraversalQueryable<TraversalData<TVertex, TEdge>> Graph<TVertex, TEdge>(this IQueryable source, string graphName)
internal static ITraversalQueryable<TraversalData<TVertex, TEdge>> Graph<TVertex, TEdge>(this IQueryable source, string graphName, Type vertexType, Type edgeType)
{
return source.Provider.CreateQuery<TraversalData<TVertex, TEdge>>(
Expression.Call(
FindExtention("Graph", typeof(TVertex), typeof(TEdge)),
source.Expression,
Expression.Constant(graphName))) as ITraversalQueryable<TraversalData<TVertex, TEdge>>;
Expression.Constant(graphName),
Expression.Constant(vertexType),
Expression.Constant(edgeType)
)) as ITraversalQueryable<TraversalData<TVertex, TEdge>>;
}

[ExtentionIdentifier("Edges")]
Expand Down
1 change: 1 addition & 0 deletions src/ArangoDB.Client/Query/AQLParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private IQueryParser CreateQueryParser()
customNodeTypeRegistry.Register(SelectModificationExpressionNode.SupportedMethods, typeof(SelectModificationExpressionNode));
customNodeTypeRegistry.Register(InModificationExpressionNode.SupportedMethods, typeof(InModificationExpressionNode));
customNodeTypeRegistry.Register(IgnoreModificationSelectExpressionNode.SupportedMethods, typeof(IgnoreModificationSelectExpressionNode));
customNodeTypeRegistry.Register(GraphExpressionNode.SupportedMethods, typeof(GraphExpressionNode));

var nodeTypeProvider = ExpressionTreeParser.CreateDefaultNodeTypeProvider();
nodeTypeProvider.InnerProviders.Insert(0, customNodeTypeRegistry);
Expand Down
30 changes: 13 additions & 17 deletions src/ArangoDB.Client/Query/ArangoExpressionTreeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected override Expression VisitMethodCall(MethodCallExpression expression)
methodExists = aqlMethods.TryGetValue(expression.Method.Name, out methodName);

if (!methodExists)
throw new InvalidOperationException($"Method {expression.Method.Name} is not supported in AqlLinqProvider");
throw new InvalidOperationException($"Method {expression.Method.Name} is not supported in ArangoLinqProvider");

string argumentSeprator = null;
bool noParenthesis = methodsWithNoParenthesis.TryGetValue(methodName, out argumentSeprator);
Expand Down Expand Up @@ -82,15 +82,7 @@ protected override Expression VisitMethodCall(MethodCallExpression expression)

return expression;
}

// Called when a LINQ expression type is not handled above.
//protected override Exception CreateUnhandledItemException<T>(T unhandledItem, string visitMethod)
//{
// string itemText = FormatUnhandledItem(unhandledItem);
// var message = string.Format("The expression '{0}' (type: {1}) is not supported by ArangoDB LINQ provider.", itemText, typeof(T));
// return new NotSupportedException(message);
//}


protected override Expression VisitParameter(ParameterExpression expression)
{
ModelVisitor.QueryText.AppendFormat(" {0} ", LinqUtility.ResolvePropertyName(expression.Name));
Expand All @@ -103,6 +95,7 @@ protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpr
ModelVisitor.QueryText.AppendFormat(" {0}", LinqUtility.ResolvePropertyName(expression.ReferencedQuerySource.ItemName));

var mainFromClause = expression.ReferencedQuerySource as MainFromClause;
// .Select(g => g.Select(gList => gList.Age)) subquery select, handle prior groupby source parameter names
if (mainFromClause != null && mainFromClause.FromExpression.Type.Name == "IGrouping`2")
{
var groupByClauses = LinqUtility.PriorGroupBy(ModelVisitor);
Expand Down Expand Up @@ -171,6 +164,7 @@ protected override Expression VisitMember(MemberExpression expression)
{
ModelVisitor.QueryText.AppendFormat(" {0} ", LinqUtility.ResolvePropertyName(expression.Member.Name));
}
// Select(g=>g.Key)
else if (expression.Expression.Type.Name == "IGrouping`2")
{
var groupByClause = LinqUtility.PriorGroupBy(ModelVisitor)[0];
Expand All @@ -195,6 +189,14 @@ protected override Expression VisitMember(MemberExpression expression)
if (groupByClause.Selector.NodeType != ExpressionType.New)
ModelVisitor.QueryText.AppendFormat(" {0} ", LinqUtility.ResolvePropertyName(groupByClause.CollectVariableName));
}
else if (expression.Expression.Type.Name == "TraversalData`2")
{
var parameterExpression = expression.Expression as ParameterExpression;
if (parameterExpression == null)
throw new InvalidOperationException("TraversalData`2 VisitMember, expected a ParameterExpression");

ModelVisitor.QueryText.AppendFormat(LinqUtility.ResolvePropertyName($"{parameterExpression.Name}{expression.Member.Name}"));
}
else
{
Visit(expression.Expression);
Expand Down Expand Up @@ -349,6 +351,7 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)

protected override Expression VisitNew(NewExpression expression)
{
// Select(g=>g)
if (expression.Type.Name == "Grouping`2")
{
var groupByClause = LinqUtility.PriorGroupBy(ModelVisitor)[0];
Expand Down Expand Up @@ -398,12 +401,5 @@ public Expression VisitNamed(NamedExpression expression)
ModelVisitor.QueryText.AppendFormat(" `{0}` {1} ", expression.Name, TreatNewWithoutBracket ? "= " : ": ");
return Visit(expression.Expression);
}

//private string FormatUnhandledItem<T>(T unhandledItem)
//{
// var itemAsExpression = unhandledItem as Expression;
// return itemAsExpression != null ? FormattingExpressionTreeVisitor.Format(itemAsExpression) : unhandledItem.ToString();
//}

}
}
21 changes: 8 additions & 13 deletions src/ArangoDB.Client/Query/ArangoModelVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,7 @@ public override void VisitQueryModel(QueryModel queryModel)
queryModel.MainFromClause.Accept(this, queryModel);

VisitBodyClauses(queryModel.BodyClauses, queryModel);

//if (CrudState.ModelVisitorHaveCrudOperation)
//{
// QueryText.AppendFormat(" in {0} ", CrudState.Collection);
// if (CrudState.ReturnResult)
// QueryText.AppendFormat(" let crudResult = {0} return crudResult ", CrudState.ReturnResultKind);
//}
//else if (!DontReturn)
// queryModel.SelectClause.Accept(this, queryModel);

//DontReturn = queryModel.BodyClauses.NextBodyClause<IModificationClause>(0) == null
// && queryModel


var modificationClause = queryModel.BodyClauses.NextBodyClause<IModificationClause>();

if (modificationClause != null && modificationClause.IgnoreSelect)
Expand Down Expand Up @@ -147,6 +135,7 @@ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel q

string fromName = LinqUtility.ResolveCollectionName(this.Db, fromClause.ItemType);

// .Select(g => g.Select(gList => gList.Age)) subquery select, changes the fromName to group name (C1 for example)
if (fromClause.FromExpression.Type.Name == "IGrouping`2")
{
var groupByClause = LinqUtility.PriorGroupBy(ParnetModelVisitor);
Expand All @@ -159,6 +148,7 @@ public override void VisitMainFromClause(MainFromClause fromClause, QueryModel q
fromName = LinqUtility.ResolvePropertyName(fromName);
}

// == "IGrouping`2" => .Select(g => g.Select(gList => gList.Age)) subquery select
if (fromClause.FromExpression.NodeType == ExpressionType.Constant
|| fromClause.FromExpression.Type.Name == "IGrouping`2")
{
Expand Down Expand Up @@ -286,7 +276,12 @@ public void VisitSkipTakeClause(SkipTakeClause skipTakeClause, QueryModel queryM

public void VisitTraversalClause(ITraversalClause traversalClause, QueryModel queryModel, int index)
{
QueryText.AppendFormat(" for {0}, {1}, {2} in ",
LinqUtility.ResolvePropertyName($"{traversalClause.AssociatedIdentifier}Vertex"),
LinqUtility.ResolvePropertyName($"{traversalClause.AssociatedIdentifier}Edge"),
LinqUtility.ResolvePropertyName($"{traversalClause.AssociatedIdentifier}Path"));

QueryText.AppendFormat(" graph {0} " , LinqUtility.ResolvePropertyName(traversalClause.GraphName.Value.ToString()));
}

public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
Expand Down
2 changes: 1 addition & 1 deletion src/ArangoDB.Client/Query/Clause/FilterExpressionNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace ArangoDB.Client.Query.Clause
{
public sealed class FilterExpressionNode : MethodCallExpressionNodeBase
public class FilterExpressionNode : MethodCallExpressionNodeBase
{
private const int c_indexSelectorParameterPosition = 1;

Expand Down
15 changes: 10 additions & 5 deletions src/ArangoDB.Client/Query/Clause/GraphClause.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ namespace ArangoDB.Client.Query.Clause
{
public class GraphClause : IBodyClause, ITraversalClause
{
public Expression GraphName { get; set; }
public ConstantExpression GraphName { get; set; }

public GraphClause(Expression graphName)
public string AssociatedIdentifier { get; set; }

public GraphClause(ConstantExpression graphName, string associatedIdentifier)
{
LinqUtility.CheckNotNull("predicate", graphName);
LinqUtility.CheckNotNull("graphName", graphName);
LinqUtility.CheckNotNull("associatedIdentifier", associatedIdentifier);

GraphName = graphName;
AssociatedIdentifier = associatedIdentifier;
}

public virtual void Accept(IQueryModelVisitor visitor, QueryModel queryModel, int index)
Expand All @@ -36,7 +41,7 @@ public virtual GraphClause Clone(CloneContext cloneContext)
{
LinqUtility.CheckNotNull("cloneContext", cloneContext);

var clone = new GraphClause(GraphName);
var clone = new GraphClause(GraphName, AssociatedIdentifier);
return clone;
}

Expand All @@ -48,7 +53,7 @@ IBodyClause IBodyClause.Clone(CloneContext cloneContext)
public void TransformExpressions(Func<Expression, Expression> transformation)
{
LinqUtility.CheckNotNull("transformation", transformation);
GraphName = transformation(GraphName);
GraphName = transformation(GraphName) as ConstantExpression;
}
}
}
86 changes: 86 additions & 0 deletions src/ArangoDB.Client/Query/Clause/GraphExpressionNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using ArangoDB.Client.Data;
using ArangoDB.Client.Utility;
using Remotion.Linq;
using Remotion.Linq.Parsing.ExpressionVisitors;
using Remotion.Linq.Parsing.Structure.IntermediateModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ArangoDB.Client.Query.Clause
{
public class GraphExpressionNode : MethodCallExpressionNodeBase, IQuerySourceExpressionNode
{
public static readonly MethodInfo[] SupportedMethods = new[]
{
LinqUtility.GetSupportedMethod(()=>QueryableExtensions.Graph<object,object>(null, null, null, null))
};

public ConstantExpression GraphName { get; private set; }
public ConstantExpression VertextType { get; private set; }
public ConstantExpression EdgeType { get; private set; }

string associatedIdentifier;

public GraphExpressionNode(MethodCallExpressionParseInfo parseInfo,
ConstantExpression graphName,
ConstantExpression vertexType,
ConstantExpression edgeType)
: base(parseInfo)
{
GraphName = graphName;
VertextType = vertexType;
EdgeType = edgeType;

associatedIdentifier = parseInfo.AssociatedIdentifier;

_resolvedAdaptedSelector = new ResolvedExpressionCache<Expression>(this);
}

public override Expression Resolve(ParameterExpression inputParameter, Expression expressionToBeResolved, ClauseGenerationContext clauseGenerationContext)
{
LinqUtility.CheckNotNull("inputParameter", inputParameter);
LinqUtility.CheckNotNull("expressionToBeResolved", expressionToBeResolved);

var resolvedSelector = GetResolvedAdaptedSelector(clauseGenerationContext);
var resolved = ReplacingExpressionVisitor.Replace(inputParameter, Expression.Parameter(resolvedSelector.Type, associatedIdentifier), expressionToBeResolved);
return resolved;
}

protected override void ApplyNodeSpecificSemantics(QueryModel queryModel, ClauseGenerationContext clauseGenerationContext)
{
LinqUtility.CheckNotNull("queryModel", queryModel);

var graphClause = new GraphClause(GraphName, associatedIdentifier);

queryModel.BodyClauses.Add(graphClause);

clauseGenerationContext.AddContextInfo(this, graphClause);

queryModel.SelectClause.Selector = GetResolvedAdaptedSelector(clauseGenerationContext);
}

private readonly ResolvedExpressionCache<Expression> _resolvedAdaptedSelector;

public Expression GetResolvedAdaptedSelector(ClauseGenerationContext clauseGenerationContext)
{
return _resolvedAdaptedSelector.GetOrCreate(
r =>
{
var traversalDataType = typeof(TraversalData<,>).MakeGenericType(new Type[] { VertextType.Value as Type, EdgeType.Value as Type });

var constr = ReflectionUtils.GetConstructors(traversalDataType).ToList()[0];
var newExpression =
Expression.Convert(
Expression.New(constr), traversalDataType)
;

return r.GetResolvedExpression(newExpression, Expression.Parameter(traversalDataType,"dummy"), clauseGenerationContext);
});
}
}
}
1 change: 0 additions & 1 deletion src/ArangoDB.Client/Query/Clause/GroupByExpressionNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ protected override void ApplyNodeSpecificSemantics(QueryModel queryModel, Clause
clauseGenerationContext.AddContextInfo(this, groupByClause);

queryModel.SelectClause.Selector = GetResolvedAdaptedSelector(clauseGenerationContext);

}

public Expression GetResolvedAdaptedSelector(ClauseGenerationContext clauseGenerationContext)
Expand Down
4 changes: 3 additions & 1 deletion src/ArangoDB.Client/Query/Clause/ITraversalClause.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ namespace ArangoDB.Client.Query.Clause
{
public interface ITraversalClause
{
Expression GraphName { get; set; }
ConstantExpression GraphName { get; set; }

string AssociatedIdentifier { get; set; }
}
}

0 comments on commit ff5ef25

Please sign in to comment.