public static async Task <TryCatch <IDocumentQueryExecutionComponent> > TryCreateAsync( IReadOnlyList <AggregateOperator> aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, CosmosElement continuationToken, Func <CosmosElement, Task <TryCatch <IDocumentQueryExecutionComponent> > > tryCreateSourceAsync) { if (tryCreateSourceAsync == null) { throw new ArgumentNullException(nameof(tryCreateSourceAsync)); } TryCatch <SingleGroupAggregator> tryCreateSingleGroupAggregator = SingleGroupAggregator.TryCreate( aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, continuationToken: null); if (!tryCreateSingleGroupAggregator.Succeeded) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException(tryCreateSingleGroupAggregator.Exception)); } return((await tryCreateSourceAsync(continuationToken)) .Try <IDocumentQueryExecutionComponent>((source) => { return new ClientAggregateDocumentQueryExecutionComponent( source, tryCreateSingleGroupAggregator.Result, hasSelectValue); })); }
private ComputeAggregateDocumentQueryExecutionComponent( IDocumentQueryExecutionComponent source, SingleGroupAggregator singleGroupAggregator, bool isValueAggregateQuery) : base(source, singleGroupAggregator, isValueAggregateQuery) { // all the work is done in the base constructor. }
/// <summary> /// Initializes a new instance of the AggregateDocumentQueryExecutionComponent class. /// </summary> /// <param name="source">The source component that will supply the local aggregates from multiple continuations and partitions.</param> /// <param name="singleGroupAggregator">The single group aggregator that we will feed results into.</param> /// <param name="isValueAggregateQuery">Whether or not the query has the 'VALUE' keyword.</param> /// <remarks>This constructor is private since there is some async initialization that needs to happen in CreateAsync().</remarks> protected AggregateDocumentQueryExecutionComponent( IDocumentQueryExecutionComponent source, SingleGroupAggregator singleGroupAggregator, bool isValueAggregateQuery) : base(source) { this.singleGroupAggregator = singleGroupAggregator ?? throw new ArgumentNullException(nameof(singleGroupAggregator)); this.isValueAggregateQuery = isValueAggregateQuery; }
private ComputeAggregateQueryPipelineStage( IQueryPipelineStage source, SingleGroupAggregator singleGroupAggregator, bool isValueAggregateQuery, CancellationToken cancellationToken) : base(source, singleGroupAggregator, isValueAggregateQuery, cancellationToken) { // all the work is done in the base constructor. }
/// <summary> /// Initializes a new instance of the AggregateDocumentQueryExecutionComponent class. /// </summary> /// <param name="source">The source component that will supply the local aggregates from multiple continuations and partitions.</param> /// <param name="singleGroupAggregator">The single group aggregator that we will feed results into.</param> /// <param name="isValueQuery">Whether or not the query has the 'VALUE' keyword.</param> /// <param name="cancellationToken">The cancellation token for cooperative yeilding.</param> /// <remarks>This constructor is private since there is some async initialization that needs to happen in CreateAsync().</remarks> public AggregateQueryPipelineStage( IQueryPipelineStage source, SingleGroupAggregator singleGroupAggregator, bool isValueQuery, CancellationToken cancellationToken) : base(source, cancellationToken) { this.singleGroupAggregator = singleGroupAggregator ?? throw new ArgumentNullException(nameof(singleGroupAggregator)); this.isValueQuery = isValueQuery; }
/// <summary> /// Creates a AggregateDocumentQueryExecutionComponent. /// </summary> /// <param name="aggregates">The aggregates.</param> /// <param name="aliasToAggregateType">The alias to aggregate type.</param> /// <param name="hasSelectValue">Whether or not the query has the 'VALUE' keyword.</param> /// <param name="requestContinuation">The continuation token to resume from.</param> /// <param name="createSourceCallback">The callback to create the source component that supplies the local aggregates.</param> /// <returns>The AggregateDocumentQueryExecutionComponent.</returns> public static async Task <AggregateDocumentQueryExecutionComponent> CreateAsync( AggregateOperator[] aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, bool hasSelectValue, string requestContinuation, Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback) { return(new AggregateDocumentQueryExecutionComponent( await createSourceCallback(requestContinuation), SingleGroupAggregator.Create(aggregates, aliasToAggregateType, hasSelectValue), aggregates != null && aggregates.Count() == 1)); }
public static async Task <TryCatch <IDocumentQueryExecutionComponent> > TryCreateAsync( AggregateOperator[] aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, string requestContinuation, Func <string, Task <TryCatch <IDocumentQueryExecutionComponent> > > tryCreateSourceAsync) { string sourceContinuationToken; string singleGroupAggregatorContinuationToken; if (requestContinuation != null) { if (!AggregateContinuationToken.TryParse(requestContinuation, out AggregateContinuationToken aggregateContinuationToken)) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException( new MalformedContinuationTokenException($"Malfomed {nameof(AggregateContinuationToken)}: '{requestContinuation}'"))); } sourceContinuationToken = aggregateContinuationToken.SourceContinuationToken; singleGroupAggregatorContinuationToken = aggregateContinuationToken.SingleGroupAggregatorContinuationToken; } else { sourceContinuationToken = null; singleGroupAggregatorContinuationToken = null; } TryCatch <SingleGroupAggregator> tryCreateSingleGroupAggregator = SingleGroupAggregator.TryCreate( aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, singleGroupAggregatorContinuationToken); if (!tryCreateSingleGroupAggregator.Succeeded) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException( tryCreateSingleGroupAggregator.Exception)); } return((await tryCreateSourceAsync(sourceContinuationToken)).Try <IDocumentQueryExecutionComponent>((source) => { return new ComputeAggregateDocumentQueryExecutionComponent( source, tryCreateSingleGroupAggregator.Result, hasSelectValue); })); }
public static TryCatch <IQueryPipelineStage> MonadicCreate( IReadOnlyList <AggregateOperator> aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, CosmosElement continuationToken, CancellationToken cancellationToken, MonadicCreatePipelineStage monadicCreatePipelineStage) { cancellationToken.ThrowIfCancellationRequested(); AggregateContinuationToken aggregateContinuationToken; if (continuationToken != null) { if (!AggregateContinuationToken.TryCreateFromCosmosElement( continuationToken, out aggregateContinuationToken)) { return(TryCatch <IQueryPipelineStage> .FromException( new MalformedContinuationTokenException( $"Malfomed {nameof(AggregateContinuationToken)}: '{continuationToken}'"))); } } else { aggregateContinuationToken = new AggregateContinuationToken(singleGroupAggregatorContinuationToken: null, sourceContinuationToken: null); } TryCatch <SingleGroupAggregator> tryCreateSingleGroupAggregator = SingleGroupAggregator.TryCreate( aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, aggregateContinuationToken.SingleGroupAggregatorContinuationToken); if (tryCreateSingleGroupAggregator.Failed) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateSingleGroupAggregator.Exception)); } TryCatch <IQueryPipelineStage> tryCreateSource; if (aggregateContinuationToken.SourceContinuationToken is CosmosString stringToken && (stringToken.Value == DoneSourceToken.Value)) { tryCreateSource = TryCatch <IQueryPipelineStage> .FromResult(EmptyQueryPipelineStage.Singleton); }
public static async Task <IDocumentQueryExecutionComponent> CreateAsync( CosmosQueryClient queryClient, AggregateOperator[] aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, string requestContinuation, Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback) { string sourceContinuationToken; string singleGroupAggregatorContinuationToken; if (requestContinuation != null) { if (!AggregateContinuationToken.TryParse(requestContinuation, out AggregateContinuationToken aggregateContinuationToken)) { throw queryClient.CreateBadRequestException($"Malfomed {nameof(AggregateContinuationToken)}: '{requestContinuation}'"); } sourceContinuationToken = aggregateContinuationToken.SourceContinuationToken; singleGroupAggregatorContinuationToken = aggregateContinuationToken.SingleGroupAggregatorContinuationToken; } else { sourceContinuationToken = null; singleGroupAggregatorContinuationToken = null; } IDocumentQueryExecutionComponent source = await createSourceCallback(sourceContinuationToken); SingleGroupAggregator singleGroupAggregator = SingleGroupAggregator.Create( queryClient, aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, singleGroupAggregatorContinuationToken); return(new ComputeAggregateDocumentQueryExecutionComponent( source, singleGroupAggregator, hasSelectValue)); }
public static TryCatch <IQueryPipelineStage> MonadicCreate( IReadOnlyList <AggregateOperator> aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, CosmosElement continuationToken, CancellationToken cancellationToken, MonadicCreatePipelineStage monadicCreatePipelineStage) { if (monadicCreatePipelineStage == null) { throw new ArgumentNullException(nameof(monadicCreatePipelineStage)); } TryCatch <SingleGroupAggregator> tryCreateSingleGroupAggregator = SingleGroupAggregator.TryCreate( aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, continuationToken: null); if (tryCreateSingleGroupAggregator.Failed) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateSingleGroupAggregator.Exception)); } TryCatch <IQueryPipelineStage> tryCreateSource = monadicCreatePipelineStage(continuationToken, cancellationToken); if (tryCreateSource.Failed) { return(tryCreateSource); } ClientAggregateQueryPipelineStage stage = new ClientAggregateQueryPipelineStage( tryCreateSource.Result, tryCreateSingleGroupAggregator.Result, hasSelectValue, cancellationToken); return(TryCatch <IQueryPipelineStage> .FromResult(stage)); }
public static async Task <IDocumentQueryExecutionComponent> CreateAsync( CosmosQueryClient queryClient, AggregateOperator[] aggregates, IReadOnlyDictionary <string, AggregateOperator?> aliasToAggregateType, IReadOnlyList <string> orderedAliases, bool hasSelectValue, string requestContinuation, Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback) { IDocumentQueryExecutionComponent source = await createSourceCallback(requestContinuation); SingleGroupAggregator singleGroupAggregator = SingleGroupAggregator.Create( queryClient, aggregates, aliasToAggregateType, orderedAliases, hasSelectValue, continuationToken: null); return(new ClientAggregateDocumentQueryExecutionComponent( source, singleGroupAggregator, hasSelectValue)); }
public override async Task <QueryResponseCore> DrainAsync( int maxElements, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Draining GROUP BY is broken down into two stages: QueryResponseCore response; if (!this.Source.IsDone) { // Stage 1: // Drain the groupings fully from all continuation and all partitions QueryResponseCore sourceResponse = await base.DrainAsync(int.MaxValue, cancellationToken); if (!sourceResponse.IsSuccess) { return(sourceResponse); } foreach (CosmosElement result in sourceResponse.CosmosElements) { // Aggregate the values for all groupings across all continuations. RewrittenGroupByProjection groupByItem = new RewrittenGroupByProjection(result); UInt192 groupByKeysHash = DistinctHash.GetHash(groupByItem.GroupByItems); if (!this.groupingTable.TryGetValue(groupByKeysHash, out SingleGroupAggregator singleGroupAggregator)) { singleGroupAggregator = SingleGroupAggregator.Create( this.cosmosQueryClient, EmptyAggregateOperators, this.groupByAliasToAggregateType, this.orderedAliases, this.hasSelectValue, continuationToken: null); this.groupingTable[groupByKeysHash] = singleGroupAggregator; } CosmosElement payload = groupByItem.Payload; singleGroupAggregator.AddValues(payload); } // We need to give empty pages until the results are fully drained. response = QueryResponseCore.CreateSuccess( result: EmptyResults, continuationToken: null, disallowContinuationTokenMessage: GroupByDocumentQueryExecutionComponent.ContinuationTokenNotSupportedWithGroupBy, activityId: sourceResponse.ActivityId, requestCharge: sourceResponse.RequestCharge, diagnostics: sourceResponse.Diagnostics, responseLengthBytes: sourceResponse.ResponseLengthBytes); this.isDone = false; } else { // Stage 2: // Emit the results from the grouping table page by page IEnumerable <SingleGroupAggregator> groupByValuesList = this.groupingTable .OrderBy(kvp => kvp.Key) .Skip(this.numPagesDrainedFromGroupingTable * maxElements) .Take(maxElements) .Select(kvp => kvp.Value); List <CosmosElement> results = new List <CosmosElement>(); foreach (SingleGroupAggregator groupByValues in groupByValuesList) { results.Add(groupByValues.GetResult()); } response = QueryResponseCore.CreateSuccess( result: results, continuationToken: null, disallowContinuationTokenMessage: GroupByDocumentQueryExecutionComponent.ContinuationTokenNotSupportedWithGroupBy, activityId: null, requestCharge: 0, diagnostics: QueryResponseCore.EmptyDiagnostics, responseLengthBytes: 0); this.numPagesDrainedFromGroupingTable++; if (this.numPagesDrainedFromGroupingTable * maxElements >= this.groupingTable.Count) { this.isDone = true; } } return(response); }
public override async Task <QueryResponse> DrainAsync( int maxElements, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Draining GROUP BY is broken down into two stages: QueryResponse response; if (!this.Source.IsDone) { // Stage 1: // Drain the groupings fully from all continuation and all partitions QueryResponse sourceResponse = await base.DrainAsync(int.MaxValue, cancellationToken); if (!sourceResponse.IsSuccessStatusCode) { return(sourceResponse); } this.containerRid = sourceResponse.QueryHeaders.ContainerRid; foreach (CosmosElement result in sourceResponse.CosmosElements) { // Aggregate the values for all groupings across all continuations. RewrittenGroupByProjection groupByItem = new RewrittenGroupByProjection(result); this.distinctMap.Add(groupByItem.GroupByItems, out UInt192? groupByKeysHash); if (!groupByKeysHash.HasValue) { throw new InvalidOperationException("hash invariant was broken"); } if (!this.groupingTable.TryGetValue(groupByKeysHash.Value, out SingleGroupAggregator singleGroupAggregator)) { singleGroupAggregator = SingleGroupAggregator.Create( EmptyAggregateOperators, this.groupByAliasToAggregateType, this.hasSelectValue); this.groupingTable[groupByKeysHash.Value] = singleGroupAggregator; } CosmosElement payload = groupByItem.Payload; singleGroupAggregator.AddValues(payload); } string updatedContinuationToken = null; // We need to give empty pages until the results are fully drained. response = QueryResponse.CreateSuccess( EmptyResults, EmptyResults.Count, sourceResponse.ResponseLengthBytes, sourceResponse.QueryHeaders.CloneKnownProperties(updatedContinuationToken, GroupByDocumentQueryExecutionComponent.ContinuationTokenNotSupportedWithGroupBy)); this.isDone = false; } else { // Stage 2: // Emit the results from the grouping table page by page IEnumerable <SingleGroupAggregator> groupByValuesList = this.groupingTable .OrderBy(kvp => kvp.Key) .Skip(this.numPagesDrainedFromGroupingTable * maxElements) .Take(maxElements) .Select(kvp => kvp.Value); List <CosmosElement> results = new List <CosmosElement>(); foreach (SingleGroupAggregator groupByValues in groupByValuesList) { results.Add(groupByValues.GetResult()); } response = QueryResponse.CreateSuccess( results, results.Count, responseLengthBytes: 0, responseHeaders: new CosmosQueryResponseMessageHeaders( continauationToken: null, disallowContinuationTokenMessage: GroupByDocumentQueryExecutionComponent.ContinuationTokenNotSupportedWithGroupBy, resourceType: Documents.ResourceType.Document, containerRid: this.containerRid)); this.numPagesDrainedFromGroupingTable++; if (this.numPagesDrainedFromGroupingTable * maxElements >= this.groupingTable.Count) { this.isDone = true; } } return(response); }