// Merge facets across multiple search services static void MergeFacets(IDictionary <string, IList <FacetResult> > singleServiceFacets, MultiSearchFacets mergedFacets) { foreach (KeyValuePair <string, IList <FacetResult> > resultFacets in singleServiceFacets) { string fieldName = resultFacets.Key; IList <FacetResult> serviceFacets = resultFacets.Value; if (mergedFacets.Facets.TryGetValue(fieldName, out List <MultiSearchFacet> multiServiceFacets)) { // If a facet can be merged into an existing multi-service facet, combine the counts // Otherwise, add a new multi-service facet matching the existing facet foreach (FacetResult facet in serviceFacets) { bool foundFacet = false; foreach (MultiSearchFacet multiServiceFacet in multiServiceFacets) { if (multiServiceFacet.CanMergeFacet(facet)) { foundFacet = true; multiServiceFacet.Count += facet.Count.Value; break; } } if (!foundFacet) { multiServiceFacets.Add(new MultiSearchFacet(facet)); } } } else { // Initialize the multi-service facet list with the facets from this service for this field mergedFacets.Facets.Add(fieldName, serviceFacets.Select(f => new MultiSearchFacet(f)).ToList()); } } }
// Run the query and combine results across multiple services static async Task <(int, List <MultiSearchResult>, MultiSearchFacets)> RunQueryAsync(string query, int pageNumber, int pageSize, string searchFields, string facets, List <Service> services) { // Page results from all services var searchResults = new List <IAsyncEnumerator <MultiSearchResultsPage> >(); foreach (Service service in services) { IAsyncEnumerable <MultiSearchResultsPage> response = SearchAsync(service, query, pageSize, searchFields, facets); searchResults.Add(response.GetAsyncEnumerator()); } // Merge each individual page from every service // Sort the combined page by result score int currentPageNumber = 0; var currentPage = new List <MultiSearchResult>(); var mergedFacets = new MultiSearchFacets(); do { // Combine the current page of results from each service // If the service has no more results, it is discarded var resultPages = new List <MultiSearchResultsPage>(); var nextSearchResults = new List <IAsyncEnumerator <MultiSearchResultsPage> >(); foreach (IAsyncEnumerator <MultiSearchResultsPage> pageEnumerator in searchResults) { if (await pageEnumerator.MoveNextAsync()) { resultPages.Add(pageEnumerator.Current); nextSearchResults.Add(pageEnumerator); } } searchResults = nextSearchResults; var mergedSearchResults = new List <MultiSearchResult>(); foreach (MultiSearchResultsPage resultPage in resultPages) { foreach (SearchResult <BookModel> result in resultPage.Page) { mergedSearchResults.Add(new MultiSearchResult { Service = resultPage.Service, Result = result }); } if (resultPage.Facets != null) { MergeFacets(resultPage.Facets, mergedFacets); } } // Sort the combined pages by score descending mergedSearchResults.Sort((a, b) => { MultiSearchResult resultA = a; MultiSearchResult resultB = b; if (resultA.Result.Score.HasValue && resultB.Result.Score.HasValue) { return(resultB.Result.Score.Value.CompareTo(resultA.Result.Score.Value)); } if (resultA.Result.Score.HasValue && !resultB.Result.Score.HasValue) { return(-1); } if (!resultA.Result.Score.HasValue && resultB.Result.Score.HasValue) { return(1); } return(0); }); // Return sub-pages of results from the combined page foreach (MultiSearchResult mergedSearchResult in mergedSearchResults) { currentPage.Add(mergedSearchResult); if (currentPage.Count == pageSize) { if (currentPageNumber == pageNumber) { return(currentPageNumber, currentPage, mergedFacets); } currentPage.Clear(); currentPageNumber++; } } }while (searchResults.Any()); // Return any leftover results as the last page return(currentPageNumber, currentPage, mergedFacets); }