/// <summary>
        /// Parses the new object that is part of the select expression with "as" based formatting
        /// </summary>
        private Expression VisitSelectNewExpression(NewExpression expression)
        {
            var arguments = expression.Arguments;
            var members   = expression.Members;

            for (var i = 0; i < members.Count; i++)
            {
                if (i > 0)
                {
                    _expression.Append(", ");
                }

                var expressionLength = _expression.Length;

                VisitExpression(arguments[i]);

                //only add 'as' part if the  previous visitexpression has generated something.
                if (_expression.Length > expressionLength)
                {
                    _expression.AppendFormat(" as {0}", N1QlHelpers.EscapeIdentifier(members[i].Name));
                }
                else if (i > 0)
                {
                    // nothing was added, so remove the extra comma
                    _expression.Length -= 2;
                }
            }

            return(expression);
        }
        protected override Expression VisitMemberExpression(MemberExpression expression)
        {
            if (expression.Expression.Type.Assembly.GetName().Name == "mscorlib")
            {
                // For property getters on the core .Net classes, we don't want to just recurse through the .Net object model
                // Instead, convert to a MethodCallExpression
                // And it will pass through the appropriate IMethodCallTranslator
                // (i.e. string.Length)

                var propInfo = expression.Member as PropertyInfo;
                if ((propInfo != null) && (propInfo.GetMethod != null) && (propInfo.GetMethod.GetParameters().Length == 0))
                {
                    // Convert to a property getter method call
                    var newExpression = System.Linq.Expressions.Expression.Call(
                        expression.Expression,
                        propInfo.GetMethod);

                    return(VisitExpression(newExpression));
                }
            }

            string memberName;

            if (_queryGenerationContext.MemberNameResolver.TryResolveMemberName(expression.Member, out memberName))
            {
                VisitExpression(expression.Expression);
                _expression.AppendFormat(".{0}", N1QlHelpers.EscapeIdentifier(memberName));
            }

            return(expression);
        }
예제 #3
0
        /// <summary>
        /// Visits a nest against a constant expression, which must be an IBucketQueryable implementation
        /// </summary>
        /// <param name="nestClause">Nest clause being visited</param>
        /// <param name="constantExpression">Constant expression that is the InnerSequence of the NestClause</param>
        /// <param name="itemName">Name to be used when referencing the data being nested</param>
        /// <returns>N1QlFromQueryPart to be added to the QueryPartsAggregator</returns>
        private N1QlFromQueryPart VisitConstantExpressionNestClause(NestClause nestClause, ConstantExpression constantExpression, string itemName)
        {
            string bucketName = null;

            if (constantExpression != null)
            {
                var bucketQueryable = constantExpression.Value as IBucketQueryable;
                if (bucketQueryable != null)
                {
                    bucketName = bucketQueryable.BucketName;
                }
            }

            if (bucketName == null)
            {
                throw new NotSupportedException("N1QL Nests Must Be Against IBucketQueryable");
            }

            return(new N1QlFromQueryPart()
            {
                Source = N1QlHelpers.EscapeIdentifier(bucketName),
                ItemName = itemName,
                OnKeys = GetN1QlExpression(nestClause.KeySelector),
                JoinType = nestClause.IsLeftOuterNest ? "LEFT OUTER NEST" : "INNER NEST"
            });
        }
        public void SetExtentName(IQuerySource querySource, string extentName)
        {
            if (querySource == null)
            {
                throw new ArgumentNullException("extentName");
            }
            if (string.IsNullOrEmpty(extentName))
            {
                return;
            }

            if (_extentDictionary.ContainsKey(querySource))
            {
                throw new InvalidOperationException("Cannot set the extent name on a query source which already has a name.");
            }

            _extentDictionary[querySource] = N1QlHelpers.EscapeIdentifier(extentName);
        }
