/// <summary>
        /// Compares two document producer trees and returns an integer with the relation of which has the document that comes first in the sort order.
        /// </summary>
        /// <param name="producer1">The first document producer tree.</param>
        /// <param name="producer2">The second document producer tree.</param>
        /// <returns>
        /// Less than zero if the document in the first document producer comes first.
        /// Zero if the documents are equivalent.
        /// Greater than zero if the document in the second document producer comes first.
        /// </returns>
        public int Compare(ItemProducerTree producer1, ItemProducerTree producer2)
        {
            if (object.ReferenceEquals(producer1, producer2))
            {
                return(0);
            }

            if (producer1.HasMoreResults && !producer2.HasMoreResults)
            {
                return(-1);
            }

            if (!producer1.HasMoreResults && producer2.HasMoreResults)
            {
                return(1);
            }

            if (!producer1.HasMoreResults && !producer2.HasMoreResults)
            {
                return(string.CompareOrdinal(producer1.PartitionKeyRange.MinInclusive, producer2.PartitionKeyRange.MinInclusive));
            }

            OrderByQueryResult result1 = new OrderByQueryResult(producer1.Current);
            OrderByQueryResult result2 = new OrderByQueryResult(producer2.Current);

            // First compare the documents based on the sort order of the query.
            int cmp = this.CompareOrderByItems(result1.OrderByItems, result2.OrderByItems);

            if (cmp != 0)
            {
                // If there is no tie just return that.
                return(cmp);
            }

            // If there is a tie, then break the tie by picking the one from the left most partition.
            return(string.CompareOrdinal(producer1.PartitionKeyRange.MinInclusive, producer2.PartitionKeyRange.MinInclusive));
        }
        /// <summary>
        /// Drains a page of documents from this context.
        /// </summary>
        /// <param name="maxElements">The maximum number of elements.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A task that when awaited on return a page of documents.</returns>
        public override async Task <QueryResponseCore> DrainAsync(int maxElements, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            //// In order to maintain the continuation token for the user we must drain with a few constraints
            //// 1) We always drain from the partition, which has the highest priority item first
            //// 2) If multiple partitions have the same priority item then we drain from the left most first
            ////   otherwise we would need to keep track of how many of each item we drained from each partition
            ////   (just like parallel queries).
            //// Visually that look the following case where we have three partitions that are numbered and store letters.
            //// For teaching purposes I have made each item a tuple of the following form:
            ////      <item stored in partition, partition number>
            //// So that duplicates across partitions are distinct, but duplicates within partitions are indistinguishable.
            ////      |-------|   |-------|   |-------|
            ////      | <a,1> |   | <a,2> |   | <a,3> |
            ////      | <a,1> |   | <b,2> |   | <c,3> |
            ////      | <a,1> |   | <b,2> |   | <c,3> |
            ////      | <d,1> |   | <c,2> |   | <c,3> |
            ////      | <d,1> |   | <e,2> |   | <f,3> |
            ////      | <e,1> |   | <h,2> |   | <j,3> |
            ////      | <f,1> |   | <i,2> |   | <k,3> |
            ////      |-------|   |-------|   |-------|
            //// Now the correct drain order in this case is:
            ////  <a,1>,<a,1>,<a,1>,<a,2>,<a,3>,<b,2>,<b,2>,<c,2>,<c,3>,<c,3>,<c,3>,
            ////  <d,1>,<d,1>,<e,1>,<e,2>,<f,1>,<f,3>,<h,2>,<i,2>,<j,3>,<k,3>
            //// In more mathematical terms
            ////  1) <x, y> always comes before <z, y> where x < z
            ////  2) <i, j> always come before <i, k> where j < k

            List <CosmosElement> results = new List <CosmosElement>();

            while (results.Count < maxElements)
            {
                // Only drain from the highest priority document producer
                // We need to pop and push back the document producer tree, since the priority changes according to the sort order.
                ItemProducerTree currentItemProducerTree = this.PopCurrentItemProducerTree();
                try
                {
                    if (!currentItemProducerTree.HasMoreResults)
                    {
                        // This means there are no more items to drain
                        break;
                    }

                    OrderByQueryResult orderByQueryResult = new OrderByQueryResult(currentItemProducerTree.Current);

                    // Only add the payload, since other stuff is garbage from the caller's perspective.
                    results.Add(orderByQueryResult.Payload);

                    // If we are at the beginning of the page and seeing an rid from the previous page we should increment the skip count
                    // due to the fact that JOINs can make a document appear multiple times and across continuations, so we don't want to
                    // surface this more than needed. More information can be found in the continuation token docs.
                    if (this.ShouldIncrementSkipCount(currentItemProducerTree.CurrentItemProducerTree.Root))
                    {
                        ++this.skipCount;
                    }
                    else
                    {
                        this.skipCount = 0;
                    }

                    this.previousRid          = orderByQueryResult.Rid;
                    this.previousOrderByItems = orderByQueryResult.OrderByItems;

                    if (!currentItemProducerTree.TryMoveNextDocumentWithinPage())
                    {
                        while (true)
                        {
                            (bool movedToNextPage, QueryResponseCore? failureResponse) = await currentItemProducerTree.TryMoveNextPageAsync(cancellationToken);

                            if (!movedToNextPage)
                            {
                                if (failureResponse.HasValue)
                                {
                                    // TODO: We can buffer this failure so that the user can still get the pages we already got.
                                    return(failureResponse.Value);
                                }

                                break;
                            }

                            if (currentItemProducerTree.IsAtBeginningOfPage)
                            {
                                break;
                            }

                            if (currentItemProducerTree.TryMoveNextDocumentWithinPage())
                            {
                                break;
                            }
                        }
                    }
                }
                finally
                {
                    this.PushCurrentItemProducerTree(currentItemProducerTree);
                }
            }

            return(QueryResponseCore.CreateSuccess(
                       result: results,
                       requestCharge: this.requestChargeTracker.GetAndResetCharge(),
                       activityId: null,
                       responseLengthBytes: this.GetAndResetResponseLengthBytes(),
                       disallowContinuationTokenMessage: null,
                       continuationToken: this.ContinuationToken,
                       diagnostics: this.GetAndResetDiagnostics()));
        }