Example #1
0
        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();
 }