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); }
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); }