public static async Task <TryCatch <CosmosOrderByItemQueryExecutionContext> > TryCreateAsync(
            CosmosQueryContext queryContext,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            string requestContinuationToken,
            CancellationToken cancellationToken)
        {
            Debug.Assert(
                initParams.PartitionedQueryExecutionInfo.QueryInfo.HasOrderBy,
                "OrderBy~Context must have order by query info.");

            if (queryContext == null)
            {
                throw new ArgumentNullException(nameof(queryContext));
            }

            cancellationToken.ThrowIfCancellationRequested();

            CosmosOrderByItemQueryExecutionContext context = new CosmosOrderByItemQueryExecutionContext(
                initPararms: queryContext,
                maxConcurrency: initParams.MaxConcurrency,
                maxItemCount: initParams.MaxItemCount,
                maxBufferedItemCount: initParams.MaxBufferedItemCount,
                consumeComparer: new OrderByConsumeComparer(initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy));

            return((await context.TryInitializeAsync(
                        sqlQuerySpec: initParams.SqlQuerySpec,
                        requestContinuation: requestContinuationToken,
                        collectionRid: initParams.CollectionRid,
                        partitionKeyRanges: initParams.PartitionKeyRanges,
                        initialPageSize: initParams.InitialPageSize,
                        sortOrders: initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy,
                        orderByExpressions: initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderByExpressions,
                        cancellationToken: cancellationToken))
                   .Try <CosmosOrderByItemQueryExecutionContext>((ignore) => context));
        }
        /// <summary>
        /// Creates an CosmosOrderByItemQueryExecutionContext
        /// </summary>
        /// <param name="constructorParams">The parameters for the base class constructor.</param>
        /// <param name="initParams">The parameters to initialize the base class.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>A task to await on, which in turn creates an CosmosOrderByItemQueryExecutionContext.</returns>
        public static async Task <CosmosOrderByItemQueryExecutionContext> CreateAsync(
            CosmosQueryContext constructorParams,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            CancellationToken token)
        {
            Debug.Assert(
                initParams.PartitionedQueryExecutionInfo.QueryInfo.HasOrderBy,
                "OrderBy~Context must have order by query info.");

            CosmosOrderByItemQueryExecutionContext context = new CosmosOrderByItemQueryExecutionContext(
                constructorParams,
                new OrderByConsumeComparer(initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy));

            await context.InitializeAsync(
                constructorParams.SqlQuerySpec,
                initParams.RequestContinuation,
                initParams.CollectionRid,
                initParams.PartitionKeyRanges,
                initParams.InitialPageSize,
                initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy,
                initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderByExpressions,
                token);

            return(context);
        }
        /// <summary>
        /// Creates an CosmosOrderByItemQueryExecutionContext
        /// </summary>
        /// <param name="queryContext">The parameters for the base class constructor.</param>
        /// <param name="initParams">The parameters to initialize the base class.</param>
        /// <param name="requestContinuationToken">The request continuation.</param>
        /// <param name="token">The cancellation token.</param>
        /// <returns>A task to await on, which in turn creates an CosmosOrderByItemQueryExecutionContext.</returns>
        public static async Task <CosmosOrderByItemQueryExecutionContext> CreateAsync(
            CosmosQueryContext queryContext,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            string requestContinuationToken,
            CancellationToken token)
        {
            Debug.Assert(
                initParams.PartitionedQueryExecutionInfo.QueryInfo.HasOrderBy,
                "OrderBy~Context must have order by query info.");

            CosmosOrderByItemQueryExecutionContext context = new CosmosOrderByItemQueryExecutionContext(
                initPararms: queryContext,
                maxConcurrency: initParams.MaxConcurrency,
                maxItemCount: initParams.MaxItemCount,
                maxBufferedItemCount: initParams.MaxBufferedItemCount,
                consumeComparer: new OrderByConsumeComparer(initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy));

            await context.InitializeAsync(
                sqlQuerySpec : initParams.SqlQuerySpec,
                requestContinuation : requestContinuationToken,
                collectionRid : initParams.CollectionRid,
                partitionKeyRanges : initParams.PartitionKeyRanges,
                initialPageSize : initParams.InitialPageSize,
                sortOrders : initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderBy,
                orderByExpressions : initParams.PartitionedQueryExecutionInfo.QueryInfo.OrderByExpressions,
                cancellationToken : token);

            return(context);
        }
        /// <summary>
        /// Creates a CosmosPipelinedItemQueryExecutionContext.
        /// </summary>
        /// <param name="queryContext">The parameters for constructing the base class.</param>
        /// <param name="initParams">The initial parameters</param>
        /// <param name="requestContinuationToken">The request continuation.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task to await on, which in turn returns a CosmosPipelinedItemQueryExecutionContext.</returns>
        public static async Task <CosmosQueryExecutionContext> CreateAsync(
            CosmosQueryContext queryContext,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            string requestContinuationToken,
            CancellationToken cancellationToken)
        {
            DefaultTrace.TraceInformation(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync",
                    DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture),
                    queryContext.CorrelatedActivityId));

            QueryInfo queryInfo = initParams.PartitionedQueryExecutionInfo.QueryInfo;

            int actualPageSize  = initParams.InitialPageSize;
            int initialPageSize = initParams.InitialPageSize;

            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams parameters = initParams;
            if (queryInfo.HasGroupBy)
            {
                initialPageSize = int.MaxValue;
                initParams      = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: initParams.SqlQuerySpec,
                    collectionRid: initParams.CollectionRid,
                    partitionedQueryExecutionInfo: initParams.PartitionedQueryExecutionInfo,
                    partitionKeyRanges: initParams.PartitionKeyRanges,
                    initialPageSize: initialPageSize,
                    maxConcurrency: initParams.MaxConcurrency,
                    maxItemCount: int.MaxValue,
                    maxBufferedItemCount: initParams.MaxBufferedItemCount);
            }

            Func <string, Task <IDocumentQueryExecutionComponent> > createOrderByComponentFunc = async(continuationToken) =>
            {
                return(await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            Func <string, Task <IDocumentQueryExecutionComponent> > createParallelComponentFunc = async(continuationToken) =>
            {
                return(await CosmosParallelItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            return((CosmosQueryExecutionContext)await PipelinedDocumentQueryExecutionContext.CreateHelperAsync(
                       queryContext.QueryClient,
                       initParams.PartitionedQueryExecutionInfo.QueryInfo,
                       initialPageSize,
                       requestContinuationToken,
                       createOrderByComponentFunc,
                       createParallelComponentFunc));
        }
        /// <summary>
        /// Creates a CosmosPipelinedItemQueryExecutionContext.
        /// </summary>
        /// <param name="executionEnvironment">The environment to execute on.</param>
        /// <param name="queryContext">The parameters for constructing the base class.</param>
        /// <param name="initParams">The initial parameters</param>
        /// <param name="requestContinuationToken">The request continuation.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task to await on, which in turn returns a CosmosPipelinedItemQueryExecutionContext.</returns>
        public static async Task <CosmosQueryExecutionContext> CreateAsync(
            ExecutionEnvironment executionEnvironment,
            CosmosQueryContext queryContext,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            string requestContinuationToken,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            QueryInfo queryInfo = initParams.PartitionedQueryExecutionInfo.QueryInfo;

            int initialPageSize = initParams.InitialPageSize;

            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams parameters = initParams;
            if (queryInfo.HasGroupBy)
            {
                // The query will block until all groupings are gathered so we might as well speed up the process.
                initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: initParams.SqlQuerySpec,
                    collectionRid: initParams.CollectionRid,
                    partitionedQueryExecutionInfo: initParams.PartitionedQueryExecutionInfo,
                    partitionKeyRanges: initParams.PartitionKeyRanges,
                    initialPageSize: int.MaxValue,
                    maxConcurrency: initParams.MaxConcurrency,
                    maxItemCount: int.MaxValue,
                    maxBufferedItemCount: initParams.MaxBufferedItemCount);
            }

            Func <string, Task <IDocumentQueryExecutionComponent> > createOrderByComponentFunc = async(continuationToken) =>
            {
                return(await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            Func <string, Task <IDocumentQueryExecutionComponent> > createParallelComponentFunc = async(continuationToken) =>
            {
                return(await CosmosParallelItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            return((CosmosQueryExecutionContext)await PipelinedDocumentQueryExecutionContext.CreateHelperAsync(
                       executionEnvironment,
                       queryContext.QueryClient,
                       initParams.PartitionedQueryExecutionInfo.QueryInfo,
                       initialPageSize,
                       requestContinuationToken,
                       createOrderByComponentFunc,
                       createParallelComponentFunc));
        }
        /// <summary>
        /// Creates a CosmosPipelinedItemQueryExecutionContext.
        /// </summary>
        /// <param name="constructorParams">The parameters for constructing the base class.</param>
        /// <param name="collectionRid">The collection rid.</param>
        /// <param name="partitionedQueryExecutionInfo">The partitioned query execution info.</param>
        /// <param name="partitionKeyRanges">The partition key ranges.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="requestContinuation">The request continuation.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task to await on, which in turn returns a CosmosPipelinedItemQueryExecutionContext.</returns>
        public static async Task <CosmosQueryExecutionContext> CreateAsync(
            CosmosQueryContext constructorParams,
            string collectionRid,
            PartitionedQueryExecutionInfo partitionedQueryExecutionInfo,
            List <PartitionKeyRange> partitionKeyRanges,
            int initialPageSize,
            string requestContinuation,
            CancellationToken cancellationToken)
        {
            DefaultTrace.TraceInformation(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync",
                    DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture),
                    constructorParams.CorrelatedActivityId));

            Func <string, Task <IDocumentQueryExecutionComponent> > createOrderByComponentFunc = async(continuationToken) =>
            {
                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    collectionRid,
                    partitionedQueryExecutionInfo,
                    partitionKeyRanges,
                    initialPageSize,
                    continuationToken);

                return(await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                           constructorParams,
                           initParams,
                           cancellationToken));
            };

            Func <string, Task <IDocumentQueryExecutionComponent> > createParallelComponentFunc = async(continuationToken) =>
            {
                CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    collectionRid,
                    partitionedQueryExecutionInfo,
                    partitionKeyRanges,
                    initialPageSize,
                    continuationToken);

                return(await CosmosParallelItemQueryExecutionContext.CreateAsync(
                           constructorParams,
                           initParams,
                           cancellationToken));
            };

            return((CosmosQueryExecutionContext)(await PipelinedDocumentQueryExecutionContext.CreateHelperAsync(
                                                     partitionedQueryExecutionInfo.QueryInfo,
                                                     initialPageSize,
                                                     requestContinuation,
                                                     createOrderByComponentFunc,
                                                     createParallelComponentFunc)));
        }
        /// <summary>
        /// Gets the formatted filters for every partition.
        /// </summary>
        /// <param name="expressions">The filter expressions.</param>
        /// <param name="continuationTokens">The continuation token.</param>
        /// <param name="sortOrders">The sort orders.</param>
        /// <returns>The formatted filters for every partition.</returns>
        private static FormattedFilterInfo GetFormattedFilters(
            string[] expressions,
            OrderByContinuationToken[] continuationTokens,
            SortOrder[] sortOrders)
        {
            // Validate the inputs
            for (int index = 0; index < continuationTokens.Length; index++)
            {
                Debug.Assert(continuationTokens[index].OrderByItems.Count == sortOrders.Length, "Expect values and orders are the same size.");
                Debug.Assert(expressions.Length == sortOrders.Length, "Expect expressions and orders are the same size.");
            }

            Tuple <string, string, string> filters = CosmosOrderByItemQueryExecutionContext.GetFormattedFilters(
                expressions,
                continuationTokens[0].OrderByItems.Select(orderByItem => orderByItem.Item).ToArray(),
                sortOrders);

            return(new FormattedFilterInfo(filters.Item1, filters.Item2, filters.Item3));
        }
        /// <summary>
        /// Gets the filters for every partition.
        /// </summary>
        private static TryCatch <OrderByInitInfo> TryGetOrderByPartitionKeyRangesInitializationInfo(
            OrderByContinuationToken[] suppliedContinuationTokens,
            List <PartitionKeyRange> partitionKeyRanges,
            SortOrder[] sortOrders,
            string[] orderByExpressions)
        {
            TryCatch <InitInfo <OrderByContinuationToken> > tryFindRangeAndContinuationTokensMonad = CosmosCrossPartitionQueryExecutionContext.TryFindTargetRangeAndExtractContinuationTokens(
                partitionKeyRanges,
                suppliedContinuationTokens
                .Select(token => Tuple.Create(token, token.CompositeContinuationToken.Range)));

            return(tryFindRangeAndContinuationTokensMonad.Try <OrderByInitInfo>((indexAndContinuationTokens) =>
            {
                int minIndex = indexAndContinuationTokens.TargetIndex;
                IReadOnlyDictionary <string, OrderByContinuationToken> partitionKeyRangeToContinuationToken = indexAndContinuationTokens.ContinuationTokens;

                FormattedFilterInfo formattedFilterInfo = CosmosOrderByItemQueryExecutionContext.GetFormattedFilters(
                    orderByExpressions,
                    suppliedContinuationTokens,
                    sortOrders);

                RangeFilterInitializationInfo[] filters = new RangeFilterInitializationInfo[]
                {
                    new RangeFilterInitializationInfo(
                        filter: formattedFilterInfo.FilterForRangesLeftOfTargetRanges,
                        startIndex: 0,
                        endIndex: minIndex - 1),
                    new RangeFilterInitializationInfo(
                        filter: formattedFilterInfo.FiltersForTargetRange,
                        startIndex: minIndex,
                        endIndex: minIndex),
                    new RangeFilterInitializationInfo(
                        filter: formattedFilterInfo.FilterForRangesRightOfTargetRanges,
                        startIndex: minIndex + 1,
                        endIndex: partitionKeyRanges.Count - 1),
                };

                return new OrderByInitInfo(
                    filters,
                    partitionKeyRangeToContinuationToken);
            }));
        }
Exemple #9
0
        /// <summary>
        /// Creates a CosmosPipelinedItemQueryExecutionContext.
        /// </summary>
        /// <param name="executionEnvironment">The environment to execute on.</param>
        /// <param name="queryContext">The parameters for constructing the base class.</param>
        /// <param name="initParams">The initial parameters</param>
        /// <param name="requestContinuationToken">The request continuation.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task to await on, which in turn returns a CosmosPipelinedItemQueryExecutionContext.</returns>
        public static async Task <CosmosQueryExecutionContext> CreateAsync(
            ExecutionEnvironment executionEnvironment,
            CosmosQueryContext queryContext,
            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams,
            string requestContinuationToken,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (requestContinuationToken != null)
            {
                if (!PipelineContinuationToken.TryParse(
                        requestContinuationToken,
                        out PipelineContinuationToken pipelineContinuationToken))
                {
                    throw queryContext.QueryClient.CreateBadRequestException(
                              $"Malformed {nameof(PipelineContinuationToken)}: {requestContinuationToken}.");
                }

                if (PipelineContinuationToken.IsTokenFromTheFuture(pipelineContinuationToken))
                {
                    throw queryContext.QueryClient.CreateBadRequestException(
                              $"{nameof(PipelineContinuationToken)} Continuation token is from a newer version of the SDK. Upgrade the SDK to avoid this issue.\n {requestContinuationToken}.");
                }

                if (!PipelineContinuationToken.TryConvertToLatest(
                        pipelineContinuationToken,
                        out PipelineContinuationTokenV1_1 latestVersionPipelineContinuationToken))
                {
                    throw queryContext.QueryClient.CreateBadRequestException(
                              $"{nameof(PipelineContinuationToken)}: '{requestContinuationToken}' is no longer supported.");
                }

                requestContinuationToken = latestVersionPipelineContinuationToken.SourceContinuationToken;
            }

            QueryInfo queryInfo = initParams.PartitionedQueryExecutionInfo.QueryInfo;

            int initialPageSize = initParams.InitialPageSize;

            CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams parameters = initParams;
            if (queryInfo.HasGroupBy)
            {
                // The query will block until all groupings are gathered so we might as well speed up the process.
                initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                    sqlQuerySpec: initParams.SqlQuerySpec,
                    collectionRid: initParams.CollectionRid,
                    partitionedQueryExecutionInfo: initParams.PartitionedQueryExecutionInfo,
                    partitionKeyRanges: initParams.PartitionKeyRanges,
                    initialPageSize: int.MaxValue,
                    maxConcurrency: initParams.MaxConcurrency,
                    maxItemCount: int.MaxValue,
                    maxBufferedItemCount: initParams.MaxBufferedItemCount);
            }

            Func <string, Task <IDocumentQueryExecutionComponent> > createOrderByComponentFunc = async(continuationToken) =>
            {
                return(await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            Func <string, Task <IDocumentQueryExecutionComponent> > createParallelComponentFunc = async(continuationToken) =>
            {
                return(await CosmosParallelItemQueryExecutionContext.CreateAsync(
                           queryContext,
                           initParams,
                           continuationToken,
                           cancellationToken));
            };

            return((CosmosQueryExecutionContext)await PipelinedDocumentQueryExecutionContext.CreateHelperAsync(
                       executionEnvironment,
                       queryContext.QueryClient,
                       initParams.PartitionedQueryExecutionInfo.QueryInfo,
                       initialPageSize,
                       requestContinuationToken,
                       createOrderByComponentFunc,
                       createParallelComponentFunc));
        }
Exemple #10
0
        /// <summary>
        /// Creates a CosmosPipelinedItemQueryExecutionContext.
        /// </summary>
        /// <param name="constructorParams">The parameters for constructing the base class.</param>
        /// <param name="collectionRid">The collection rid.</param>
        /// <param name="partitionedQueryExecutionInfo">The partitioned query execution info.</param>
        /// <param name="partitionKeyRanges">The partition key ranges.</param>
        /// <param name="initialPageSize">The initial page size.</param>
        /// <param name="requestContinuation">The request continuation.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task to await on, which in turn returns a CosmosPipelinedItemQueryExecutionContext.</returns>
        public static async Task <IDocumentQueryExecutionContext> CreateAsync(
            CosmosQueryContext constructorParams,
            string collectionRid,
            PartitionedQueryExecutionInfo partitionedQueryExecutionInfo,
            List <PartitionKeyRange> partitionKeyRanges,
            int initialPageSize,
            string requestContinuation,
            CancellationToken cancellationToken)
        {
            DefaultTrace.TraceInformation(
                string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}, CorrelatedActivityId: {1} | Pipelined~Context.CreateAsync",
                    DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture),
                    constructorParams.CorrelatedActivityId));

            Func <string, Task <IDocumentQueryExecutionComponent> > createComponentFunc;
            QueryInfo queryInfo = partitionedQueryExecutionInfo.QueryInfo;

            if (queryInfo.HasOrderBy)
            {
                createComponentFunc = async(continuationToken) =>
                {
                    CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                        collectionRid,
                        partitionedQueryExecutionInfo,
                        partitionKeyRanges,
                        initialPageSize,
                        continuationToken);

                    return(await CosmosOrderByItemQueryExecutionContext.CreateAsync(
                               constructorParams,
                               initParams,
                               cancellationToken));
                };
            }
            else
            {
                createComponentFunc = async(continuationToken) =>
                {
                    CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams initParams = new CosmosCrossPartitionQueryExecutionContext.CrossPartitionInitParams(
                        collectionRid,
                        partitionedQueryExecutionInfo,
                        partitionKeyRanges,
                        initialPageSize,
                        continuationToken);

                    return(await CosmosParallelItemQueryExecutionContext.CreateAsync(
                               constructorParams,
                               initParams,
                               cancellationToken));
                };
            }

            if (queryInfo.HasAggregates)
            {
                Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc;
                createComponentFunc = async(continuationToken) =>
                {
                    return(await AggregateDocumentQueryExecutionComponent.CreateAsync(
                               queryInfo.Aggregates,
                               continuationToken,
                               createSourceCallback));
                };
            }

            if (queryInfo.HasDistinct)
            {
                Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc;
                createComponentFunc = async(continuationToken) =>
                {
                    return(await DistinctDocumentQueryExecutionComponent.CreateAsync(
                               continuationToken,
                               createSourceCallback,
                               queryInfo.DistinctType));
                };
            }

            if (queryInfo.HasOffset)
            {
                if (!constructorParams.QueryRequestOptions.EnableCrossPartitionSkipTake)
                {
                    throw new ArgumentException("Cross Partition OFFSET / LIMIT is not supported.");
                }

                Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc;
                createComponentFunc = async(continuationToken) =>
                {
                    return(await SkipDocumentQueryExecutionComponent.CreateAsync(
                               queryInfo.Offset.Value,
                               continuationToken,
                               createSourceCallback));
                };
            }

            if (queryInfo.HasLimit)
            {
                if (!constructorParams.QueryRequestOptions.EnableCrossPartitionSkipTake)
                {
                    throw new ArgumentException("Cross Partition OFFSET / LIMIT is not supported.");
                }

                Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc;
                createComponentFunc = async(continuationToken) =>
                {
                    return(await TakeDocumentQueryExecutionComponent.CreateLimitDocumentQueryExecutionComponentAsync(
                               queryInfo.Limit.Value,
                               continuationToken,
                               createSourceCallback));
                };
            }

            if (queryInfo.HasTop)
            {
                Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback = createComponentFunc;
                createComponentFunc = async(continuationToken) =>
                {
                    return(await TakeDocumentQueryExecutionComponent.CreateTopDocumentQueryExecutionComponentAsync(
                               queryInfo.Top.Value,
                               continuationToken,
                               createSourceCallback));
                };
            }

            return(new CosmosPipelinedItemQueryExecutionContext(
                       await createComponentFunc(requestContinuation), initialPageSize));
        }
        private static Tuple <string, string, string> GetFormattedFilters(
            string[] expressions,
            CosmosElement[] orderByItems,
            SortOrder[] sortOrders)
        {
            // When we run cross partition queries,
            // we only serialize the continuation token for the partition that we left off on.
            // The only problem is that when we resume the order by query,
            // we don't have continuation tokens for all other partition.
            // The saving grace is that the data has a composite sort order(query sort order, partition key range id)
            // so we can generate range filters which in turn the backend will turn into rid based continuation tokens,
            // which is enough to get the streams of data flowing from all partitions.
            // The details of how this is done is described below:
            int           numOrderByItems = expressions.Length;
            bool          isSingleOrderBy = numOrderByItems == 1;
            StringBuilder left            = new StringBuilder();
            StringBuilder target          = new StringBuilder();
            StringBuilder right           = new StringBuilder();

            Tuple <StringBuilder, StringBuilder, StringBuilder> builders = new Tuple <StringBuilder, StringBuilder, StringBuilder>(left, right, target);

            if (isSingleOrderBy)
            {
                //For a single order by query we resume the continuations in this manner
                //    Suppose the query is SELECT* FROM c ORDER BY c.string ASC
                //        And we left off on partition N with the value "B"
                //        Then
                //            All the partitions to the left will have finished reading "B"
                //            Partition N is still reading "B"
                //            All the partitions to the right have let to read a "B
                //        Therefore the filters should be
                //            > "B" , >= "B", and >= "B" respectively
                //    Repeat the same logic for DESC and you will get
                //            < "B", <= "B", and <= "B" respectively
                //    The general rule becomes
                //        For ASC
                //            > for partitions to the left
                //            >= for the partition we left off on
                //            >= for the partitions to the right
                //        For DESC
                //            < for partitions to the left
                //            <= for the partition we left off on
                //            <= for the partitions to the right
                string        expression          = expressions.First();
                SortOrder     sortOrder           = sortOrders.First();
                CosmosElement orderByItem         = orderByItems.First();
                string        orderByItemToString = JsonConvert.SerializeObject(orderByItem, DefaultJsonSerializationSettings.Value);
                left.Append($"{expression} {(sortOrder == SortOrder.Descending ? "<" : ">")} {orderByItemToString}");
                target.Append($"{expression} {(sortOrder == SortOrder.Descending ? "<=" : ">=")} {orderByItemToString}");
                right.Append($"{expression} {(sortOrder == SortOrder.Descending ? "<=" : ">=")} {orderByItemToString}");
            }
            else
            {
                //For a multi order by query
                //    Suppose the query is SELECT* FROM c ORDER BY c.string ASC, c.number ASC
                //        And we left off on partition N with the value("A", 1)
                //        Then
                //            All the partitions to the left will have finished reading("A", 1)
                //            Partition N is still reading("A", 1)
                //            All the partitions to the right have let to read a "(A", 1)
                //        The filters are harder to derive since their are multiple columns
                //        But the problem reduces to "How do you know one document comes after another in a multi order by query"
                //        The answer is to just look at it one column at a time.
                //        For this particular scenario:
                //        If a first column is greater ex. ("B", blah), then the document comes later in the sort order
                //            Therefore we want all documents where the first column is greater than "A" which means > "A"
                //        Or if the first column is a tie, then you look at the second column ex. ("A", blah).
                //            Therefore we also want all documents where the first column was a tie but the second column is greater which means = "A" AND > 1
                //        Therefore the filters should be
                //            (> "A") OR (= "A" AND > 1), (> "A") OR (= "A" AND >= 1), (> "A") OR (= "A" AND >= 1)
                //            Notice that if we repeated the same logic we for single order by we would have gotten
                //            > "A" AND > 1, >= "A" AND >= 1, >= "A" AND >= 1
                //            which is wrong since we missed some documents
                //    Repeat the same logic for ASC, DESC
                //            (> "A") OR (= "A" AND < 1), (> "A") OR (= "A" AND <= 1), (> "A") OR (= "A" AND <= 1)
                //        Again for DESC, ASC
                //            (< "A") OR (= "A" AND > 1), (< "A") OR (= "A" AND >= 1), (< "A") OR (= "A" AND >= 1)
                //        And again for DESC DESC
                //            (< "A") OR (= "A" AND < 1), (< "A") OR (= "A" AND <= 1), (< "A") OR (= "A" AND <= 1)
                //    The general we look at all prefixes of the order by columns to look for tie breakers.
                //        Except for the full prefix whose last column follows the rules for single item order by
                //        And then you just OR all the possibilities together
                for (int prefixLength = 1; prefixLength <= numOrderByItems; prefixLength++)
                {
                    ArraySegment <string>        expressionPrefix   = new ArraySegment <string>(expressions, 0, prefixLength);
                    ArraySegment <SortOrder>     sortOrderPrefix    = new ArraySegment <SortOrder>(sortOrders, 0, prefixLength);
                    ArraySegment <CosmosElement> orderByItemsPrefix = new ArraySegment <CosmosElement>(orderByItems, 0, prefixLength);

                    bool lastPrefix  = prefixLength == numOrderByItems;
                    bool firstPrefix = prefixLength == 1;

                    CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, "(");

                    for (int index = 0; index < prefixLength; index++)
                    {
                        string        expression  = expressionPrefix.ElementAt(index);
                        SortOrder     sortOrder   = sortOrderPrefix.ElementAt(index);
                        CosmosElement orderByItem = orderByItemsPrefix.ElementAt(index);
                        bool          lastItem    = index == prefixLength - 1;

                        // Append Expression
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, expression);
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, " ");

                        // Append binary operator
                        if (lastItem)
                        {
                            string inequality = sortOrder == SortOrder.Descending ? "<" : ">";
                            CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, inequality);
                            if (lastPrefix)
                            {
                                CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, string.Empty, "=", "=");
                            }
                        }
                        else
                        {
                            CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, "=");
                        }

                        // Append SortOrder
                        string orderByItemToString = JsonConvert.SerializeObject(orderByItem, DefaultJsonSerializationSettings.Value);
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, " ");
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, orderByItemToString);
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, " ");

                        if (!lastItem)
                        {
                            CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, "AND ");
                        }
                    }

                    CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, ")");
                    if (!lastPrefix)
                    {
                        CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, " OR ");
                    }
                }
            }

            return(new Tuple <string, string, string>(left.ToString(), target.ToString(), right.ToString()));
        }
 private static void AppendToBuilders(Tuple <StringBuilder, StringBuilder, StringBuilder> builders, object str)
 {
     CosmosOrderByItemQueryExecutionContext.AppendToBuilders(builders, str, str, str);
 }
        private async Task <TryCatch <bool> > TryInitializeAsync(
            SqlQuerySpec sqlQuerySpec,
            string requestContinuation,
            string collectionRid,
            List <PartitionKeyRange> partitionKeyRanges,
            int initialPageSize,
            SortOrder[] sortOrders,
            string[] orderByExpressions,
            CancellationToken cancellationToken)
        {
            if (sqlQuerySpec == null)
            {
                throw new ArgumentNullException(nameof(sqlQuerySpec));
            }

            if (collectionRid == null)
            {
                throw new ArgumentNullException(nameof(collectionRid));
            }

            if (partitionKeyRanges == null)
            {
                throw new ArgumentNullException(nameof(partitionKeyRanges));
            }

            if (sortOrders == null)
            {
                throw new ArgumentNullException(nameof(sortOrders));
            }

            if (orderByExpressions == null)
            {
                throw new ArgumentNullException(nameof(orderByExpressions));
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (requestContinuation == null)
            {
                SqlQuerySpec sqlQuerySpecForInit = new SqlQuerySpec(
                    sqlQuerySpec.QueryText.Replace(oldValue: FormatPlaceHolder, newValue: True),
                    sqlQuerySpec.Parameters);

                TryCatch <bool> tryInitialize = await base.TryInitializeAsync(
                    collectionRid,
                    partitionKeyRanges,
                    initialPageSize,
                    sqlQuerySpecForInit,
                    cancellationToken : cancellationToken,
                    targetRangeToContinuationMap : null,
                    deferFirstPage : false,
                    filter : null,
                    tryFilterAsync : null);

                if (!tryInitialize.Succeeded)
                {
                    return(tryInitialize);
                }
            }
            else
            {
                TryCatch <OrderByContinuationToken[]> tryExtractContinuationTokens = CosmosOrderByItemQueryExecutionContext.TryExtractContinuationTokens(
                    requestContinuation,
                    sortOrders,
                    orderByExpressions);
                if (!tryExtractContinuationTokens.Succeeded)
                {
                    return(TryCatch <bool> .FromException(tryExtractContinuationTokens.Exception));
                }

                TryCatch <OrderByInitInfo> tryGetOrderByInitInfo = CosmosOrderByItemQueryExecutionContext.TryGetOrderByPartitionKeyRangesInitializationInfo(
                    tryExtractContinuationTokens.Result,
                    partitionKeyRanges,
                    sortOrders,
                    orderByExpressions);
                if (!tryGetOrderByInitInfo.Succeeded)
                {
                    return(TryCatch <bool> .FromException(tryGetOrderByInitInfo.Exception));
                }

                OrderByInitInfo initiaizationInfo            = tryGetOrderByInitInfo.Result;
                RangeFilterInitializationInfo[] orderByInfos = initiaizationInfo.Filters;
                IReadOnlyDictionary <string, OrderByContinuationToken> targetRangeToOrderByContinuationMap = initiaizationInfo.ContinuationTokens;
                Debug.Assert(
                    targetRangeToOrderByContinuationMap != null,
                    "If targetRangeToOrderByContinuationMap can't be null is valid continuation is supplied");

                // For ascending order-by, left of target partition has filter expression > value,
                // right of target partition has filter expression >= value,
                // and target partition takes the previous filter from continuation (or true if no continuation)
                foreach (RangeFilterInitializationInfo info in orderByInfos)
                {
                    if (info.StartIndex > info.EndIndex)
                    {
                        continue;
                    }

                    PartialReadOnlyList <PartitionKeyRange> partialRanges =
                        new PartialReadOnlyList <PartitionKeyRange>(
                            partitionKeyRanges,
                            info.StartIndex,
                            info.EndIndex - info.StartIndex + 1);

                    SqlQuerySpec sqlQuerySpecForInit = new SqlQuerySpec(
                        sqlQuerySpec.QueryText.Replace(FormatPlaceHolder, info.Filter),
                        sqlQuerySpec.Parameters);

                    await base.TryInitializeAsync(
                        collectionRid,
                        partialRanges,
                        initialPageSize,
                        sqlQuerySpecForInit,
                        targetRangeToOrderByContinuationMap.ToDictionary(
                            kvp => kvp.Key,
                            kvp => kvp.Value.CompositeContinuationToken.Token),
                        false,
                        info.Filter,
                        async (itemProducerTree) =>
                    {
                        if (targetRangeToOrderByContinuationMap.TryGetValue(
                                itemProducerTree.Root.PartitionKeyRange.Id,
                                out OrderByContinuationToken continuationToken))
                        {
                            TryCatch <bool> tryFilter = await this.TryFilterAsync(
                                itemProducerTree,
                                sortOrders,
                                continuationToken,
                                cancellationToken);

                            if (!tryFilter.Succeeded)
                            {
                                return(tryFilter);
                            }
                        }

                        return(TryCatch <bool> .FromResult(true));
                    },
                        cancellationToken);
                }
            }

            return(TryCatch <bool> .FromResult(true));
        }