public static CosmosElement ToCosmosElement(OrderByContinuationToken orderByContinuationToken)
        {
            CosmosElement        compositeContinuationToken = ParallelContinuationToken.ToCosmosElement(orderByContinuationToken.ParallelContinuationToken);
            List <CosmosElement> orderByItemsRaw            = new List <CosmosElement>();

            foreach (OrderByItem orderByItem in orderByContinuationToken.OrderByItems)
            {
                orderByItemsRaw.Add(OrderByItem.ToCosmosElement(orderByItem));
            }

            CosmosArray orderByItems = CosmosArray.Create(orderByItemsRaw);

            CosmosElement filter = orderByContinuationToken.Filter == null?CosmosNull.Create() : (CosmosElement)CosmosString.Create(orderByContinuationToken.Filter);

            CosmosObject cosmosObject = CosmosObject.Create(
                new Dictionary <string, CosmosElement>()
            {
                { PropertyNames.CompositeToken, compositeContinuationToken },
                { PropertyNames.OrderByItems, orderByItems },
                { PropertyNames.Rid, CosmosString.Create(orderByContinuationToken.Rid) },
                { PropertyNames.SkipCount, CosmosNumber64.Create(orderByContinuationToken.SkipCount) },
                { PropertyNames.Filter, filter },
            });

            return(cosmosObject);
        }
        public void TestMatchRangesTocontinuationTokens_OneToNone()
        {
            PartitionKeyRange partitionKeyRange = new PartitionKeyRange()
            {
                MinInclusive = string.Empty,
                MaxExclusive = "A",
                Id           = "1"
            };

            ParallelContinuationToken token = new ParallelContinuationToken(
                token: "asdf",
                range: new Documents.Routing.Range <string>(
                    min: "B",
                    max: "C",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMapping = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { partitionKeyRange, null },
            };

            ContinuationResumeLogicTests.RunMatchRangesToContinuationTokens(
                expectedMapping,
                new PartitionKeyRange[] { partitionKeyRange },
                new ParallelContinuationToken[] { token });
        }
        public void TestMatchRangesTocontinuationTokens_OneToMany()
        {
            FeedRangeEpk partitionKeyRange1 = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: string.Empty,
                    max: "A",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            FeedRangeEpk partitionKeyRange2 = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: "A",
                    max: "B",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            ParallelContinuationToken token = new ParallelContinuationToken(
                token: "asdf",
                range: new Documents.Routing.Range <string>(
                    min: string.Empty,
                    max: "B",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMapping = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
                { partitionKeyRange1, token },
                { partitionKeyRange2, token }
            };

            ContinuationResumeLogicTests.RunMatchRangesToContinuationTokens(
                expectedMapping,
                new FeedRangeEpk[] { partitionKeyRange1, partitionKeyRange2 },
                new ParallelContinuationToken[] { token });
        }
        /// <summary>
        /// Initializes a new instance of the OrderByContinuationToken struct.
        /// </summary>
        /// <param name="compositeContinuationToken">The composite continuation token (refer to property documentation).</param>
        /// <param name="orderByItems">The order by items (refer to property documentation).</param>
        /// <param name="rid">The rid (refer to property documentation).</param>
        /// <param name="skipCount">The skip count (refer to property documentation).</param>
        /// <param name="filter">The filter (refer to property documentation).</param>
        public OrderByContinuationToken(
            ParallelContinuationToken compositeContinuationToken,
            IReadOnlyList <OrderByItem> orderByItems,
            string rid,
            int skipCount,
            string filter)
        {
            if (orderByItems.Count == 0)
            {
                throw new ArgumentException($"{nameof(orderByItems)} can not be empty.");
            }

            if (string.IsNullOrWhiteSpace(rid))
            {
                throw new ArgumentNullException($"{nameof(rid)} can not be null or empty or whitespace.");
            }

            if (skipCount < 0)
            {
                throw new ArgumentException($"{nameof(skipCount)} can not be negative.");
            }

            //// filter is allowed to be null.
            this.ParallelContinuationToken = compositeContinuationToken ?? throw new ArgumentNullException(nameof(compositeContinuationToken));
            this.OrderByItems = orderByItems ?? throw new ArgumentNullException(nameof(orderByItems));
            this.Rid          = rid;
            this.SkipCount    = skipCount;
            this.Filter       = filter;
        }
        public void TestTryGetInitializationInfo_ResumeRightPartition()
        {
            PartitionKeyRange pkRange1 = new PartitionKeyRange()
            {
                MinInclusive = string.Empty,
                MaxExclusive = "A",
                Id           = "1"
            };

            PartitionKeyRange pkRange2 = new PartitionKeyRange()
            {
                MinInclusive = "A",
                MaxExclusive = "B",
                Id           = "2"
            };

            PartitionKeyRange pkRange3 = new PartitionKeyRange()
            {
                MinInclusive = "B",
                MaxExclusive = "C",
                Id           = "3"
            };

            ParallelContinuationToken token = new ParallelContinuationToken(
                token: "asdf",
                range: new Documents.Routing.Range <string>(
                    min: "B",
                    max: "C",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { pkRange1, null },
                { pkRange2, null },
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
                { pkRange3, token },
            };

            IReadOnlyDictionary <PartitionKeyRange, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <PartitionKeyRange, IPartitionedToken>()
            {
            };

            RunTryGetInitializationInfo(
                expectedMappingLeftPartitions,
                expectedMappingTargetPartition,
                expectedMappingRightPartitions,
                new PartitionKeyRange[] { pkRange1, pkRange2, pkRange3 },
                new IPartitionedToken[] { token });
        }
        public void TestTryGetInitializationInfo_ResumeMiddlePartition()
        {
            FeedRangeEpk pkRange1 = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: string.Empty,
                    max: "A",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            FeedRangeEpk pkRange2 = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: "A",
                    max: "B",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            FeedRangeEpk pkRange3 = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: "B",
                    max: "C",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            ParallelContinuationToken token = new ParallelContinuationToken(
                token: "asdf",
                range: new Documents.Routing.Range <string>(
                    min: "A",
                    max: "B",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
                { pkRange1, null }
            };

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
                { pkRange2, token },
            };

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
                { pkRange3, null },
            };

            RunTryGetInitializationInfo(
                expectedMappingLeftPartitions,
                expectedMappingTargetPartition,
                expectedMappingRightPartitions,
                new FeedRangeEpk[] { pkRange1, pkRange2, pkRange3 },
                new IPartitionedToken[] { token });
        }
Example #7
0
        public void ResumeOnMultipleTokens()
        {
            FeedRangeEpk range = Range(min: "A", max: "F");
            ParallelContinuationToken token1 = Token(min: "A", max: "C");
            ParallelContinuationToken token2 = Token(min: "C", max: "E");

            RunTryGetInitializationInfo(
                Mapping(),
                Mapping((Range(min: "A", max: "C"), token1)),
                Mapping((Range(min: "C", max: "E"), token2), (Range(min: "E", max: "F"), null)),
                new FeedRangeEpk[] { range, },
                new IPartitionedToken[] { token1, token2 });
        }
Example #8
0
        public void ResumeRightPartition()
        {
            FeedRangeEpk range1             = Range(min: string.Empty, max: "A");
            FeedRangeEpk range2             = Range(min: "A", max: "B");
            FeedRangeEpk range3             = Range(min: "B", max: "C");
            ParallelContinuationToken token = Token(min: "B", max: "C");

            RunTryGetInitializationInfo(
                Mapping((CombineRanges(range1, range2), null)),
                Mapping((range3, token)),
                Mapping(),
                new FeedRangeEpk[] { range1, range2, range3 },
                new IPartitionedToken[] { token });
        }
        public void TestRoundTripAsCosmosElement()
        {
            ParallelContinuationToken parallelContinuationToken = new ParallelContinuationToken(
                token: "someToken",
                range: new Range <string>("asdf", "asdf", false, false));

            List <OrderByItem> orderByItems = new List <OrderByItem>()
            {
                new OrderByItem(CosmosObject.Create(new Dictionary <string, CosmosElement>()
                {
                    { "item", CosmosString.Create("asdf") }
                })),
                new OrderByItem(CosmosObject.Create(new Dictionary <string, CosmosElement>()
                {
                    { "item", CosmosInt64.Create(1337) }
                })),
                new OrderByItem(CosmosObject.Create(new Dictionary <string, CosmosElement>()
                {
                    { "item", CosmosBinary.Create(new byte[] { 1, 2, 3 }) }
                })),
            };

            string rid       = "someRid";
            int    skipCount = 42;
            string filter    = "someFilter";
            OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken(
                parallelContinuationToken,
                orderByItems,
                rid,
                skipCount,
                filter);

            CosmosElement cosmosElementToken = OrderByContinuationToken.ToCosmosElement(orderByContinuationToken);

            Assert.AreEqual(
                @"{""compositeToken"":{""token"":""someToken"",""range"":{""min"":""asdf"",""max"":""asdf""}},""orderByItems"":[{""item"":""asdf""},{""item"":LL1337},{""item"":BAQID}],""rid"":""someRid"",""skipCount"":42,""filter"":""someFilter""}",
                cosmosElementToken.ToString());
            TryCatch <OrderByContinuationToken> tryOrderByContinuationTokenFromCosmosElement = OrderByContinuationToken.TryCreateFromCosmosElement(cosmosElementToken);

            Assert.IsTrue(tryOrderByContinuationTokenFromCosmosElement.Succeeded);
            OrderByContinuationToken orderByContinuationTokenFromCosmosElement = tryOrderByContinuationTokenFromCosmosElement.Result;

            Assert.IsNotNull(orderByContinuationTokenFromCosmosElement);
            Assert.AreEqual(cosmosElementToken.ToString(), OrderByContinuationToken.ToCosmosElement(orderByContinuationTokenFromCosmosElement).ToString());
        }
Example #10
0
        public void TestRoundTripAsCosmosElement(string token)
        {
            ParallelContinuationToken compositeContinuationToken = new ParallelContinuationToken(
                token,
                new Documents.Routing.Range <string>("asdf", "asdf", true, false));

            CosmosElement cosmosElementToken = ParallelContinuationToken.ToCosmosElement(compositeContinuationToken);
            TryCatch <ParallelContinuationToken> tryCompositeContinuationTokenFromCosmosElement = ParallelContinuationToken.TryCreateFromCosmosElement(cosmosElementToken);

            Assert.IsTrue(tryCompositeContinuationTokenFromCosmosElement.Succeeded);
            ParallelContinuationToken compositeContinuationTokenFromCosmosElement = tryCompositeContinuationTokenFromCosmosElement.Result;

            Assert.AreEqual(compositeContinuationToken.Token, compositeContinuationTokenFromCosmosElement.Token);
            Assert.AreEqual(compositeContinuationToken.Range.Min, compositeContinuationTokenFromCosmosElement.Range.Min);
            Assert.AreEqual(compositeContinuationToken.Range.Max, compositeContinuationTokenFromCosmosElement.Range.Max);
            Assert.AreEqual(compositeContinuationToken.Range.IsMinInclusive, compositeContinuationTokenFromCosmosElement.Range.IsMinInclusive);
            Assert.AreEqual(compositeContinuationToken.Range.IsMaxInclusive, compositeContinuationTokenFromCosmosElement.Range.IsMaxInclusive);
        }
Example #11
0
        public void ResumeOnSplit_LogicalParition()
        {
            // Suppose the partition spans epk range A to E
            // And the user send a query with partition key that hashes to C
            // The the token will look like:
            ParallelContinuationToken token = Token(min: "A", "E");

            // Now suppose there is a split that creates two partitions A to B and B to E
            // Now C will map to the partition that goes from B to E
            FeedRangeEpk range = Range(min: "B", max: "E");

            RunTryGetInitializationInfo(
                Mapping(),
                Mapping((range, token)),
                Mapping(),
                new FeedRangeEpk[] { range },
                new IPartitionedToken[] { token });
        }
        public void TestTryGetInitializationInfo_ResumeLogicalParition()
        {
            // Suppose the partition spans epk range A to E
            // And the user send a query with partition key that hashes to C
            // The the token will look like:
            ParallelContinuationToken token = new ParallelContinuationToken(
                token: "asdf",
                range: new Documents.Routing.Range <string>(
                    min: "A",
                    max: "E",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            // Now suppose there is a split that creates two partitions A to B and B to E
            // Now C will map to the partition that goes from B to E
            FeedRangeEpk pkRange = new FeedRangeEpk(
                new Documents.Routing.Range <string>(
                    min: "B",
                    max: "E",
                    isMinInclusive: true,
                    isMaxInclusive: false));

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingLeftPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
            };

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingTargetPartition = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
                { pkRange, token },
            };

            IReadOnlyDictionary <FeedRangeEpk, IPartitionedToken> expectedMappingRightPartitions = new Dictionary <FeedRangeEpk, IPartitionedToken>()
            {
            };

            RunTryGetInitializationInfo(
                expectedMappingLeftPartitions,
                expectedMappingTargetPartition,
                expectedMappingRightPartitions,
                new FeedRangeEpk[] { pkRange },
                new IPartitionedToken[] { token });
        }
Example #13
0
        public void ResumeOnAMerge()
        {
            // Suppose that we read from range 1
            FeedRangeEpk range1 = Range(min: string.Empty, max: "A");

            // Then Range 1 Merged with Range 2
            FeedRangeEpk range2 = Range(min: "A", max: "B");

            // And we have a continuation token for range 1
            ParallelContinuationToken token = Token(min: string.Empty, max: "A");

            // Then we should resume on range 1 with epk range filtering
            // and still have range 2 with null continuation.
            RunTryGetInitializationInfo(
                Mapping(),
                Mapping((range1, token)),
                Mapping((range2, null)),
                new FeedRangeEpk[] { CombineRanges(range1, range2) },
                new IPartitionedToken[] { token });
        }
Example #14
0
        public void ResumeOnAMerge_LogicalPartition()
        {
            // Suppose that we read from range 2 with a logical partiton key that hashes to D
            FeedRangeEpk range2 = Range(min: "C", max: "E");

            // Then Range 1
            FeedRangeEpk range1 = Range(min: "A", max: "C");

            // and Range 3 merge with range 2
            FeedRangeEpk range3 = Range(min: "E", max: "G");

            // And we have a continuation token for range 2
            ParallelContinuationToken token = Token(min: "C", max: "E");

            // Then we should resume on range 2 with epk range filtering
            // and still have range 1 and 3 with null continuation (but, since there is a logical partition key it won't match any results).
            RunTryGetInitializationInfo(
                Mapping((range1, null)),
                Mapping((range2, token)),
                Mapping((range3, null)),
                new FeedRangeEpk[] { CombineRanges(CombineRanges(range1, range2), range3) },
                new IPartitionedToken[] { token });
        }
        public static TryCatch <OrderByContinuationToken> TryCreateFromCosmosElement(CosmosElement cosmosElement)
        {
            if (!(cosmosElement is CosmosObject cosmosObject))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is not an object: {cosmosElement}")));
            }

            if (!cosmosObject.TryGetValue(PropertyNames.CompositeToken, out CosmosElement compositeContinuationTokenElement))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is missing field: '{PropertyNames.CompositeToken}': {cosmosElement}")));
            }

            TryCatch <ParallelContinuationToken> tryCompositeContinuation = ParallelContinuationToken.TryCreateFromCosmosElement(compositeContinuationTokenElement);

            if (!tryCompositeContinuation.Succeeded)
            {
                return(TryCatch <OrderByContinuationToken> .FromException(tryCompositeContinuation.Exception));
            }

            ParallelContinuationToken compositeContinuationToken = tryCompositeContinuation.Result;

            if (!cosmosObject.TryGetValue(PropertyNames.OrderByItems, out CosmosArray orderByItemsRaw))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is missing field: '{PropertyNames.OrderByItems}': {cosmosElement}")));
            }

            List <OrderByItem> orderByItems = orderByItemsRaw.Select(x => OrderByItem.FromCosmosElement(x)).ToList();

            if (!cosmosObject.TryGetValue(PropertyNames.Rid, out CosmosString ridRaw))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is missing field: '{PropertyNames.Rid}': {cosmosElement}")));
            }

            string rid = ridRaw.Value;

            if (!cosmosObject.TryGetValue(PropertyNames.SkipCount, out CosmosNumber64 skipCountRaw))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is missing field: '{PropertyNames.SkipCount}': {cosmosElement}")));
            }

            int skipCount = (int)Number64.ToLong(skipCountRaw.GetValue());

            if (!cosmosObject.TryGetValue(PropertyNames.Filter, out CosmosElement filterRaw))
            {
                return(TryCatch <OrderByContinuationToken> .FromException(
                           new MalformedContinuationTokenException($"{nameof(OrderByContinuationToken)} is missing field: '{PropertyNames.Filter}': {cosmosElement}")));
            }

            string filter;

            if (filterRaw is CosmosString filterStringRaw)
            {
                filter = filterStringRaw.Value;
            }
            else
            {
                filter = null;
            }

            OrderByContinuationToken orderByContinuationToken = new OrderByContinuationToken(
                compositeContinuationToken,
                orderByItems,
                rid,
                skipCount,
                filter);

            return(TryCatch <OrderByContinuationToken> .FromResult(orderByContinuationToken));
        }