public async Task <FindResults <TResult> > FindAsAsync <TResult>(IRepositoryQuery query, ICommandOptions options = null) where TResult : class, new()
        {
            if (query == null)
            {
                query = new RepositoryQuery();
            }

            bool useSnapshotPaging = options.ShouldUseSnapshotPaging();
            // don't use caching with snapshot paging.
            bool allowCaching = IsCacheEnabled && useSnapshotPaging == false;

            options = ConfigureOptions(options);
            await OnBeforeQueryAsync(query, options, typeof(TResult)).AnyContext();

            Func <FindResults <TResult>, Task <FindResults <TResult> > > getNextPageFunc = async r => {
                var previousResults = r;
                if (previousResults == null)
                {
                    throw new ArgumentException(nameof(r));
                }

                if (!String.IsNullOrEmpty(previousResults.GetScrollId()))
                {
                    var scrollResponse = await _client.ScrollAsync <TResult>(options.GetSnapshotLifetime(), previousResults.GetScrollId()).AnyContext();

                    _logger.Trace(() => scrollResponse.GetRequest());

                    var results = scrollResponse.ToFindResults();
                    results.Page    = previousResults.Page + 1;
                    results.HasMore = scrollResponse.Hits.Count() >= options.GetLimit();
                    return(results);
                }

                if (options == null)
                {
                    return(new FindResults <TResult>());
                }

                options?.PageNumber(!options.HasPageNumber() ? 2 : options.GetPage() + 1);

                return(await FindAsAsync <TResult>(query, options).AnyContext());
            };

            string cacheSuffix = options?.HasPageLimit() == true?String.Concat(options.GetPage().ToString(), ":", options.GetLimit().ToString()) : null;

            FindResults <TResult> result;

            if (allowCaching)
            {
                result = await GetCachedQueryResultAsync <FindResults <TResult> >(options, cacheSuffix : cacheSuffix).AnyContext();

                if (result != null)
                {
                    ((IGetNextPage <TResult>)result).GetNextPageFunc = async r => await getNextPageFunc(r).AnyContext();

                    return(result);
                }
            }

            ISearchResponse <TResult> response = null;

            if (useSnapshotPaging == false || !options.HasSnapshotScrollId())
            {
                var searchDescriptor = await CreateSearchDescriptorAsync(query, options).AnyContext();

                if (useSnapshotPaging)
                {
                    searchDescriptor.Scroll(options.GetSnapshotLifetime());
                }

                response = await _client.SearchAsync <TResult>(searchDescriptor).AnyContext();
            }
            else
            {
                response = await _client.ScrollAsync <TResult>(options.GetSnapshotLifetime(), options.GetSnapshotScrollId()).AnyContext();
            }

            _logger.Trace(() => response.GetRequest());
            if (!response.IsValid)
            {
                if (response.ApiCall.HttpStatusCode.GetValueOrDefault() == 404)
                {
                    return(new FindResults <TResult>());
                }

                string message = response.GetErrorMessage();
                _logger.Error().Exception(response.OriginalException).Message(message).Property("request", response.GetRequest()).Write();
                throw new ApplicationException(message, response.OriginalException);
            }

            if (useSnapshotPaging)
            {
                result = response.ToFindResults();
                // TODO: Is there a better way to figure out if you are done scrolling?
                result.HasMore = response.Hits.Count() >= options.GetLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = getNextPageFunc;
            }
            else if (options.HasPageLimit() == true)
            {
                result         = response.ToFindResults(options.GetLimit());
                result.HasMore = response.Hits.Count() > options.GetLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = getNextPageFunc;
            }
            else
            {
                result = response.ToFindResults();
            }

            result.Page = options.GetPage();

            if (!allowCaching)
            {
                return(result);
            }

            var nextPageFunc = ((IGetNextPage <TResult>)result).GetNextPageFunc;

            ((IGetNextPage <TResult>)result).GetNextPageFunc = null;
            await SetCachedQueryResultAsync(options, result, cacheSuffix : cacheSuffix).AnyContext();

            ((IGetNextPage <TResult>)result).GetNextPageFunc = nextPageFunc;

            return(result);
        }
