Exemplo n.º 1
0
        public static int GetSkip(this ICommandOptions options)
        {
            if (!options.HasPageLimit() && !options.HasPageNumber())
            {
                return(0);
            }

            int limit = options.GetLimit();
            int page  = options.GetPage();

            int skip = (page - 1) * limit;

            if (skip < 0)
            {
                skip = 0;
            }

            return(skip);
        }
        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);
        }
        public virtual 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();

            async Task <FindResults <TResult> > GetNextPageFunc(FindResults <TResult> r)
            {
                var previousResults = r;

                if (previousResults == null)
                {
                    throw new ArgumentException(nameof(r));
                }

                string scrollId = previousResults.GetScrollId();

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

                    if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace))
                    {
                        _logger.LogTrace(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;

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

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

                string message = response.GetErrorMessage();
                _logger.LogError(response.OriginalException, message);
                throw new ApplicationException(message, response.OriginalException);
            }

            if (useSnapshotPaging)
            {
                result         = response.ToFindResults();
                result.HasMore = response.Hits.Count >= options.GetLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = GetNextPageFunc;
            }
            else if (options.HasPageLimit())
            {
                int limit = options.GetLimit();
                result         = response.ToFindResults(limit);
                result.HasMore = response.Hits.Count > limit;
                ((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);
        }
        public virtual 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();

            async Task <FindResults <TResult> > GetNextPageFunc(FindResults <TResult> r)
            {
                var previousResults = r;

                if (previousResults == null)
                {
                    throw new ArgumentException(nameof(r));
                }

                string scrollId = previousResults.GetScrollId();

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

                    if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Trace))
                    {
                        _logger.LogTrace(scrollResponse.GetRequest());
                    }

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

                if (options.ShouldUseSearchAfterPaging())
                {
                    var lastDocument = previousResults.Documents.LastOrDefault();
                    if (lastDocument != null)
                    {
                        var searchAfterValues = new List <object>();
                        var sorts             = query.GetSorts();
                        if (sorts.Count > 0)
                        {
                            foreach (var sort in query.GetSorts())
                            {
                                if (sort.SortKey.Property?.DeclaringType == lastDocument.GetType())
                                {
                                    searchAfterValues.Add(sort.SortKey.Property.GetValue(lastDocument));
                                }
                                else if (typeof(TResult) == typeof(T) && sort.SortKey.Expression is Expression <Func <T, object> > valueGetterExpression)
                                {
                                    var valueGetter       = valueGetterExpression.Compile();
                                    var typedLastDocument = lastDocument as T;
                                    if (typedLastDocument != null)
                                    {
                                        var value = valueGetter.Invoke(typedLastDocument);
                                        searchAfterValues.Add(value);
                                    }
                                }
                                else if (sort.SortKey.Name != null)
                                {
                                    var propertyInfo = lastDocument.GetType().GetProperty(sort.SortKey.Name);
                                    if (propertyInfo != null)
                                    {
                                        searchAfterValues.Add(propertyInfo.GetValue(lastDocument));
                                    }
                                }
                                else
                                {
                                    // TODO: going to to need to take the Expression and pull the string name from it
                                }
                            }
                        }
                        else if (lastDocument is IIdentity lastDocumentId)
                        {
                            searchAfterValues.Add(lastDocumentId.Id);
                        }

                        if (searchAfterValues.Count > 0)
                        {
                            options.SearchAfter(searchAfterValues.ToArray());
                        }
                        else
                        {
                            throw new ArgumentException("Unable to automatically calculate values for SearchAfterPaging.");
                        }
                    }
                }

                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;

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

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

                string message = response.GetErrorMessage();
                _logger.LogError(response.OriginalException, message);
                throw new ApplicationException(message, response.OriginalException);
            }

            if (useSnapshotPaging)
            {
                result         = response.ToFindResults();
                result.HasMore = response.Hits.Count >= options.GetLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = GetNextPageFunc;
            }
            else if (options.HasPageLimit())
            {
                int limit = options.GetLimit();
                result         = response.ToFindResults(limit);
                result.HasMore = response.Hits.Count > limit;
                ((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);
        }
        public virtual async Task <FindResults <TResult> > FindAsAsync <TResult>(IRepositoryQuery query, ICommandOptions options = null) where TResult : class, new()
        {
            options = ConfigureOptions(options.As <T>());
            bool useSnapshotPaging = options.ShouldUseSnapshotPaging();
            // don't use caching with snapshot paging.
            bool allowCaching = IsCacheEnabled && useSnapshotPaging == false;

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

            await RefreshForConsistency(query, options).AnyContext();

            async Task <FindResults <TResult> > GetNextPageFunc(FindResults <TResult> r)
            {
                var previousResults = r;

                if (previousResults == null)
                {
                    throw new ArgumentException(nameof(r));
                }

                string scrollId = previousResults.GetScrollId();

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

                    _logger.LogRequest(scrollResponse, options.GetQueryLogLevel());

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

                    // clear the scroll
                    if (!results.HasMore)
                    {
                        await _client.ClearScrollAsync(s => s.ScrollId(scrollId));
                    }

                    return(results);
                }

                if (options.ShouldUseSearchAfterPaging())
                {
                    options.SearchAfterToken(previousResults.GetSearchAfterToken());
                }

                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;

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

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

                if (query.ShouldOnlyHaveIds())
                {
                    searchDescriptor.Source(false);
                }

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

            if (response.IsValid)
            {
                _logger.LogRequest(response, options.GetQueryLogLevel());
            }
            else
            {
                if (response.ApiCall.HttpStatusCode.GetValueOrDefault() == 404)
                {
                    return(new FindResults <TResult>());
                }

                _logger.LogErrorRequest(response, "Error while searching");
                throw new ApplicationException(response.GetErrorMessage(), response.OriginalException);
            }

            if (useSnapshotPaging)
            {
                result         = response.ToFindResults();
                result.HasMore = response.Hits.Count >= options.GetLimit();

                // clear the scroll
                if (!result.HasMore)
                {
                    var scrollId = result.GetScrollId();
                    if (!String.IsNullOrEmpty(scrollId))
                    {
                        await _client.ClearScrollAsync(s => s.ScrollId(result.GetScrollId()));
                    }
                }

                ((IGetNextPage <TResult>)result).GetNextPageFunc = GetNextPageFunc;
            }
            else
            {
                int limit = options.GetLimit();
                result         = response.ToFindResults(limit);
                result.HasMore = response.Hits.Count > limit || response.Hits.Count >= options.GetMaxLimit();
                ((IGetNextPage <TResult>)result).GetNextPageFunc = GetNextPageFunc;
            }

            if (options.HasSearchAfter())
            {
                result.SetSearchBeforeToken();
                if (result.HasMore)
                {
                    result.SetSearchAfterToken();
                }
            }
            else if (options.HasSearchBefore())
            {
                // reverse results
                bool hasMore = result.HasMore;
                result         = new FindResults <TResult>(result.Hits.Reverse(), result.Total, result.Aggregations.ToDictionary(k => k.Key, v => v.Value), GetNextPageFunc, result.Data.ToDictionary(k => k.Key, v => v.Value));
                result.HasMore = hasMore;

                result.SetSearchAfterToken();
                if (result.HasMore)
                {
                    result.SetSearchBeforeToken();
                }
            }
            else if (result.HasMore)
            {
                result.SetSearchAfterToken();
            }

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