src/LogicalPlanner/LogicalPlan.cs
/// <summary>
/// Create logical operator tree for projection expressions with optional orderby/limit/filtering
/// </summary>
/// <param name="pipedData"></param>
/// <param name="isDistinct"></param>
/// <param name="projExprs"></param>
/// <param name="limitClause"></param>
/// <param name="orderByClause"></param>
/// <param name="filterExpr"></param>
/// <param name="allLogicalOps"></param>
/// <returns></returns>
Create logical operator tree for projection expressions with optional orderby/limit/filtering
Create logical operator tree for projection expressions with optional orderby / limit / filtering
private LogicalOperator CreateLogicalTree(LogicalOperator pipedData, bool isDistinct, 
            IList<QueryExpressionWithAlias> projExprs, 
            IList<QueryExpressionWithAlias> implicitProjExprs,
            LimitClause limitClause, IList<SortItem> orderByClause, QueryExpression filterExpr, 
            ISet<LogicalOperator> allLogicalOps)
        {
            LogicalOperator lastOperator = pipedData;
            var hasAggregationExpression = projExprs
                .Any(n => n.GetChildrenQueryExpressionType<QueryExpressionAggregationFunction>().Count() > 0);
            var hasOrderByLimit = (limitClause != null || (orderByClause?.Count() ?? 0) > 0);
            var hasWhere = (filterExpr != null);
            var removeExtraImplicitFields = false;
              Example: 
              MATCH (a)-[]->(b)
              WITH DISTINCT a.Name as Name, b.Title as Title
              ORDER BY Name, Title
              WHERE Name <> "Tom"
              RETURN Name, Title
              ** ORDER BY/WHERE can only refer Name, Title as there is a DISTINCT
              if no distinct 
              MATCH (a)-[]->(b)
              WITH a.Name as Name, b.Title as Title
              ORDER BY a.Tagline, n.Price
              WHERE Name <> "Tom"
              RETURN Name, Title
              ** ORDER BY/WHERE can refer any field in a and b even though only Name and Title
                 were explicitly projected
            var extraImplicitProjections = implicitProjExprs?.Where(n => projExprs.All(p => p.Alias != n.Alias));
            if ((hasOrderByLimit || hasWhere) &&
                !(isDistinct || hasAggregationExpression) &&
                (extraImplicitProjections?.Count() ?? 0) > 0)
            {
                 If we need to expose the implicit projected entities from previous query parts,
                 and the circumstance allows it (i.e. when DISTINCT/aggregation function was not used)
                 we add extra implicit fields, and will remove it after ORDER BY/WHERE is processed
                var extendedProjectionExprs = projExprs.Union(extraImplicitProjections).ToList();
                lastOperator = CreateLogicalTree(extendedProjectionExprs, isDistinct , lastOperator, allLogicalOps);
                removeExtraImplicitFields = true;
            }
            else
            {
                lastOperator = CreateLogicalTree(projExprs, isDistinct, lastOperator, allLogicalOps);
            }
             Optional OrderBy / Limit
            if (hasOrderByLimit)
            {
                lastOperator = CreateLogicalTree(orderByClause, limitClause, lastOperator, allLogicalOps);
            }
             Optional WHERE
            if (filterExpr != null)
            {
                 without match, we can just apply filtering if needed (e.g. WITH ... WHERE ...)
                lastOperator = CreateLogicalTree(filterExpr, lastOperator, allLogicalOps);
            }
             do a simple projection to remove extra implicitly projected fields if applies
            if (removeExtraImplicitFields)
            {
                 by now, all fields need to be projected are already computed in the previous projection operator
                 so we just do a project that retains all the fields that are explicitly projected (by doing an 'field AS field')
                var trimmedSimpleProjExprs = projExprs.Select(n =>
                    new QueryExpressionWithAlias
                    {
                        Alias = n.Alias,
                        InnerExpression = new QueryExpressionProperty()
                        {
                            VariableName = n.Alias,
                            Entity = n.InnerExpression.TryGetDirectlyExposedEntity(),
                        }
                    }
                ).ToList();
                lastOperator = CreateLogicalTree(trimmedSimpleProjExprs, false , lastOperator, allLogicalOps);
            }
            return lastOperator;
        }
