/// <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); }
/// <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); }
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); }
/// <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" }); }
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); }
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); }
/// <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); }