protected override void ApplyNodeSpecificSemantics(QueryModel queryModel, ClauseGenerationContext clauseGenerationContext) { var nestClause = new NestClause( ResultSelector.Parameters[1].Name, ResultSelector.Parameters[1].Type, InnerSequence, GetResolvedKeySelector(clauseGenerationContext), IsLeftOuterNest); clauseGenerationContext.AddContextInfo(this, nestClause); queryModel.BodyClauses.Add(nestClause); var selectClause = queryModel.SelectClause; selectClause.Selector = GetResolvedResultSelector(clauseGenerationContext); }
/// <summary> /// Clones this clause. /// </summary> /// <param name="cloneContext">The clones of all query source clauses are registered with this <see cref="CloneContext" />.</param> /// <returns></returns> public virtual NestClause Clone(CloneContext cloneContext) { var clone = new NestClause(ItemName, ItemType, InnerSequence, KeySelector, IsLeftOuterNest); return clone; }
/// <summary> /// Clones this clause. /// </summary> /// <param name="cloneContext">The clones of all query source clauses are registered with this <see cref="CloneContext" />.</param> /// <returns></returns> public virtual NestClause Clone(CloneContext cloneContext) { var clone = new NestClause(ItemName, ItemType, InnerSequence, KeySelector, IsLeftOuterNest); return(clone); }
/// <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" }; }
/// <summary> /// Visits a nest against either a constant expression of IBucketQueryable, or a subquery based on an IBucketQueryable /// </summary> /// <param name="nestClause">Nest clause being visited</param> /// <returns>N1QlFromQueryPart to be added to the QueryPartsAggregator</returns> private N1QlFromQueryPart ParseNestClause(NestClause nestClause) { switch (nestClause.InnerSequence.NodeType) { case ExpressionType.Constant: return VisitConstantExpressionNestClause(nestClause, nestClause.InnerSequence as ConstantExpression, GetExtentName(nestClause)); case SubQueryExpression.ExpressionType: // SubQueryExpression var subQuery = nestClause.InnerSequence as SubQueryExpression; if ((subQuery == null) || subQuery.QueryModel.ResultOperators.Any() || subQuery.QueryModel.MainFromClause.FromExpression.NodeType != ExpressionType.Constant) { throw new NotSupportedException("Unsupported Nest Inner Sequence"); } // Generate a temporary item name to use on the NEST statement, which we can then reference in the LET statement var genItemName = _queryGenerationContext.ExtentNameProvider.GetUnlinkedExtentName(); var fromPart = VisitConstantExpressionNestClause(nestClause, subQuery.QueryModel.MainFromClause.FromExpression as ConstantExpression, genItemName); // Put any where clauses in the sub query in an ARRAY filtering clause using a LET statement var whereClauseString = string.Join(" AND ", subQuery.QueryModel.BodyClauses.OfType<WhereClause>() .Select(p => GetN1QlExpression(p.Predicate))); var letPart = new N1QlLetQueryPart() { ItemName = GetExtentName(nestClause), Value = string.Format("ARRAY {0} FOR {0} IN {1} WHEN {2} END", GetExtentName(subQuery.QueryModel.MainFromClause), genItemName, whereClauseString) }; _queryPartsAggregator.AddLetPart(letPart); if (!nestClause.IsLeftOuterNest) { // This is an INNER NEST, but the inner sequence filter is being applied after the NEST operation is done // So we need to put an additional filter to drop rows with an empty array result _queryPartsAggregator.AddWherePart("(ARRAY_LENGTH({0}) > 0)", letPart.ItemName); } return fromPart; default: throw new NotSupportedException("Unsupported Nest Inner Sequence"); } }
public void VisitNestClause(NestClause nestClause, QueryModel queryModel, int index) { EnsureNotArraySubquery(); _queryPartsAggregator.AddFromPart(ParseNestClause(nestClause)); }