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 }); }
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 }); }
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()); }
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); }
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 }); }
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 }); }
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)); }