Beispiel #1
0
        /// <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.")));
        }
Beispiel #2
0
        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));
        }
Beispiel #3
0
        /// <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.")));
        }
Beispiel #4
0
        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.")));
        }
Beispiel #6
0
 /// <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);
Beispiel #7
0
 /// <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));
 }
Beispiel #8
0
        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));
        }