/// <summary> /// Gets the rows for the join. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="multiPartQuery"> /// The multi part query. /// </param> /// <returns> /// The <see cref="IAsyncEnumerable{Row}"/>. /// </returns> internal override IAsyncEnumerable <Row> GetRows(IInternalExecutionContext context, [NotNull] IMultiPartQuery multiPartQuery) { var rowBuilder = new RowBuilder(); //// Build the left part by filtering by parts that contain the fields of the left side. var leftQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.Left.Aliases.Contains(f.SourceAlias)), FilterExpression = multiPartQuery.FilterExpression.FilterByAliases(this.Left.Aliases), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.Left.Aliases).ToArray(), }; //// Create the enumerable. return(context.CreateAsyncEnumerable( async() => { //// Retrieve the records from the left side. var leftData = await this.Left.GetRows(context, leftQuery).MaterializeAsync().ConfigureAwait(false); var rightQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.Right.Aliases.Contains(f.SourceAlias)), FilterExpression = CrossJoin.RangesToJoinFilter(await this.FindRangesAsync(context, multiPartQuery.FilterExpression, leftData)), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.Right.Aliases), }; var rightData = await this.Right.GetRows(context, rightQuery).MaterializeAsync().ConfigureAwait(false); return leftData.CrossJoin(rightData, rowBuilder.CombineRows); }) .Where(multiPartQuery.FilterExpression.GetRowFilter()) .OrderBy(multiPartQuery.OrderByExpressions) .AfterLastElement(count => context.Logger.Verbose($"{this.GetType().Name} returned {count} records."))); }
protected virtual JoinQuery CreateJoinQuery(IExecutionContext context, [NotNull] IMultiPartQuery query) { var filter = query.GetFilter(context); var leftFilter = filter.RemoveAllPartsThatAreNotInSource(this.Left); var rightFilter = filter.RemoveAllPartsThatAreNotInSource(this.Right); var resultFilter = filter.SplitByAndExpressions() .Except(leftFilter.SplitByAndExpressions(), JoinSourceBase.ExpressionComparer) .Except(rightFilter.SplitByAndExpressions(), JoinSourceBase.ExpressionComparer) .DefaultIfEmpty() .Aggregate(Expression.AndAlso); var leftQuery = new MultiPartQuery { Fields = query.GetUsedFields(this.Left, resultFilter), FilterExpression = leftFilter, }; var rightQuery = new MultiPartQuery { Fields = query.GetUsedFields(this.Right, resultFilter), FilterExpression = leftFilter, }; return(new JoinQuery(leftQuery, rightQuery, resultFilter, query.OrderByExpressions)); }
/// <summary> /// Gets the rows for the join. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="multiPartQuery"> /// The multi part query. /// </param> /// <returns> /// The <see cref="IAsyncEnumerable{Row}"/>. /// </returns> internal override IAsyncEnumerable <Row> GetRows(IInternalExecutionContext context, [NotNull] IMultiPartQuery multiPartQuery) { var rowBuilder = new RowBuilder(); //// For a join, we can append the filter expression (WHERE part) with the filter for the join. var filterExpression = multiPartQuery.FilterExpression == null ? this.Filter : Expression.AndAlso(multiPartQuery.FilterExpression, this.Filter); //// Build the left part by filtering by parts that contain the fields of the left side. var leftQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.Left.Aliases.Contains(f.SourceAlias)).Union(this.Filter.GetFields().Where(f => this.Left.Aliases.Contains(f.SourceAlias))), FilterExpression = filterExpression.FilterByAliases(this.Left.Aliases).Simplify(context), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.Left.Aliases).ToArray(), }; //// Create the enumerable. return(context.CreateAsyncEnumerable( async() => { //// Split by OR-expressions, var joinsParts = this.Filter.SplitByOrExpressions() //// Then, re-order AND expressions, so the most specific comparer is in front. .Select(part => part.Simplify(context).SplitByAndExpressions().Cast <CompareExpression>() .Select(c => c.MoveFieldsToTheLeft(this.Left)) .OrderBy(p => p, MostSpecificComparer.Default) .ToArray()) .ToArray(); //// Retrieve the records from the left side. var leftData = await this.Left.GetRows(context, leftQuery).MaterializeAsync().ConfigureAwait(false); if (leftData.Count == 0) { return context.CreateEmptyAsyncEnumerable <Row>(); } //// Build the right query, by using the var rightQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.Right.Aliases.Contains(f.SourceAlias)).Union(this.Filter.GetDataSourceFields(this.Right)), FilterExpression = JoinBase.RangesToJoinFilter(await this.FindRangesAsync(context, filterExpression, leftData)), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.Right.Aliases), }; var rightData = await this.Right.GetRows(context, rightQuery).MaterializeAsync().ConfigureAwait(false); var result = this.CombineResults(joinsParts, rowBuilder, leftData, rightData); result = await result.MaterializeAsync(); return result; }) .Where(multiPartQuery.FilterExpression.GetRowFilter()) .OrderBy(multiPartQuery.OrderByExpressions) .AfterLastElement(count => context.Logger.Verbose($"{this.GetType().Name} returned {count} records."))); }
protected override JoinQuery CreateJoinQuery(IExecutionContext context, [NotNull] IMultiPartQuery query) { var resultFilter = query.GetFilter(context); var leftQuery = new MultiPartQuery { Fields = query.GetUsedFields(this.Left, resultFilter), }; var rightQuery = new MultiPartQuery { Fields = query.GetUsedFields(this.Right, resultFilter), }; return(new JoinQuery(leftQuery, rightQuery, resultFilter, query.OrderByExpressions)); }
/// <summary> /// Gets the rows for the join. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="multiPartQuery"> /// The multi part query. /// </param> /// <returns> /// The <see cref="IAsyncEnumerable{Row}"/>. /// </returns> internal override IAsyncEnumerable <Row> GetRows(IInternalExecutionContext context, [NotNull] IMultiPartQuery multiPartQuery) { var rowBuilder = new RowBuilder(); //// Build the left part by filtering by parts that contain the fields of the left side. var leftQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.left.Aliases.Contains(f.SourceAlias)), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.left.Aliases).ToArray(), }; var rightQuery = new MultiPartQuery { Fields = multiPartQuery.Fields.Where(f => this.right.Aliases.Contains(f.SourceAlias)), WildcardAliases = multiPartQuery.WildcardAliases.Intersect(this.right.Aliases), }; return(this.left.GetRows(context, leftQuery).ZipAll(this.right.GetRows(context, rightQuery), rowBuilder.CombineRows) .Where(multiPartQuery.FilterExpression.GetRowFilter()) .OrderBy(multiPartQuery.OrderByExpressions) .AfterLastElement(count => context.Logger.Verbose($"{this.GetType().Name} returned {count} records."))); }
/// <summary> /// Combines the left and right result sets. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="leftData"> /// The left result set. /// </param> /// <param name="rightData"> /// The right result set. /// </param> /// <param name="rightQuery"> /// The query for the right side. /// </param> /// <param name="rowBuilder"> /// The row builder. /// </param> /// <returns> /// The <see cref="IAsyncEnumerable{Row}"/>. /// </returns> protected abstract IAsyncEnumerable <Row> CombineResults(IInternalExecutionContext context, IAsyncReadOnlyCollection <Row> leftData, IAsyncReadOnlyCollection <Row> rightData, MultiPartQuery rightQuery, RowBuilder rowBuilder);
/// <summary> /// Combines the left and right result sets. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="leftData"> /// The left result set. /// </param> /// <param name="rightData"> /// The right result set. /// </param> /// <param name="rightQuery"> /// The query for the right side. /// </param> /// <param name="rowBuilder"> /// The row builder. /// </param> /// <returns> /// The <see cref="IAsyncEnumerable{Row}"/>. /// </returns> protected override IAsyncEnumerable <Row> CombineResults(IInternalExecutionContext context, IAsyncReadOnlyCollection <Row> leftData, IAsyncReadOnlyCollection <Row> rightData, MultiPartQuery rightQuery, [NotNull] RowBuilder rowBuilder) { return(this.RightFactory != null ? leftData.CrossApply(row => this.RightFactory(context, row).GetRows(context, rightQuery), rowBuilder.CombineRows) : leftData.CrossApply(row => rightData, rowBuilder.CombineRows)); }
protected virtual JoinQuery CreateJoinQuery(IExecutionContext context, [NotNull] IMultiPartQuery query, Expression joinFilter) { var filterParts = (query.FilterExpression == null ? joinFilter : Expression.AndAlso(query.GetFilter(context), joinFilter)).SplitByAndExpressions(); // Get all parts of the query that contain fields from both sources. var joinParts = filterParts.OfType <CompareExpression>() .Where(comparison => { var fields = comparison.GetFields().Select(f => f.SourceAlias).ToArray(); return(fields.Intersect(this.Left.Aliases).Any() && fields.Intersect(this.Right.Aliases).Any()); }) .ToArray(); // What's left are the filters for only one source. These we can split in a left part, a right part, and a filter part over the result. var filter = filterParts.Except(joinParts, new ExpressionComparer()).DefaultIfEmpty().Aggregate(Expression.AndAlso); var leftFilter = filter.RemoveAllPartsThatAreNotInSource(this.Left); var rightFilter = filter.RemoveAllPartsThatAreNotInSource(this.Right); var resultFilter = filter.Except(leftFilter, rightFilter); var joinExpression = joinParts.OrderBy(p => p, new MostSpecificComparer()).First().MoveFieldsToTheLeft(this.Left); var ascending = joinExpression.CompareType != ExpressionType.GreaterThan && joinExpression.CompareType != ExpressionType.GreaterThanOrEqual; var leftQuery = new MultiPartQuery { Fields = query.Fields .Where(f => this.Left.Aliases.Contains(f.SourceAlias)) .Concat(joinParts.SelectMany(l => l.GetDataSourceFields(this.Left))) .Concat(resultFilter.GetDataSourceFields(this.Left)) .Distinct(), FilterExpression = leftFilter, OrderByExpressions = new[] { new OrderByExpression(joinExpression.Left, ascending) }, }; var rightQuery = new MultiPartQuery { Fields = query.Fields .Where(f => this.Right.Aliases.Contains(f.SourceAlias)) .Concat(joinParts.SelectMany(l => l.GetDataSourceFields(this.Right))) .Concat(resultFilter.GetDataSourceFields(this.Right)) .Distinct(), FilterExpression = rightFilter, OrderByExpressions = new[] { new OrderByExpression(joinExpression.Right, ascending) }, }; var exraJoinFilter = GenericVisitor.Visit( (ExecutionContextExpression e) => Expression.Constant(context), joinParts.Skip(1).DefaultIfEmpty().Aggregate <Expression>(Expression.AndAlso)); return(new JoinQuery( leftQuery, rightQuery, joinExpression.Left.GetRowExpression <Row>(), joinExpression.CompareType, joinExpression.Right.GetRowExpression <Row>(), joinParts.DefaultIfEmpty <Expression>().Aggregate(Expression.AndAlso), exraJoinFilter.GetJoinFunction(this.Left), query.OrderByExpressions, resultFilter)); }