protected ICollection <TModel> FindAs <TModel>(ElasticSearchOptions <T> options) where TModel : class, new()
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            ICollection <TModel> result = null;

            if (options.UseCache)
            {
                result = Cache.Get <ICollection <TModel> >(GetScopedCacheKey(options.CacheKey));
            }

            if (result != null)
            {
                return(result);
            }

            var searchDescriptor = new SearchDescriptor <T>().Filter(options.GetElasticSearchFilter());

            searchDescriptor.Indices(options.Indices);

            if (options.UsePaging)
            {
                searchDescriptor.Skip(options.GetSkip());
            }
            searchDescriptor.Size(options.GetLimit());
            searchDescriptor.Type(typeof(T));
            if (options.Fields.Count > 0)
            {
                searchDescriptor.Source(s => s.Include(options.Fields.ToArray()));
            }
            if (options.SortBy.Count > 0)
            {
                foreach (var sort in options.SortBy)
                {
                    searchDescriptor.Sort(sort);
                }
            }

            var results = _elasticClient.Search <T>(searchDescriptor);

            options.HasMore = options.UseLimit && results.Total > options.GetLimit();

            var items = results.Documents.ToList();

            if (typeof(T) != typeof(TModel))
            {
                if (Mapper.FindTypeMapFor <T, TModel>() == null)
                {
                    Mapper.CreateMap <T, TModel>();
                }

                result = items.Select(Mapper.Map <T, TModel>).ToList();
            }
            else
            {
                result = items as List <TModel>;
            }

            if (options.UseCache)
            {
                Cache.Set(GetScopedCacheKey(options.CacheKey), result, options.GetCacheExpirationDate());
            }

            return(result);
        }
        protected ICollection <TModel> FindAs <TModel>(ElasticSearchOptions <T> options) where TModel : class, new()
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            ICollection <TModel> result;

            if (EnableCache && options.UseCache)
            {
                result = Cache.Get <ICollection <TModel> >(GetScopedCacheKey(options.CacheKey));
                if (result != null)
                {
                    return(result);
                }
            }

            var searchDescriptor = new SearchDescriptor <T>().Filter(options.GetElasticSearchFilter());

            searchDescriptor.Indices(options.Indices);
            searchDescriptor.IgnoreUnavailable();

            if (options.UsePaging)
            {
                searchDescriptor.Skip(options.GetSkip());
            }
            searchDescriptor.Size(options.GetLimit());
            searchDescriptor.Type(typeof(T));
            if (options.Fields.Count > 0)
            {
                searchDescriptor.Source(s => s.Include(options.Fields.ToArray()));
            }
            else
            {
                searchDescriptor.Source(s => s.Exclude("idx"));
            }
            if (options.SortBy.Count > 0)
            {
                foreach (var sort in options.SortBy)
                {
                    searchDescriptor.Sort(sort);
                }
            }

            _elasticClient.EnableTrace();
            var results = _elasticClient.Search <T>(searchDescriptor);

            _elasticClient.DisableTrace();

            if (!results.IsValid)
            {
                throw new ApplicationException(String.Format("ElasticSearch error code \"{0}\".", results.ConnectionStatus.HttpStatusCode), results.ConnectionStatus.OriginalException);
            }

            options.HasMore = options.UseLimit && results.Total > options.GetLimit();

            var items = results.Documents.ToList();

            if (typeof(T) != typeof(TModel))
            {
                if (Mapper.FindTypeMapFor <T, TModel>() == null)
                {
                    Mapper.CreateMap <T, TModel>();
                }

                result = Enumerable.ToList(items.Select(Mapper.Map <T, TModel>));
            }
            else
            {
                result = items as List <TModel>;
            }

            if (EnableCache && options.UseCache)
            {
                Cache.Set(GetScopedCacheKey(options.CacheKey), result, options.GetCacheExpirationDate());
            }

            return(result);
        }
        protected async Task <FindResults <T> > FindAsync(ElasticSearchOptions <T> options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (EnableCache && options.UseCache)
            {
                var cacheValue = await Cache.GetAsync <FindResults <T> >(GetScopedCacheKey(options.CacheKey)).AnyContext();

#if DEBUG
                Logger.Trace().Message("Cache {0}: type={1}", cacheValue.HasValue ? "hit" : "miss", _entityType).Write();
#endif
                if (cacheValue.HasValue)
                {
                    return(cacheValue.Value);
                }
            }

            var searchDescriptor = new SearchDescriptor <T>();
            searchDescriptor.Query(options.GetElasticSearchQuery(_supportsSoftDeletes));
            searchDescriptor.Indices(options.Indices.Any() ? options.Indices.ToArray() : GetIndices());
            searchDescriptor.IgnoreUnavailable();
            searchDescriptor.Size(options.GetLimit());
            searchDescriptor.Type(typeof(T));

            if (options.UsePaging)
            {
                searchDescriptor.Skip(options.GetSkip());
            }

            if (options.Fields.Count > 0)
            {
                searchDescriptor.Source(s => s.Include(options.Fields.ToArray()));
            }
            else
            {
                searchDescriptor.Source(s => s.Exclude("idx"));
            }

            if (options.SortBy.Count > 0)
            {
                foreach (var sort in options.SortBy)
                {
                    searchDescriptor.Sort(sort);
                }
            }
#if DEBUG
            _elasticClient.EnableTrace();
            var sw = Stopwatch.StartNew();
#endif
            var results = await _elasticClient.SearchAsync <T>(searchDescriptor).AnyContext();

#if DEBUG
            sw.Stop();
            _elasticClient.DisableTrace();
            Logger.Trace().Message($"FindAsync: {sw.ElapsedMilliseconds}ms, Elastic Took {results.ElapsedMilliseconds}ms, Serialization Took {results.ConnectionStatus.Metrics.SerializationTime}ms, Deserialization Took {results.ConnectionStatus.Metrics.DeserializationTime}ms").Write();
#endif

            if (!results.IsValid)
            {
                throw new ApplicationException($"ElasticSearch error code \"{results.ConnectionStatus.HttpStatusCode}\".", results.ConnectionStatus.OriginalException);
            }

            options.HasMore = options.UseLimit && results.Total > options.GetLimit();

            var result = new FindResults <T> {
                Documents = results.Documents.ToList(),
                Total     = results.Total
            };

            if (EnableCache && options.UseCache)
            {
                await Cache.SetAsync(GetScopedCacheKey(options.CacheKey), result, options.GetCacheExpirationDate()).AnyContext();
            }

            return(result);
        }