예제 #5
0
        protected override Expression VisitMember(MemberExpression expression)
        {
            if (expression.Expression.Type.GetTypeInfo().Assembly.Equals(Mscorlib))
            {
                // For property getters on the core .Net classes, we don't want to just recurse through the .Net object model
                // Instead, convert to a MethodCallExpression
                // And it will pass through the appropriate IMethodCallTranslator
                // (i.e. string.Length)

                var propInfo = expression.Member as PropertyInfo;
                if ((propInfo != null) && (propInfo.GetMethod != null) && (propInfo.GetMethod.GetParameters().Length == 0))
                {
                    // Convert to a property getter method call
                    var newExpression = System.Linq.Expressions.Expression.Call(
                        expression.Expression,
                        propInfo.GetMethod);

                    return(Visit(newExpression));
                }
            }

            string memberName;

            if (_queryGenerationContext.MemberNameResolver.TryResolveMemberName(expression.Member, out memberName))
            {
                var querySourceExpression = expression.Expression as QuerySourceReferenceExpression;
                if ((querySourceExpression != null) &&
                    (_queryGenerationContext.ExtentNameProvider.GetExtentName(
                         querySourceExpression.ReferencedQuerySource) == ""))
                {
                    // This query source has a blank extent name, so we don't need to reference the extent to access the member

                    _expression.Append(N1QlHelpers.EscapeIdentifier(memberName));
                }
                else
                {
                    Visit(expression.Expression);
                    _expression.AppendFormat(".{0}", N1QlHelpers.EscapeIdentifier(memberName));
                }
            }

            return(expression);
        }
예제 #6
0
        /// <summary>
        /// Visits a join against a constant expression, which must be an IBucketQueryable implementation
        /// </summary>
        /// <param name="joinClause">Join clause being visited</param>
        /// <param name="constantExpression">Constant expression that is the InnerSequence of the JoinClause</param>
        /// <returns>N1QlFromQueryPart to be added to the QueryPartsAggregator.  JoinType is defaulted to INNER JOIN.</returns>
        /// <remarks>The InnerKeySelector must be selecting the N1Ql.Key of the InnerSequence</remarks>
        private N1QlFromQueryPart VisitConstantExpressionJoinClause(JoinClause joinClause, ConstantExpression constantExpression)
        {
            string bucketName = null;

            if (constantExpression != null)
            {
                var bucketQueryable = constantExpression.Value as IBucketQueryable;
                if (bucketQueryable != null)
                {
                    bucketName = bucketQueryable.BucketName;
                }
            }

            if (bucketName == null)
            {
                throw new NotSupportedException("N1QL Joins Must Be Against IBucketQueryable");
            }

            var keyExpression = joinClause.InnerKeySelector as MethodCallExpression;

            if ((keyExpression == null) ||
                (keyExpression.Method != typeof(N1Ql).GetMethod("Key")) ||
                (keyExpression.Arguments.Count != 1))
            {
                throw new NotSupportedException("N1QL Join Selector Must Be A Call To N1Ql.Key");
            }

            if (!(keyExpression.Arguments[0] is QuerySourceReferenceExpression))
            {
                throw new NotSupportedException("N1QL Join Selector Call To N1Ql.Key Must Reference The Inner Sequence");
            }

            return(new N1QlFromQueryPart()
            {
                Source = N1QlHelpers.EscapeIdentifier(bucketName),
                ItemName = GetExtentName(joinClause),
                OnKeys = GetN1QlExpression(joinClause.OuterKeySelector),
                JoinType = "INNER JOIN"
            });
        }
예제 #7
0
 private string GetNextExtentName()
 {
     return(N1QlHelpers.EscapeIdentifier(string.Format(ExtentNameFormat, ++_extentIndex)));
 }
        /// <summary>
        /// Parses the new object that is part of the select expression with "as" based formatting.
        /// Can accept either a NewExpression or MemberInitExpression
        /// </summary>
        private Expression VisitSelectNewExpression(Expression expression)
        {
            IList <Expression> arguments;
            IList <MemberInfo> members;

            var memberInitExpression = expression as MemberInitExpression;

            if (memberInitExpression != null)
            {
                if (memberInitExpression.NewExpression.Arguments.Count > 0)
                {
                    throw new NotSupportedException("New Objects Must Be Initialized With A Parameterless Constructor");
                }
                if (memberInitExpression.Bindings.Any(p => p.BindingType != MemberBindingType.Assignment))
                {
                    throw new NotSupportedException("New Objects Must Be Initialized With Assignments Only");
                }

                arguments = memberInitExpression.Bindings.Cast <MemberAssignment>().Select(p => p.Expression).ToList();
                members   = memberInitExpression.Bindings.Select(p => p.Member).ToList();
            }
            else
            {
                var newExpression = expression as NewExpression;
                if (newExpression == null)
                {
                    throw new NotSupportedException("Unsupported Select Clause Expression");
                }

                arguments = newExpression.Arguments;
                members   = newExpression.Members;
            }

            for (var i = 0; i < members.Count; i++)
            {
                if (i > 0)
                {
                    _expression.Append(", ");
                }

                var expressionLength = _expression.Length;

                Visit(arguments[i]);

                //only add 'as' part if the  previous visitexpression has generated something.
                if (_expression.Length > expressionLength)
                {
                    if (!QueryGenerationContext.MemberNameResolver.TryResolveMemberName(members[i], out var memberName))
                    {
                        memberName = members[i].Name;
                    }

                    _expression.AppendFormat(" as {0}", N1QlHelpers.EscapeIdentifier(memberName));
                }
                else if (i > 0)
                {
                    // nothing was added, so remove the extra comma
                    _expression.Length -= 2;
                }
            }

            return(expression);
        }