private LogicalOperator CreateLogicalTree ( LogicalOperator pipedData , bool isDistinct , IList < QueryExpressionWithAlias > projExprs , IList < QueryExpressionWithAlias > implicitProjExprs , LimitClause limitClause , IList < SortItem > orderByClause , QueryExpression filterExpr , ISet < LogicalOperator > allLogicalOps ) { LogicalOperator lastOperator = pipedData ; var hasAggregationExpression = projExprs . Any ( n => n . GetChildrenQueryExpressionType < QueryExpressionAggregationFunction > ( ) . Count ( ) > 0 ) ; var hasOrderByLimit = ( limitClause != null || ( orderByClause ? . Count ( ) ?? 0 ) > 0 ) ; var hasWhere = ( filterExpr != null ) ; var removeExtraImplicitFields = false ; var extraImplicitProjections = implicitProjExprs ? . Where ( n => projExprs . All ( p => p . Alias != n . Alias ) ) ; if ( ( hasOrderByLimit || hasWhere ) && ! ( isDistinct || hasAggregationExpression ) && ( extraImplicitProjections ? . Count ( ) ?? 0 ) > 0 ) { var extendedProjectionExprs = projExprs . Union ( extraImplicitProjections ) . ToList ( ) ; lastOperator = CreateLogicalTree ( extendedProjectionExprs , isDistinct , lastOperator , allLogicalOps ) ; removeExtraImplicitFields = true ; } else { lastOperator = CreateLogicalTree ( projExprs , isDistinct , lastOperator , allLogicalOps ) ; } if ( hasOrderByLimit ) { lastOperator = CreateLogicalTree ( orderByClause , limitClause , lastOperator , allLogicalOps ) ; } if ( filterExpr != null ) { lastOperator = CreateLogicalTree ( filterExpr , lastOperator , allLogicalOps ) ; } if ( removeExtraImplicitFields ) { var trimmedSimpleProjExprs = projExprs . Select ( n => new QueryExpressionWithAlias { Alias = n . Alias , InnerExpression = new QueryExpressionProperty ( ) { VariableName = n . Alias , Entity = n . InnerExpression . TryGetDirectlyExposedEntity ( ) , } } ) . ToList ( ) ; lastOperator = CreateLogicalTree ( trimmedSimpleProjExprs , false , lastOperator , allLogicalOps ) ; } return lastOperator ; }
Create logical operator tree for projection expressions with optional orderby/limit/filtering
Create logical operator tree for projection expressions with optional orderby / limit / filtering
//  If there is "DISTINCT" keyword, it is equivalent to have an extra "WITH" clause where only explicitly projected //  variables and entities are kept valid for downstream ORDER BY, WHERE clauses. // //  Example:  //  MATCH (a)-[]->(b) //  WITH DISTINCT a.Name as Name, b.Title as Title //  ORDER BY Name, Title //  WHERE Name <> "Tom" //  RETURN Name, Title //  ** ORDER BY/WHERE can only refer Name, Title as there is a DISTINCT // //  if no distinct  //  MATCH (a)-[]->(b) //  WITH a.Name as Name, b.Title as Title //  ORDER BY a.Tagline, n.Price //  WHERE Name <> "Tom" //  RETURN Name, Title //  ** ORDER BY/WHERE can refer any field in a and b even though only Name and Title //     were explicitly projected // // If we need to expose the implicit projected entities from previous query parts, // and the circumstance allows it (i.e. when DISTINCT/aggregation function was not used) // we add extra implicit fields, and will remove it after ORDER BY/WHERE is processed /*false*/ // Optional OrderBy / Limit // Optional WHERE // without match, we can just apply filtering if needed (e.g. WITH ... WHERE ...) // do a simple projection to remove extra implicitly projected fields if applies // by now, all fields need to be projected are already computed in the previous projection operator // so we just do a project that retains all the fields that are explicitly projected (by doing an 'field AS field') /*distinct should've applied no not needed here*/
None
pipedData
LogicalOperator
None
isDistinct
bool
None
projExprs
IList<QueryExpressionWithAlias>
None
implicitProjExprs
IList<QueryExpressionWithAlias>
limitClause
LimitClause
None
orderByClause
IList<SortItem>
None
filterExpr
QueryExpression
None
allLogicalOps
ISet<LogicalOperator>
None