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