/// <summary> /// Tries to parse a DistinctContinuationToken from a string. /// </summary> /// <param name="cosmosElement">The value to parse.</param> /// <param name="distinctContinuationToken">The output DistinctContinuationToken.</param> /// <returns>True if we successfully parsed the DistinctContinuationToken, else false.</returns> public static bool TryParse( CosmosElement cosmosElement, out DistinctContinuationToken distinctContinuationToken) { if (!(cosmosElement is CosmosObject cosmosObject)) { distinctContinuationToken = default; return(false); } if (!cosmosObject.TryGetValue( DistinctContinuationToken.PropertyNames.SourceToken, out CosmosString sourceToken)) { distinctContinuationToken = default; return(false); } if (!cosmosObject.TryGetValue( DistinctContinuationToken.PropertyNames.DistinctMapToken, out CosmosString distinctMapToken)) { distinctContinuationToken = default; return(false); } distinctContinuationToken = new DistinctContinuationToken(sourceToken.Value, distinctMapToken.Value); return(true); }
public static bool TryParse( CosmosElement requestContinuationToken, out DistinctContinuationToken distinctContinuationToken) { if (requestContinuationToken == null) { distinctContinuationToken = default; return(false); } if (!(requestContinuationToken is CosmosObject rawObject)) { distinctContinuationToken = default; return(false); } if (!rawObject.TryGetValue(SourceTokenName, out CosmosElement sourceToken)) { distinctContinuationToken = default; return(false); } if (!rawObject.TryGetValue(DistinctMapTokenName, out CosmosElement distinctMapToken)) { distinctContinuationToken = default; return(false); } distinctContinuationToken = new DistinctContinuationToken(sourceToken: sourceToken, distinctMapToken: distinctMapToken); return(true); }
/// <summary> /// Creates an DistinctDocumentQueryExecutionComponent /// </summary> /// <param name="queryClient">The query client</param> /// <param name="requestContinuation">The continuation token.</param> /// <param name="createSourceCallback">The callback to create the source to drain from.</param> /// <param name="distinctQueryType">The type of distinct query.</param> /// <returns>A task to await on and in return </returns> public static async Task <DistinctDocumentQueryExecutionComponent> CreateAsync( CosmosQueryClient queryClient, string requestContinuation, Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback, DistinctQueryType distinctQueryType) { DistinctContinuationToken distinctContinuationToken; if (requestContinuation != null) { if (!DistinctContinuationToken.TryParse(requestContinuation, out distinctContinuationToken)) { throw queryClient.CreateBadRequestException($"Invalid {nameof(DistinctContinuationToken)}: {requestContinuation}"); } } else { distinctContinuationToken = new DistinctContinuationToken(sourceToken: null, distinctMapToken: null); } DistinctMap distinctMap = DistinctMap.Create(distinctQueryType, distinctContinuationToken.DistinctMapToken); IDocumentQueryExecutionComponent source = await createSourceCallback(distinctContinuationToken.SourceToken); return(new ComputeDistinctDocumentQueryExecutionComponent( distinctQueryType, distinctMap, source)); }
/// <summary> /// Drains a page of results returning only distinct elements. /// </summary> /// <param name="maxElements">The maximum number of items to drain.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A page of distinct results.</returns> public override async Task <QueryResponseCore> DrainAsync(int maxElements, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); List <CosmosElement> distinctResults = new List <CosmosElement>(); QueryResponseCore sourceResponse = await base.DrainAsync(maxElements, cancellationToken); if (!sourceResponse.IsSuccess) { return(sourceResponse); } foreach (CosmosElement document in sourceResponse.CosmosElements) { if (this.distinctMap.Add(document, out UInt128 hash)) { distinctResults.Add(document); } } // For clients we write out the continuation token if it's a streaming query. QueryResponseCore queryResponseCore; if (this.distinctQueryType == DistinctQueryType.Ordered) { string updatedContinuationToken; if (this.IsDone) { updatedContinuationToken = null; } else { updatedContinuationToken = new DistinctContinuationToken( sourceToken: sourceResponse.ContinuationToken, distinctMapToken: this.distinctMap.GetContinuationToken()).ToString(); } queryResponseCore = QueryResponseCore.CreateSuccess( result: distinctResults, continuationToken: updatedContinuationToken, disallowContinuationTokenMessage: null, activityId: sourceResponse.ActivityId, requestCharge: sourceResponse.RequestCharge, diagnostics: sourceResponse.Diagnostics, responseLengthBytes: sourceResponse.ResponseLengthBytes); } else { queryResponseCore = QueryResponseCore.CreateSuccess( result: distinctResults, continuationToken: null, disallowContinuationTokenMessage: ClientDistinctDocumentQueryExecutionComponent.DisallowContinuationTokenMessage, activityId: sourceResponse.ActivityId, requestCharge: sourceResponse.RequestCharge, diagnostics: sourceResponse.Diagnostics, responseLengthBytes: sourceResponse.ResponseLengthBytes); } return(queryResponseCore); }
public override bool TryGetContinuationToken(out string continuationToken) { if (this.distinctQueryType != DistinctQueryType.Ordered) { continuationToken = null; return(false); } if (this.IsDone) { continuationToken = null; return(true); } if (!this.Source.TryGetContinuationToken(out string sourceContinuationToken)) { continuationToken = default; return(false); } continuationToken = new DistinctContinuationToken( sourceContinuationToken, this.distinctMap.GetContinuationToken()).ToString(); return(true); }
public override async ValueTask <bool> MoveNextAsync(ITrace trace) { if (trace == null) { throw new ArgumentNullException(nameof(trace)); } if (!await this.inputStage.MoveNextAsync(trace)) { this.Current = default; return(false); } TryCatch <QueryPage> tryGetSourcePage = this.inputStage.Current; if (tryGetSourcePage.Failed) { this.Current = tryGetSourcePage; return(true); } QueryPage sourcePage = tryGetSourcePage.Result; List <CosmosElement> distinctResults = new List <CosmosElement>(); foreach (CosmosElement document in sourcePage.Documents) { if (this.distinctMap.Add(document, out UInt128 _)) { distinctResults.Add(document); } } QueryState queryState; if (sourcePage.State != null) { DistinctContinuationToken distinctContinuationToken = new DistinctContinuationToken( sourceToken: sourcePage.State.Value, distinctMapToken: this.distinctMap.GetCosmosElementContinuationToken()); queryState = new QueryState(DistinctContinuationToken.ToCosmosElement(distinctContinuationToken)); } else { queryState = null; } QueryPage queryPage = new QueryPage( documents: distinctResults, requestCharge: sourcePage.RequestCharge, activityId: sourcePage.ActivityId, responseLengthInBytes: sourcePage.ResponseLengthInBytes, cosmosQueryExecutionInfo: sourcePage.CosmosQueryExecutionInfo, disallowContinuationTokenMessage: ComputeDistinctQueryPipelineStage.UseTryGetContinuationTokenMessage, state: queryState); this.Current = TryCatch <QueryPage> .FromResult(queryPage); return(true); }
public static TryCatch <IQueryPipelineStage> MonadicCreate( CosmosElement requestContinuation, CancellationToken cancellationToken, MonadicCreatePipelineStage monadicCreatePipelineStage, DistinctQueryType distinctQueryType) { if (monadicCreatePipelineStage == null) { throw new ArgumentNullException(nameof(monadicCreatePipelineStage)); } DistinctContinuationToken distinctContinuationToken; if (requestContinuation != null) { if (!DistinctContinuationToken.TryParse(requestContinuation, out distinctContinuationToken)) { return(TryCatch <IQueryPipelineStage> .FromException( new MalformedContinuationTokenException( $"Invalid {nameof(DistinctContinuationToken)}: {requestContinuation}"))); } } else { distinctContinuationToken = new DistinctContinuationToken(sourceToken: null, distinctMapToken: null); } TryCatch <DistinctMap> tryCreateDistinctMap = DistinctMap.TryCreate( distinctQueryType, distinctContinuationToken.DistinctMapToken); if (!tryCreateDistinctMap.Succeeded) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateDistinctMap.Exception)); } TryCatch <IQueryPipelineStage> tryCreateSource = monadicCreatePipelineStage(distinctContinuationToken.SourceToken, cancellationToken); if (!tryCreateSource.Succeeded) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateSource.Exception)); } return(TryCatch <IQueryPipelineStage> .FromResult( new ComputeDistinctQueryPipelineStage( tryCreateDistinctMap.Result, tryCreateSource.Result, cancellationToken))); }
public static async Task <TryCatch <IDocumentQueryExecutionComponent> > TryCreateAsync( CosmosElement requestContinuation, Func <CosmosElement, Task <TryCatch <IDocumentQueryExecutionComponent> > > tryCreateSourceAsync, DistinctQueryType distinctQueryType) { if (tryCreateSourceAsync == null) { throw new ArgumentNullException(nameof(tryCreateSourceAsync)); } DistinctContinuationToken distinctContinuationToken; if (requestContinuation != null) { if (!DistinctContinuationToken.TryParse(requestContinuation, out distinctContinuationToken)) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException( new MalformedContinuationTokenException($"Invalid {nameof(DistinctContinuationToken)}: {requestContinuation}"))); } } else { distinctContinuationToken = new DistinctContinuationToken(sourceToken: null, distinctMapToken: null); } TryCatch <DistinctMap> tryCreateDistinctMap = DistinctMap.TryCreate( distinctQueryType, distinctContinuationToken.DistinctMapToken); if (!tryCreateDistinctMap.Succeeded) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException(tryCreateDistinctMap.Exception)); } TryCatch <IDocumentQueryExecutionComponent> tryCreateSource = await tryCreateSourceAsync( distinctContinuationToken.SourceToken); if (!tryCreateSource.Succeeded) { return(TryCatch <IDocumentQueryExecutionComponent> .FromException(tryCreateSource.Exception)); } return(TryCatch <IDocumentQueryExecutionComponent> .FromResult( new ComputeDistinctDocumentQueryExecutionComponent( distinctQueryType, tryCreateDistinctMap.Result, tryCreateSource.Result))); }
public static CosmosElement ToCosmosElement(DistinctContinuationToken distinctContinuationToken) { Dictionary <string, CosmosElement> dictionary = new Dictionary <string, CosmosElement>() { { DistinctContinuationToken.SourceTokenName, distinctContinuationToken.SourceToken }, { DistinctContinuationToken.DistinctMapTokenName, distinctContinuationToken.DistinctMapToken } }; return(CosmosObject.Create(dictionary)); }
/// <summary> /// Drains a page of results returning only distinct elements. /// </summary> /// <param name="maxElements">The maximum number of items to drain.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A page of distinct results.</returns> public override async Task <QueryResponseCore> DrainAsync(int maxElements, CancellationToken cancellationToken) { List <CosmosElement> distinctResults = new List <CosmosElement>(); QueryResponseCore cosmosQueryResponse = await base.DrainAsync(maxElements, cancellationToken); if (!cosmosQueryResponse.IsSuccess) { return(cosmosQueryResponse); } foreach (CosmosElement document in cosmosQueryResponse.CosmosElements) { if (this.distinctMap.Add(document, out this.lastHash)) { distinctResults.Add(document); } } string updatedContinuationToken; if (!this.IsDone) { updatedContinuationToken = new DistinctContinuationToken( this.lastHash, cosmosQueryResponse.ContinuationToken).ToString(); } else { this.Source.Stop(); updatedContinuationToken = null; } string disallowContinuationTokenMessage = this.distinctQueryType == DistinctQueryType.Ordered ? null : RMResources.UnorderedDistinctQueryContinuationToken; return(QueryResponseCore.CreateSuccess( result: distinctResults, continuationToken: updatedContinuationToken, disallowContinuationTokenMessage: disallowContinuationTokenMessage, activityId: cosmosQueryResponse.ActivityId, requestCharge: cosmosQueryResponse.RequestCharge, queryMetricsText: cosmosQueryResponse.QueryMetricsText, queryMetrics: cosmosQueryResponse.QueryMetrics, requestStatistics: cosmosQueryResponse.RequestStatistics, responseLengthBytes: cosmosQueryResponse.ResponseLengthBytes)); }
/// <summary> /// Tries to parse a DistinctContinuationToken from a string. /// </summary> /// <param name="value">The value to parse.</param> /// <param name="distinctContinuationToken">The output DistinctContinuationToken.</param> /// <returns>True if we successfully parsed the DistinctContinuationToken, else false.</returns> public static bool TryParse(string value, out DistinctContinuationToken distinctContinuationToken) { distinctContinuationToken = default(DistinctContinuationToken); if (string.IsNullOrWhiteSpace(value)) { return(false); } try { distinctContinuationToken = JsonConvert.DeserializeObject <DistinctContinuationToken>(value); return(true); } catch (JsonException ex) { DefaultTrace.TraceWarning($"{DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture)} Invalid continuation token {value} for Distinct~Component, exception: {ex.Message}"); return(false); } }
/// <summary> /// Tries to parse a DistinctContinuationToken from a string. /// </summary> /// <param name="value">The value to parse.</param> /// <param name="distinctContinuationToken">The output DistinctContinuationToken.</param> /// <returns>True if we successfully parsed the DistinctContinuationToken, else false.</returns> public static bool TryParse( string value, out DistinctContinuationToken distinctContinuationToken) { distinctContinuationToken = default(DistinctContinuationToken); if (string.IsNullOrWhiteSpace(value)) { return(false); } try { distinctContinuationToken = JsonConvert.DeserializeObject <DistinctContinuationToken>(value); return(true); } catch (JsonException) { return(false); } }
/// <summary> /// Drains a page of results returning only distinct elements. /// </summary> /// <param name="maxElements">The maximum number of items to drain.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A page of distinct results.</returns> public override async Task <QueryResponse> DrainAsync(int maxElements, CancellationToken cancellationToken) { List <CosmosElement> distinctResults = new List <CosmosElement>(); QueryResponse cosmosQueryResponse = await base.DrainAsync(maxElements, cancellationToken); if (!cosmosQueryResponse.IsSuccessStatusCode) { return(cosmosQueryResponse); } foreach (CosmosElement document in cosmosQueryResponse.CosmosElements) { if (this.distinctMap.Add(document, out this.lastHash)) { distinctResults.Add(document); } } string updatedContinuationToken; if (!this.IsDone) { updatedContinuationToken = new DistinctContinuationToken( this.lastHash, cosmosQueryResponse.Headers.ContinuationToken).ToString(); } else { this.Source.Stop(); updatedContinuationToken = null; } string disallowContinuationTokenMessage = this.distinctQueryType == DistinctQueryType.Ordered ? null : RMResources.UnorderedDistinctQueryContinuationToken; return(QueryResponse.CreateSuccess( distinctResults, distinctResults.Count, cosmosQueryResponse.ResponseLengthBytes, cosmosQueryResponse.QueryHeaders.CloneKnownProperties(updatedContinuationToken, disallowContinuationTokenMessage), cosmosQueryResponse.queryMetrics)); }
/// <summary> /// Creates an DistinctDocumentQueryExecutionComponent /// </summary> /// <param name="requestContinuation">The continuation token.</param> /// <param name="createSourceCallback">The callback to create the source to drain from.</param> /// <param name="distinctQueryType">The type of distinct query.</param> /// <returns>A task to await on and in return </returns> public static async Task <IDocumentQueryExecutionComponent> CreateAsync( string requestContinuation, Func <string, Task <IDocumentQueryExecutionComponent> > createSourceCallback, DistinctQueryType distinctQueryType) { DistinctContinuationToken distinctContinuationToken = new DistinctContinuationToken(null, null); if (requestContinuation != null) { distinctContinuationToken = DistinctContinuationToken.Parse(requestContinuation); if (distinctQueryType != DistinctQueryType.Ordered && distinctContinuationToken.LastHash != null) { throw new BadRequestException($"DistinctContinuationToken is malformed: {distinctContinuationToken}. DistinctContinuationToken can not have a 'lastHash', when the query type is not ordered (ex SELECT DISTINCT VALUE c.blah FROM c ORDER BY c.blah)."); } } return(new DistinctDocumentQueryExecutionComponent( distinctQueryType, distinctContinuationToken.LastHash, await createSourceCallback(distinctContinuationToken.SourceToken))); }
public static TryCatch <IQueryPipelineStage> MonadicCreate( CosmosElement requestContinuation, CancellationToken cancellationToken, MonadicCreatePipelineStage monadicCreatePipelineStage, DistinctQueryType distinctQueryType) { if (monadicCreatePipelineStage == null) { throw new ArgumentNullException(nameof(monadicCreatePipelineStage)); } DistinctContinuationToken distinctContinuationToken; if (requestContinuation != null) { if (!DistinctContinuationToken.TryParse(requestContinuation, out distinctContinuationToken)) { return(TryCatch <IQueryPipelineStage> .FromException( new MalformedContinuationTokenException( $"Invalid {nameof(DistinctContinuationToken)}: {requestContinuation}"))); } } else { distinctContinuationToken = new DistinctContinuationToken( sourceToken: null, distinctMapToken: null); } CosmosElement distinctMapToken = distinctContinuationToken.DistinctMapToken != null ? CosmosString.Create(distinctContinuationToken.DistinctMapToken) : null; TryCatch <DistinctMap> tryCreateDistinctMap = DistinctMap.TryCreate( distinctQueryType, distinctMapToken); if (!tryCreateDistinctMap.Succeeded) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateDistinctMap.Exception)); } CosmosElement sourceToken; if (distinctContinuationToken.SourceToken != null) { TryCatch <CosmosElement> tryParse = CosmosElement.Monadic.Parse(distinctContinuationToken.SourceToken); if (tryParse.Failed) { return(TryCatch <IQueryPipelineStage> .FromException( new MalformedContinuationTokenException( message : $"Invalid Source Token: {distinctContinuationToken.SourceToken}", innerException : tryParse.Exception))); } sourceToken = tryParse.Result; } else { sourceToken = null; } TryCatch <IQueryPipelineStage> tryCreateSource = monadicCreatePipelineStage(sourceToken, cancellationToken); if (!tryCreateSource.Succeeded) { return(TryCatch <IQueryPipelineStage> .FromException(tryCreateSource.Exception)); } return(TryCatch <IQueryPipelineStage> .FromResult( new ClientDistinctQueryPipelineStage( distinctQueryType, tryCreateDistinctMap.Result, tryCreateSource.Result, cancellationToken))); }
public override async ValueTask <bool> MoveNextAsync() { this.cancellationToken.ThrowIfCancellationRequested(); if (!await this.inputStage.MoveNextAsync()) { this.Current = default; return(false); } TryCatch <QueryPage> tryGetSourcePage = this.inputStage.Current; if (tryGetSourcePage.Failed) { this.Current = tryGetSourcePage; return(true); } QueryPage sourcePage = tryGetSourcePage.Result; List <CosmosElement> distinctResults = new List <CosmosElement>(); foreach (CosmosElement document in sourcePage.Documents) { this.cancellationToken.ThrowIfCancellationRequested(); if (this.distinctMap.Add(document, out UInt128 _)) { distinctResults.Add(document); } } // For clients we write out the continuation token if it's a streaming query. QueryPage queryPage; if (this.distinctQueryType == DistinctQueryType.Ordered) { QueryState state; if (sourcePage.State != null) { string updatedContinuationToken = new DistinctContinuationToken( sourceToken: sourcePage.State.Value.ToString(), distinctMapToken: this.distinctMap.GetContinuationToken()).ToString(); state = new QueryState(CosmosElement.Parse(updatedContinuationToken)); } else { state = null; } queryPage = new QueryPage( documents: distinctResults, requestCharge: sourcePage.RequestCharge, activityId: sourcePage.ActivityId, responseLengthInBytes: sourcePage.ResponseLengthInBytes, cosmosQueryExecutionInfo: sourcePage.CosmosQueryExecutionInfo, disallowContinuationTokenMessage: sourcePage.DisallowContinuationTokenMessage, state: state); } else { queryPage = new QueryPage( documents: distinctResults, requestCharge: sourcePage.RequestCharge, activityId: sourcePage.ActivityId, responseLengthInBytes: sourcePage.ResponseLengthInBytes, cosmosQueryExecutionInfo: sourcePage.CosmosQueryExecutionInfo, disallowContinuationTokenMessage: ClientDistinctQueryPipelineStage.DisallowContinuationTokenMessage, state: null); } this.Current = TryCatch <QueryPage> .FromResult(queryPage); return(true); }