public async Task <IActionResult> ManifestSearchPostAsync( [HttpTrigger(AuthorizationLevel.Anonymous, FunctionConstants.FunctionPost, Route = "manifestSearch")] HttpRequest req, ILogger log) { Dictionary <string, string> headers = null; ManifestSearchRequest manifestSearch = null; ApiDataPage <ManifestSearchResponse> manifestSearchResponse = new ApiDataPage <ManifestSearchResponse>(); try { // Parse Headers headers = HeaderProcessor.ToDictionary(req.Headers); // Get Manifest Search Request and Validate. manifestSearch = await Parser.StreamParser <ManifestSearchRequest>(req.Body, log); ApiDataValidator.Validate(manifestSearch); manifestSearchResponse = await this.dataStore.SearchPackageManifests(manifestSearch, headers, req.Query); } catch (DefaultException e) { log.LogError(e.ToString()); return(ActionResultHelper.ProcessError(e.InternalRestError)); } catch (Exception e) { log.LogError(e.ToString()); return(ActionResultHelper.UnhandledError(e)); } return(manifestSearchResponse.Items.Count() switch { 0 => new NoContentResult(), _ => new ApiObjectResult(new ApiResponse <List <ManifestSearchResponse> >(manifestSearchResponse.Items.ToList(), manifestSearchResponse.ContinuationToken)), });
/// <inheritdoc /> public async Task <ApiDataPage <ManifestSearchResponse> > SearchPackageManifests(ManifestSearchRequest manifestSearchRequest, Dictionary <string, string> headers, IQueryCollection queryParameters) { // Create Working Set and return ApiDataPage <ManifestSearchResponse> apiDataPage = new ApiDataPage <ManifestSearchResponse>(); List <PackageManifest> manifests = new List <PackageManifest>(); List <ManifestSearchResponse> manifestSearchResponse = new List <ManifestSearchResponse>(); // Create feed options for inclusion search: -1 so we can get all matches in inclusion, then filter down. FeedOptions feedOptions = new FeedOptions { ResponseContinuationTokenLimitInKb = CosmosConnectionConstants.ResponseContinuationTokenLimitInKb, EnableCrossPartitionQuery = true, MaxItemCount = AllElements, RequestContinuation = null, }; if (manifestSearchRequest.FetchAllManifests || (manifestSearchRequest.Inclusions == null && manifestSearchRequest.Query == null)) { IQueryable <CosmosPackageManifest> query = this.cosmosDatabase.GetIQueryable <CosmosPackageManifest>(feedOptions); IDocumentQuery <CosmosPackageManifest> documentQuery = query.AsDocumentQuery(); ApiDataPage <CosmosPackageManifest> apiDataDocument = await this.cosmosDatabase.GetByDocumentQuery <CosmosPackageManifest>(documentQuery); manifests.AddRange(apiDataDocument.Items); } else { // Process Inclusions Models.Arrays.SearchRequestPackageMatchFilter inclusions = new Models.Arrays.SearchRequestPackageMatchFilter(); if (manifestSearchRequest.Inclusions != null) { inclusions.AddRange(manifestSearchRequest.Inclusions); } // Convert Query to inclusions to submit to cosmos if (manifestSearchRequest.Query != null) { inclusions.AddRange(this.queryList.Select(q => new Models.Objects.SearchRequestPackageMatchFilter() { PackageMatchField = q, RequestMatch = manifestSearchRequest.Query, })); } // Submit Inclusions to Cosmos // Due to join limitation on Cosmos - we are submitting each predicate separately. // TODO: Create a more efficient search - but this will suffice for now for a light weight reference. if (inclusions.Count > 0) { List <Task <ApiDataPage <PackageManifest> > > taskSet = new List <Task <ApiDataPage <PackageManifest> > >(); foreach (string packageMatchField in inclusions.Select(inc => inc.PackageMatchField).Distinct()) { // Create Predicate for search ExpressionStarter <PackageManifest> inclusionPredicate = PredicateBuilder.New <PackageManifest>(); foreach (SearchRequestPackageMatchFilter matchFilter in inclusions.Where(inc => inc.PackageMatchField.Equals(packageMatchField))) { // Some package match fields or types might not be supported by current version of search. // So we will check the supported list before adding any predicates. if (this.IsPackageMatchFieldSupported(matchFilter.PackageMatchField) && this.IsMatchTypeSupported(matchFilter.RequestMatch.MatchType)) { inclusionPredicate.Or(this.QueryPredicate(matchFilter.PackageMatchField, matchFilter.RequestMatch)); } } // Create Document Query IQueryable <PackageManifest> query = this.cosmosDatabase.GetIQueryable <PackageManifest>(feedOptions); query = query.Where(inclusionPredicate); IDocumentQuery <PackageManifest> documentQuery = query.AsDocumentQuery(); // Submit Query to Cosmos taskSet.Add(Task.Run(() => this.cosmosDatabase.GetByDocumentQuery(documentQuery))); } // Wait for Cosmos Queries to complete await Task.WhenAll(taskSet.ToArray()); // Process Manifests from Cosmos foreach (Task <ApiDataPage <PackageManifest> > task in taskSet) { manifests.AddRange(task.Result.Items); } manifests = manifests.Distinct().ToList(); } } // Process Filters if (manifestSearchRequest.Filters != null) { ExpressionStarter <PackageManifest> filterPredicate = PredicateBuilder.New <PackageManifest>(); foreach (SearchRequestPackageMatchFilter matchFilter in manifestSearchRequest.Filters) { if (this.IsPackageMatchFieldSupported(matchFilter.PackageMatchField) && this.IsMatchTypeSupported(matchFilter.RequestMatch.MatchType)) { filterPredicate.Or(this.QueryPredicate(matchFilter.PackageMatchField, matchFilter.RequestMatch)); } } manifests = manifests.Where(filterPredicate).ToList(); } foreach (PackageManifest manifest in manifests) { foreach (ManifestSearchResponse response in ManifestSearchResponse.GetSearchVersions(manifest)) { manifestSearchResponse.Add(response); } } // Consolidate Results manifestSearchResponse = ManifestSearchResponse.Consolidate(manifestSearchResponse).OrderBy(manifest => manifest.PackageIdentifier).ToList(); // Process results if (manifestSearchResponse.Count > manifestSearchRequest.MaximumResults && manifestSearchRequest.MaximumResults > 0) { manifestSearchResponse = manifestSearchResponse.GetRange(0, manifestSearchRequest.MaximumResults); } int maxPageCount = manifestSearchRequest.MaximumResults < FunctionSettingsConstants.MaxResultsPerPage && manifestSearchRequest.MaximumResults > 0 ? manifestSearchRequest.MaximumResults : FunctionSettingsConstants.MaxResultsPerPage; int totalResults = manifestSearchResponse.Count; if (totalResults > maxPageCount) { // Process Continuation Token ApiContinuationToken token = null; if (headers.ContainsKey(HeaderConstants.ContinuationToken)) { token = Parser.StringParser <ApiContinuationToken>(StringEncoder.DecodeContinuationToken(headers[HeaderConstants.ContinuationToken])); } else { token = new ApiContinuationToken() { Index = 0, MaxPageSize = maxPageCount, }; } // If index miss-match dump results and return no content. if (token.Index > manifestSearchResponse.Count - 1) { manifestSearchResponse = new List <ManifestSearchResponse>(); token = null; } else { int elementsRemaining = totalResults - token.Index; int elements = elementsRemaining < token.MaxPageSize ? elementsRemaining : token.MaxPageSize; manifestSearchResponse = manifestSearchResponse.GetRange(token.Index, elements); token.Index += elements; if (token.Index == totalResults) { token = null; } } apiDataPage.ContinuationToken = token != null?StringEncoder.EncodeContinuationToken(FormatJSON.None(token)) : null; } apiDataPage.Items = ManifestSearchResponse.Consolidate(manifestSearchResponse.ToList()); return(apiDataPage); }