예제 #9
0
        private string GetSelectParameters(SelectClause selectClause, QueryModel queryModel)
        {
            string expression;

            if (selectClause.Selector.GetType() == typeof(QuerySourceReferenceExpression))
            {
                if (_queryPartsAggregator.AggregateFunction == null)
                {
                    expression = GetN1QlExpression(selectClause.Selector);

                    if (_queryPartsAggregator.QueryType != N1QlQueryType.Array)
                    {
                        expression = string.Concat(expression, ".*");
                    }
                }
                else
                {
                    // for aggregates, just use "*" (i.e. AggregateFunction = "COUNT", expression = "*" results in COUNT(*)"

                    expression = "*";
                }
            }
            else if (selectClause.Selector.NodeType == ExpressionType.New)
            {
                if (_queryPartsAggregator.QueryType != N1QlQueryType.Array)
                {
                    var selector = selectClause.Selector as NewExpression;

                    if (_groupingStatus == GroupingStatus.AfterGroupSubquery)
                    {
                        // SELECT clauses must be remapped to refer directly to the extents in the grouping subquery
                        // rather than refering to the output of the grouping subquery

                        selector = (NewExpression)TransformingExpressionTreeVisitor.Transform(selector, _groupingExpressionTransformerRegistry);
                    }

                    expression =
                        N1QlExpressionTreeVisitor.GetN1QlSelectNewExpression(selector, _queryGenerationContext);
                }
                else
                {
                    expression = GetN1QlExpression(selectClause.Selector);
                }
            }
            else
            {
                expression = GetN1QlExpression(selectClause.Selector);

                if ((_queryPartsAggregator.QueryType == N1QlQueryType.Subquery) || (_queryPartsAggregator.QueryType == N1QlQueryType.Array))
                {
                    // For LINQ, this subquery is expected to return a list of the specific property being selected
                    // But N1QL will always return a list of objects with a single property
                    // So we need to use an ARRAY statement to convert the list

                    _queryPartsAggregator.ArrayPropertyExtractionPart = N1QlHelpers.EscapeIdentifier("result");

                    expression += " as " + _queryPartsAggregator.ArrayPropertyExtractionPart;
                }
            }

            return(expression);
        }
