private static IEnumerable <CosmosElement> ProjectOnGroupings( IEnumerable <IGrouping <GroupByKey, CosmosElement> > groupings, SqlSelectClause sqlSelectClause) { foreach (IGrouping <GroupByKey, CosmosElement> grouping in groupings) { IEnumerable <CosmosElement> dataSource = grouping; if (AggregateProjectionDector.HasAggregate(sqlSelectClause.SelectSpec)) { // If there is an aggregate then we need to just project out the one document // But we need to transform the query to first evaluate the aggregate on all the documents AggregateProjectionTransformer aggregateProjectionTransformer = new AggregateProjectionTransformer(dataSource); SqlSelectSpec transformedSpec = aggregateProjectionTransformer .TransformAggregatesInProjection(sqlSelectClause.SelectSpec); CosmosElement aggregationResult = transformedSpec.Accept( Projector.Singleton, dataSource.FirstOrDefault()); if (aggregationResult != null) { dataSource = new CosmosElement[] { aggregationResult }; } else { dataSource = Array.Empty <CosmosElement>(); } } else { dataSource = dataSource .Select(element => sqlSelectClause.SelectSpec.Accept( Projector.Singleton, element)) .Where(projection => projection != Undefined); } if (dataSource.Any()) { yield return(dataSource.First()); } } }
public static IEnumerable <CosmosElement> ExecuteQuery( IEnumerable <CosmosElement> dataSource, SqlQuery sqlQuery, IReadOnlyDictionary <string, PartitionKeyRange> ridToPartitionKeyRange = null) { if (dataSource == null) { throw new ArgumentNullException($"{nameof(dataSource)} must not be null."); } if (sqlQuery == null) { throw new ArgumentNullException($"{nameof(sqlQuery)} must not be null."); } PerformStaticAnalysis(sqlQuery); if (ridToPartitionKeyRange == null) { ridToPartitionKeyRange = SinglePartitionRidToPartitionKeyRange.Value; } // From clause binds the data for the rest of the pipeline if (sqlQuery.FromClause != null) { dataSource = ExecuteFromClause( dataSource, sqlQuery.FromClause); } else { dataSource = NoFromClauseDataSource; } // We execute the filter here to reduce the data set as soon as possible. if (sqlQuery.WhereClause != null) { dataSource = ExecuteWhereClause( dataSource, sqlQuery.WhereClause); } // We sort before the projection, // since the projection might remove the order by items. if (sqlQuery.OrderByClause != null) { dataSource = ExecuteOrderByClause( dataSource, sqlQuery.OrderByClause, ridToPartitionKeyRange); } else { // Even for non order by queries we need to order by partition key ranges and document ids dataSource = ExecuteCrossPartitionOrdering( dataSource, ridToPartitionKeyRange); } IEnumerable <IGrouping <GroupByKey, CosmosElement> > groupings; // We need to create the groupings at this point for the rest of the pipeline if (sqlQuery.GroupByClause != null) { groupings = ExecuteGroupByClause( dataSource, sqlQuery.GroupByClause); } else { if (AggregateProjectionDector.HasAggregate(sqlQuery.SelectClause.SelectSpec)) { groupings = CreateOneGroupingForWholeCollection(dataSource); } else { groupings = CreateOneGroupingForEachDocument(dataSource); } } // We finally project out the needed columns and remove all binding artifacts dataSource = ExecuteSelectClause( groupings, sqlQuery.SelectClause); // Offset limit just performs skip take if (sqlQuery.OffsetLimitClause != null) { dataSource = ExecuteOffsetLimitClause(dataSource, sqlQuery.OffsetLimitClause); } return(dataSource); }