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.
 }
Exemple #3
0
 /// <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.
 }
Exemple #5
0
 /// <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;
 }
Exemple #6
0
 /// <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);
                }
Exemple #9
0
            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));
            }
Exemple #12
0
        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);
        }