예제 #10
0
        /// <exception cref="NotSupportedException">N1Ql Bucket Subqueries Require A UseKeys Call</exception>
        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
        {
            var bucketConstantExpression = fromClause.FromExpression as ConstantExpression;

            if ((bucketConstantExpression != null) &&
                typeof(IBucketQueryable).IsAssignableFrom(bucketConstantExpression.Type))
            {
                if (_isSubQuery && !queryModel.BodyClauses.Any(p => p is UseKeysClause))
                {
                    throw new NotSupportedException("N1Ql Bucket Subqueries Require A UseKeys Call");
                }

                _queryPartsAggregator.AddFromPart(new N1QlFromQueryPart()
                {
                    Source   = N1QlHelpers.EscapeIdentifier(((IBucketQueryable)bucketConstantExpression.Value).BucketName),
                    ItemName = GetExtentName(fromClause)
                });
            }
            else if (fromClause.FromExpression.NodeType == ExpressionType.MemberAccess)
            {
                if (!_isSubQuery)
                {
                    throw new NotSupportedException("Member Access In The Main From Clause Is Only Supported In Subqueries");
                }

                _queryPartsAggregator.AddFromPart(new N1QlFromQueryPart()
                {
                    Source   = GetN1QlExpression((MemberExpression)fromClause.FromExpression),
                    ItemName = GetExtentName(fromClause)
                });

                // This is an Array type subquery, since we're querying against a member not a bucket
                _queryPartsAggregator.QueryType = N1QlQueryType.Array;
            }
            else if (fromClause.FromExpression.NodeType == SubQueryExpression.ExpressionType)
            {
                var subQuery = (SubQueryExpression)fromClause.FromExpression;
                if (!subQuery.QueryModel.ResultOperators.Any(p => p is GroupResultOperator))
                {
                    throw new NotSupportedException("Subqueries In The Main From Clause Are Only Supported For Grouping");
                }

                _groupingStatus = GroupingStatus.InGroupSubquery;
                _queryGenerationContext.GroupingQuerySource = new QuerySourceReferenceExpression(fromClause);

                VisitQueryModel(subQuery.QueryModel);

                _groupingStatus = GroupingStatus.AfterGroupSubquery;
            }
            else if (fromClause.FromExpression.NodeType == QuerySourceReferenceExpression.ExpressionType)
            {
                if (!fromClause.FromExpression.Equals(_queryGenerationContext.GroupingQuerySource))
                {
                    throw new NotSupportedException("From Clauses May Not Reference Any Query Source Other Than The Grouping Subquery");
                }

                // We're performing an aggregate against a group
                _queryPartsAggregator.QueryType = N1QlQueryType.Aggregate;

                // Ensure that we use the same extent name as the grouping
                _queryGenerationContext.ExtentNameProvider.LinkExtents(_queryGenerationContext.GroupingQuerySource.ReferencedQuerySource, fromClause);
            }

            base.VisitMainFromClause(fromClause, queryModel);
        }
        private string GetSelectParameters(SelectClause selectClause, QueryModel queryModel)
        {
            string expression;

            if (selectClause.Selector.GetType() == typeof(QuerySourceReferenceExpression))
            {
                if (_queryPartsAggregator.AggregateFunction == null)
                {
                    expression = GetN1QlExpression(selectClause.Selector);

                    if (_queryPartsAggregator.QueryType != N1QlQueryType.Array)
                    {
                        expression = string.Concat(expression, ".*");
                    }
                }
                else
                {
                    // for aggregates, just use "*" (i.e. AggregateFunction = "COUNT", expression = "*" results in COUNT(*)"

                    ResultExtractionRequired = true;
                    _queryPartsAggregator.PropertyExtractionPart = N1QlHelpers.EscapeIdentifier("result");
                    expression = "*";
                }
            }
            else if (selectClause.Selector.NodeType == ExpressionType.New)
            {
                if (_queryPartsAggregator.QueryType != N1QlQueryType.Array)
                {
                    var selector = selectClause.Selector as NewExpression;

                    if (_groupingStatus == GroupingStatus.AfterGroupSubquery)
                    {
                        // SELECT clauses must be remapped to refer directly to the extents in the grouping subquery
                        // rather than refering to the output of the grouping subquery

                        selector = (NewExpression)TransformingExpressionTreeVisitor.Transform(selector, _groupingExpressionTransformerRegistry);
                    }

                    expression =
                        N1QlExpressionTreeVisitor.GetN1QlSelectNewExpression(selector, _queryGenerationContext);
                }
                else
                {
                    expression = GetN1QlExpression(selectClause.Selector);
                }
            }
            else
            {
                expression = GetN1QlExpression(selectClause.Selector);

                if (_queryPartsAggregator.QueryType == N1QlQueryType.Subquery)
                {
                    // For LINQ, this subquery is expected to return a list of the specific property being selected
                    // But N1QL will always return a list of objects with a single property
                    // So we need to use an ARRAY statement to convert the list

                    _queryPartsAggregator.PropertyExtractionPart = N1QlHelpers.EscapeIdentifier("result");

                    expression += " as " + _queryPartsAggregator.PropertyExtractionPart;
                }
                else
                {
                    // This is a select expression on the main query that doesn't use a "new" clause, and isn't against an extent
                    // So it will return an array of objects with properties, while LINQ is expecting an array of values
                    // Since we can't extract the properties from the object in N1QL like we can with subqueries
                    // We need to indicate to the BucketQueryExecutor that it will need to extract the properties

                    _queryPartsAggregator.PropertyExtractionPart = N1QlHelpers.EscapeIdentifier("result");
                    ResultExtractionRequired = true;
                }
            }

            return(expression);
        }