in src/Microsoft.Health.Fhir.SqlServer/Features/Search/SqlServerSearchService.cs [99:178]
public override async Task<SearchResult> SearchAsync(SearchOptions searchOptions, CancellationToken cancellationToken)
{
SqlSearchOptions sqlSearchOptions = new SqlSearchOptions(searchOptions);
SearchResult searchResult = await SearchImpl(sqlSearchOptions, SqlSearchType.Default, null, cancellationToken);
int resultCount = searchResult.Results.Count();
if (!sqlSearchOptions.IsSortWithFilter &&
searchResult.ContinuationToken == null &&
resultCount <= sqlSearchOptions.MaxItemCount &&
sqlSearchOptions.Sort != null &&
sqlSearchOptions.Sort.Count > 0 &&
sqlSearchOptions.Sort[0].searchParameterInfo.Code != KnownQueryParameterNames.LastUpdated)
{
// We seem to have run a sort which has returned less results than what max we can return.
// Let's determine whether we need to execute another query or not.
if ((sqlSearchOptions.Sort[0].sortOrder == SortOrder.Ascending && sqlSearchOptions.DidWeSearchForSortValue.HasValue && !sqlSearchOptions.DidWeSearchForSortValue.Value) ||
(sqlSearchOptions.Sort[0].sortOrder == SortOrder.Descending && sqlSearchOptions.DidWeSearchForSortValue.HasValue && sqlSearchOptions.DidWeSearchForSortValue.Value))
{
if (sqlSearchOptions.MaxItemCount - resultCount == 0)
{
// Since we are already returning MaxItemCount number of resources we don't want
// to execute another search right now just to drop all the resources. We will return
// a "special" ct so that we the subsequent request will be handled correctly.
var ct = new ContinuationToken(new object[]
{
SqlSearchConstants.SortSentinelValueForCt,
0,
});
searchResult = new SearchResult(searchResult.Results, ct.ToJson(), searchResult.SortOrder, searchResult.UnsupportedSearchParameters);
}
else
{
var finalResultsInOrder = new List<SearchResultEntry>();
finalResultsInOrder.AddRange(searchResult.Results);
sqlSearchOptions.SortQuerySecondPhase = true;
sqlSearchOptions.MaxItemCount -= resultCount;
searchResult = await SearchImpl(sqlSearchOptions, SqlSearchType.Default, null, cancellationToken);
finalResultsInOrder.AddRange(searchResult.Results);
searchResult = new SearchResult(
finalResultsInOrder,
searchResult.ContinuationToken,
searchResult.SortOrder,
searchResult.UnsupportedSearchParameters);
}
}
}
// If we should include the total count of matching search results
if (sqlSearchOptions.IncludeTotal == TotalType.Accurate && !sqlSearchOptions.CountOnly)
{
// If this is the first page and there aren't any more pages
if (sqlSearchOptions.ContinuationToken == null && searchResult.ContinuationToken == null)
{
// Count the match results on the page.
searchResult.TotalCount = searchResult.Results.Count(r => r.SearchEntryMode == SearchEntryMode.Match);
}
else
{
try
{
// Otherwise, indicate that we'd like to get the count
sqlSearchOptions.CountOnly = true;
// And perform a second read.
var countOnlySearchResult = await SearchImpl(sqlSearchOptions, SqlSearchType.Default, null, cancellationToken);
searchResult.TotalCount = countOnlySearchResult.TotalCount;
}
finally
{
// Ensure search options is set to its original state.
sqlSearchOptions.CountOnly = false;
}
}
}
return searchResult;
}