private async Task <SearchResult> SearchImpl(SearchOptions searchOptions, bool historySearch, CancellationToken cancellationToken) { await _model.EnsureInitialized(); Expression searchExpression = searchOptions.Expression; // AND in the continuation token if (!string.IsNullOrWhiteSpace(searchOptions.ContinuationToken)) { if (long.TryParse(searchOptions.ContinuationToken, NumberStyles.None, CultureInfo.InvariantCulture, out var token)) { var tokenExpression = Expression.SearchParameter(SqlSearchParameters.ResourceSurrogateIdParameter, Expression.GreaterThan(SqlFieldName.ResourceSurrogateId, null, token)); searchExpression = searchExpression == null ? tokenExpression : (Expression)Expression.And(tokenExpression, searchExpression); } else { throw new BadRequestException(Resources.InvalidContinuationToken); } } SqlRootExpression expression = (SqlRootExpression)searchExpression ?.AcceptVisitor(LastUpdatedToResourceSurrogateIdRewriter.Instance) .AcceptVisitor(DateTimeEqualityRewriter.Instance) .AcceptVisitor(FlatteningRewriter.Instance) .AcceptVisitor(_sqlRootExpressionRewriter) .AcceptVisitor(TableExpressionCombiner.Instance) .AcceptVisitor(DenormalizedPredicateRewriter.Instance) .AcceptVisitor(NormalizedPredicateReorderer.Instance) .AcceptVisitor(_chainFlatteningRewriter) .AcceptVisitor(DateTimeBoundedRangeRewriter.Instance) .AcceptVisitor(_stringOverflowRewriter) .AcceptVisitor(NumericRangeRewriter.Instance) .AcceptVisitor(MissingSearchParamVisitor.Instance) .AcceptVisitor(IncludeDenormalizedRewriter.Instance) .AcceptVisitor(TopRewriter.Instance, searchOptions) .AcceptVisitor(IncludeRewriter.Instance) ?? SqlRootExpression.WithDenormalizedExpressions(); using (var connection = new SqlConnection(_configuration.ConnectionString)) { connection.Open(); using (SqlCommand sqlCommand = connection.CreateCommand()) { var stringBuilder = new IndentedStringBuilder(new StringBuilder()); EnableTimeAndIoMessageLogging(stringBuilder, connection); var queryGenerator = new SqlQueryGenerator(stringBuilder, new SqlQueryParameterManager(sqlCommand.Parameters), _model, historySearch); expression.AcceptVisitor(queryGenerator, searchOptions); sqlCommand.CommandText = stringBuilder.ToString(); LogSqlComand(sqlCommand); using (var reader = await sqlCommand.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)) { if (searchOptions.CountOnly) { await reader.ReadAsync(cancellationToken); return(new SearchResult(reader.GetInt32(0), searchOptions.UnsupportedSearchParams)); } var resources = new List <SearchResultEntry>(searchOptions.MaxItemCount); long?newContinuationId = null; bool moreResults = false; int matchCount = 0; while (await reader.ReadAsync(cancellationToken)) { (short resourceTypeId, string resourceId, int version, bool isDeleted, long resourceSurrogateId, string requestMethod, bool isMatch, Stream rawResourceStream) = reader.ReadRow( V1.Resource.ResourceTypeId, V1.Resource.ResourceId, V1.Resource.Version, V1.Resource.IsDeleted, V1.Resource.ResourceSurrogateId, V1.Resource.RequestMethod, _isMatch, V1.Resource.RawResource); // If we get to this point, we know there are more results so we need a continuation token // Additionally, this resource shouldn't be included in the results if (matchCount >= searchOptions.MaxItemCount && isMatch) { moreResults = true; continue; } // See if this resource is a continuation token candidate and increase the count if (isMatch) { newContinuationId = resourceSurrogateId; matchCount++; } string rawResource; using (rawResourceStream) using (var gzipStream = new GZipStream(rawResourceStream, CompressionMode.Decompress)) using (var streamReader = new StreamReader(gzipStream, SqlServerFhirDataStore.ResourceEncoding)) { rawResource = await streamReader.ReadToEndAsync(); } resources.Add(new SearchResultEntry( new ResourceWrapper( resourceId, version.ToString(CultureInfo.InvariantCulture), _model.GetResourceTypeName(resourceTypeId), new RawResource(rawResource, FhirResourceFormat.Json), new ResourceRequest(requestMethod), new DateTimeOffset(ResourceSurrogateIdHelper.ResourceSurrogateIdToLastUpdated(resourceSurrogateId), TimeSpan.Zero), isDeleted, null, null, null), isMatch ? SearchEntryMode.Match : SearchEntryMode.Include)); } // call NextResultAsync to get the info messages await reader.NextResultAsync(cancellationToken); IReadOnlyList <(string parameterName, string reason)> unsupportedSortingParameters; if (searchOptions.Sort?.Count > 0) { // we don't currently support sort unsupportedSortingParameters = searchOptions.UnsupportedSortingParams.Concat(searchOptions.Sort.Select(s => (s.searchParameterInfo.Name, Core.Resources.SortNotSupported))).ToList(); } else { unsupportedSortingParameters = searchOptions.UnsupportedSortingParams; } return(new SearchResult(resources, searchOptions.UnsupportedSearchParams, unsupportedSortingParameters, moreResults ? newContinuationId.Value.ToString(CultureInfo.InvariantCulture) : null)); } } } }
public async Task EnsureInitializedAsync() { await _model.EnsureInitialized(); }