public virtual async Task <bool> ExistsAsync(IRepositoryQuery query, ICommandOptions options = null) { options = ConfigureOptions(options.As <T>()); await OnBeforeQueryAsync(query, options, typeof(T)).AnyContext(); await RefreshForConsistency(query, options).AnyContext(); var searchDescriptor = (await CreateSearchDescriptorAsync(query, options).AnyContext()).Size(0); searchDescriptor.DocValueFields(_idField.Value); var response = await _client.SearchAsync <T>(searchDescriptor).AnyContext(); if (response.IsValid) { _logger.LogRequest(response, options.GetQueryLogLevel()); } else { if (response.ApiCall.HttpStatusCode.GetValueOrDefault() == 404) { return(false); } _logger.LogErrorRequest(response, "Error checking if document exists"); throw new ApplicationException(response.GetErrorMessage(), response.OriginalException); } return(response.HitsMetadata.Total.Value > 0); }
public virtual async Task <bool> ExistsAsync(Id id, ICommandOptions options = null) { if (String.IsNullOrEmpty(id.Value)) { return(false); } // documents that use soft deletes or have parents without a routing id need to use search for exists if (!SupportsSoftDeletes && (!HasParent || id.Routing != null)) { var response = await _client.DocumentExistsAsync(new DocumentPath <T>(id.Value), d => { d.Index(ElasticIndex.GetIndex(id)); if (id.Routing != null) { d.Routing(id.Routing); } return(d); }).AnyContext(); _logger.LogRequest(response, options.GetQueryLogLevel()); return(response.Exists); } return(await ExistsAsync(q => q.Id(id), o => options.As <T>()).AnyContext()); }
public virtual async Task <T> GetByIdAsync(Id id, ICommandOptions options = null) { if (String.IsNullOrEmpty(id.Value)) { return(null); } options = ConfigureOptions(options.As <T>()); if (IsCacheEnabled && options.HasCacheKey()) { throw new ArgumentException("Cache key can't be set when calling GetById"); } if (IsCacheEnabled && options.ShouldReadCache()) { var value = await GetCachedFindHit(id).AnyContext(); if (value?.Document != null) { _logger.LogTrace("Cache hit: type={EntityType} key={Id}", EntityTypeName, id); return(ShouldReturnDocument(value.Document, options) ? value.Document : null); } } FindHit <T> findHit; if (!HasParent || id.Routing != null) { var request = new GetRequest(ElasticIndex.GetIndex(id), id.Value); if (id.Routing != null) { request.Routing = id.Routing; } var response = await _client.GetAsync <T>(request).AnyContext(); _logger.LogRequest(response, options.GetQueryLogLevel()); findHit = response.Found ? response.ToFindHit() : null; } else { // we don't have the parent id so we have to do a query // TODO: Ensure this find one query is not cached. findHit = await FindOneAsync(NewQuery().Id(id), options.Clone().DefaultCacheKey(id)).AnyContext(); } if (IsCacheEnabled && options.ShouldUseCache()) { await AddDocumentsToCacheAsync(findHit ?? new FindHit <T>(id, null, 0), options).AnyContext(); } return(ShouldReturnDocument(findHit?.Document, options) ? findHit?.Document : null); }
public virtual async Task <CountResult> CountAsync(IRepositoryQuery query, ICommandOptions options = null) { options = ConfigureOptions(options.As <T>()); CountResult result; if (IsCacheEnabled && options.ShouldReadCache()) { result = await GetCachedQueryResultAsync <CountResult>(options, "count").AnyContext(); if (result != null) { return(result); } } await OnBeforeQueryAsync(query, options, typeof(T)).AnyContext(); await RefreshForConsistency(query, options).AnyContext(); var searchDescriptor = await CreateSearchDescriptorAsync(query, options).AnyContext(); searchDescriptor.Size(0); var response = await _client.SearchAsync <T>(searchDescriptor).AnyContext(); if (response.IsValid) { _logger.LogRequest(response, options.GetQueryLogLevel()); } else { if (response.ApiCall.HttpStatusCode.GetValueOrDefault() == 404) { return(new CountResult()); } _logger.LogErrorRequest(response, "Error getting document count"); throw new ApplicationException(response.GetErrorMessage(), response.OriginalException); } result = new CountResult(response.Total, response.ToAggregations()); if (IsCacheEnabled && options.ShouldUseCache()) { await SetCachedQueryResultAsync(options, result, "count").AnyContext(); } return(result); }
public virtual async Task <FindHit <T> > FindOneAsync(IRepositoryQuery query, ICommandOptions options = null) { options = ConfigureOptions(options.As <T>()); if (IsCacheEnabled && (options.ShouldUseCache() || options.ShouldReadCache()) && !options.HasCacheKey()) { throw new ArgumentException("Cache key is required when enabling cache.", nameof(options)); } var result = IsCacheEnabled && options.ShouldReadCache() && options.HasCacheKey() ? await GetCachedFindHit(options).AnyContext() : null; if (result != null) { return(result.FirstOrDefault()); } await OnBeforeQueryAsync(query, options, typeof(T)).AnyContext(); await RefreshForConsistency(query, options).AnyContext(); var searchDescriptor = (await CreateSearchDescriptorAsync(query, options).AnyContext()).Size(1); var response = await _client.SearchAsync <T>(searchDescriptor).AnyContext(); if (response.IsValid) { _logger.LogRequest(response, options.GetQueryLogLevel()); } else { if (response.ApiCall.HttpStatusCode.GetValueOrDefault() == 404) { return(FindHit <T> .Empty); } _logger.LogErrorRequest(response, "Error while finding document"); throw new ApplicationException(response.GetErrorMessage(), response.OriginalException); } result = response.Hits.Select(h => h.ToFindHit()).ToList(); if (IsCacheEnabled && options.ShouldUseCache()) { await AddDocumentsToCacheAsync(result, options).AnyContext(); } return(result.FirstOrDefault()); }
public virtual Task <CountResult> CountBySearchAsync(ISystemFilter systemFilter, string filter = null, string aggregations = null, ICommandOptions options = null) { return(CountAsync(q => q.SystemFilter(systemFilter).FilterExpression(filter).AggregationsExpression(aggregations), o => options.As <T>())); }
public virtual Task <FindResults <T> > SearchAsync(ISystemFilter systemFilter, string filter = null, string criteria = null, string sort = null, string aggregations = null, ICommandOptions options = null) { return(FindAsAsync <T>(q => q.SystemFilter(systemFilter).FilterExpression(filter).SearchExpression(criteria).SortExpression(sort).AggregationsExpression(aggregations), o => options.As <T>())); }
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); }
public virtual async Task <IReadOnlyCollection <T> > GetByIdsAsync(Ids ids, ICommandOptions options = null) { var idList = ids?.Distinct().Where(i => !String.IsNullOrEmpty(i)).ToList(); if (idList == null || idList.Count == 0) { return(EmptyList); } if (!HasIdentity) { throw new NotSupportedException("Model type must implement IIdentity."); } options = ConfigureOptions(options.As <T>()); if (IsCacheEnabled && options.HasCacheKey()) { throw new ArgumentException("Cache key can't be set when calling GetByIds"); } var hits = new List <FindHit <T> >(); if (IsCacheEnabled && options.ShouldReadCache()) { hits.AddRange(await GetCachedFindHit(idList).AnyContext()); } var itemsToFind = idList.Except(hits.Select(i => (Id)i.Id)).ToList(); if (itemsToFind.Count == 0) { return(hits.Where(h => h.Document != null && ShouldReturnDocument(h.Document, options)).Select(h => h.Document).ToList().AsReadOnly()); } var multiGet = new MultiGetDescriptor(); foreach (var id in itemsToFind.Where(i => i.Routing != null || !HasParent)) { multiGet.Get <T>(f => { f.Id(id.Value).Index(ElasticIndex.GetIndex(id)); if (id.Routing != null) { f.Routing(id.Routing); } return(f); }); } var multiGetResults = await _client.MultiGetAsync(multiGet).AnyContext(); _logger.LogRequest(multiGetResults, options.GetQueryLogLevel()); foreach (var doc in multiGetResults.Hits) { hits.Add(((IMultiGetHit <T>)doc).ToFindHit()); itemsToFind.Remove(new Id(doc.Id, doc.Routing)); } // fallback to doing a find if (itemsToFind.Count > 0 && (HasParent || ElasticIndex.HasMultipleIndexes)) { var response = await FindAsync(q => q.Id(itemsToFind.Select(id => id.Value)), o => o.PageLimit(1000)).AnyContext(); do { if (response.Hits.Count > 0) { hits.AddRange(response.Hits.Where(h => h.Document != null)); } } while (await response.NextPageAsync().AnyContext()); } if (IsCacheEnabled && options.ShouldUseCache()) { await AddDocumentsToCacheAsync(hits, options).AnyContext(); } return(hits.Where(h => h.Document != null && ShouldReturnDocument(h.Document, options)).Select(h => h.Document).ToList().AsReadOnly()); }