Esempio n. 2
0
        protected async Task <FindResults <TResult> > FindAsAsync <TResult>(IRepositoryQuery query) where TResult : class, new()
        {
            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }

            var  pagableQuery         = query as IPagableQuery;
            var  pagingOptions        = pagableQuery?.Options as IPagingOptions;
            var  elasticPagingOptions = pagableQuery?.Options as ElasticPagingOptions;
            bool useSnapshotPaging    = elasticPagingOptions?.UseSnapshotPaging ?? false;

            // don't use caching with snapshot paging.
            bool allowCaching = IsCacheEnabled && useSnapshotPaging == false;

            var queryOptions = GetQueryOptions();

            await OnBeforeQueryAsync(query, queryOptions, typeof(TResult)).AnyContext();

            Func <FindResults <TResult>, Task <FindResults <TResult> > > getNextPageFunc = async r => {
                var previousResults = r;
                if (previousResults == null)
                {
                    throw new ArgumentException(nameof(r));
                }

                if (!String.IsNullOrEmpty(previousResults.GetScrollId()))
                {
                    var scrollResponse = await _client.ScrollAsync <TResult>(pagableQuery.GetLifetime(), previousResults.GetScrollId()).AnyContext();

                    _logger.Trace(() => scrollResponse.GetRequest());

                    var results = scrollResponse.ToFindResults();
                    results.Page    = previousResults.Page + 1;
                    results.HasMore = scrollResponse.Hits.Count() >= pagableQuery.GetLimit();
                    return(results);
                }

                if (pagableQuery == null)
                {
                    return(new FindResults <TResult>());
                }

                if (pagingOptions != null)
                {
                    pagingOptions.Page = pagingOptions.Page == null ? 2 : pagingOptions.Page + 1;
                }

                return(await FindAsAsync <TResult>(query).AnyContext());
            };

            string cacheSuffix = pagableQuery?.ShouldUseLimit() == true?String.Concat(pagingOptions.Page?.ToString() ?? "1", ":", pagableQuery.GetLimit().ToString()) : String.Empty;

            FindResults <TResult> result;

            if (allowCaching)
            {
                result = await GetCachedQueryResultAsync <FindResults <TResult> >(query, cacheSuffix : cacheSuffix).AnyContext();

                if (result != null)
                {
                    ((IGetNextPage <TResult>)result).GetNextPageFunc = async r => await getNextPageFunc(r).AnyContext();

                    return(result);
                }
            }

            ISearchResponse <TResult> response = null;

            if (useSnapshotPaging == false || String.IsNullOrEmpty(elasticPagingOptions?.ScrollId))
            {
                SearchDescriptor <T> searchDescriptor = await CreateSearchDescriptorAsync(query, queryOptions).AnyContext();

                if (useSnapshotPaging)
                {
                    searchDescriptor.SearchType(SearchType.Scan).Scroll(pagableQuery.GetLifetime());
                }

                response = await _client.SearchAsync <TResult>(searchDescriptor).AnyContext();

                _logger.Trace(() => response.GetRequest());
                if (!response.IsValid)
                {
                    if (response.ConnectionStatus.HttpStatusCode.GetValueOrDefault() == 404)
                    {
                        return(new FindResults <TResult>());
                    }

                    string message = response.GetErrorMessage();
                    _logger.Error().Exception(response.ConnectionStatus.OriginalException).Message(message).Property("request", response.GetRequest()).Write();
                    throw new ApplicationException(message, response.ConnectionStatus.OriginalException);
                }
            }

            if (useSnapshotPaging)
            {
                // The response might have returned 0 search results.
                if (response?.Total == 0)
                {
                    return(response.ToFindResults());
                }

                var scrollResponse = await _client.ScrollAsync <TResult>(pagableQuery.GetLifetime(), response?.ScrollId ?? elasticPagingOptions?.ScrollId).AnyContext();

                _logger.Trace(() => scrollResponse.GetRequest());

                if (!scrollResponse.IsValid)
                {
                    string message = scrollResponse.GetErrorMessage();
                    _logger.Error().Exception(scrollResponse.ConnectionStatus.OriginalException).Message(message).Property("request", scrollResponse.GetRequest()).Write();
                    throw new ApplicationException(message, scrollResponse.ConnectionStatus.OriginalException);
                }

                result         = scrollResponse.ToFindResults();
                result.HasMore = scrollResponse.Hits.Count() >= pagableQuery.GetLimit();

                ((IGetNextPage <TResult>)result).GetNextPageFunc = getNextPageFunc;
            }
            else if (pagableQuery?.ShouldUseLimit() == true)
            {
                result         = response.ToFindResults(pagableQuery.GetLimit());
                result.HasMore = response.Hits.Count() > pagableQuery.GetLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = getNextPageFunc;
            }
            else
            {
                result = response.ToFindResults();
            }

            result.Page = pagingOptions?.Page ?? 1;

            if (!allowCaching)
            {
                return(result);
            }

            var nextPageFunc = ((IGetNextPage <TResult>)result).GetNextPageFunc;

            ((IGetNextPage <TResult>)result).GetNextPageFunc = null;
            await SetCachedQueryResultAsync(query, result, cacheSuffix : cacheSuffix).AnyContext();

            ((IGetNextPage <TResult>)result).GetNextPageFunc = nextPageFunc;

            return(result);
        }