public async Task <NumbersTimelineStatsResult> GetNumbersTimelineStatsAsync(IEnumerable <FieldAggregation> fields, DateTime utcStart, DateTime utcEnd, string systemFilter, string userFilter = null, TimeSpan?displayTimeOffset = null, int desiredDataPoints = 100) { if (!displayTimeOffset.HasValue) { displayTimeOffset = TimeSpan.Zero; } var filter = new ElasticQuery() .WithSystemFilter(systemFilter) .WithFilter(userFilter) .WithDateRange(utcStart, utcEnd, EventIndex.Fields.PersistentEvent.Date) .WithIndices(utcStart, utcEnd, $"'{_eventIndex.VersionedName}-'yyyyMM"); // if no start date then figure out first event date if (!filter.DateRanges.First().UseStartDate) { await UpdateFilterStartDateRangesAsync(filter, utcEnd).AnyContext(); } utcStart = filter.DateRanges.First().GetStartDate(); utcEnd = filter.DateRanges.First().GetEndDate(); var interval = GetInterval(utcStart, utcEnd, desiredDataPoints); var response = await _elasticClient.SearchAsync <PersistentEvent>(s => s .SearchType(SearchType.Count) .IgnoreUnavailable() .Index(filter.Indices.Count > 0 ? String.Join(",", filter.Indices) : _eventIndex.AliasName) .Query(_queryBuilder.BuildQuery <PersistentEvent>(filter)) .Aggregations(agg => BuildAggregations(agg .DateHistogram("timelime", t => t .Field(ev => ev.Date) .MinimumDocumentCount(0) .Interval(interval.Item1) .TimeZone(HoursAndMinutes(displayTimeOffset.Value)) .Aggregations(agg2 => BuildAggregations(agg2, fields)) ) .Min("first_occurrence", t => t.Field(ev => ev.Date)) .Max("last_occurrence", t => t.Field(ev => ev.Date)), fields) ) ).AnyContext(); if (!response.IsValid) { _logger.Error("Retrieving stats failed: {0}", response.ServerError.Error); throw new ApplicationException("Retrieving stats failed."); } var stats = new NumbersTimelineStatsResult { Total = response.Total, Numbers = GetNumbers(response.Aggs, fields) }; var timeline = response.Aggs.DateHistogram("timelime"); if (timeline != null) { stats.Timeline.AddRange(timeline.Items.Select(i => new NumbersTimelineItem { Date = i.Date, Total = i.DocCount, Numbers = GetNumbers(i, fields) })); } stats.Start = stats.Timeline.Count > 0 ? stats.Timeline.Min(tl => tl.Date).SafeAdd(displayTimeOffset.Value) : utcStart.SafeAdd(displayTimeOffset.Value); stats.End = utcEnd.SafeAdd(displayTimeOffset.Value); var totalHours = stats.End.Subtract(stats.Start).TotalHours; if (totalHours > 0.0) { stats.AvgPerHour = stats.Total / totalHours; } if (stats.Timeline.Count <= 0) { return(stats); } var firstOccurrence = response.Aggs.Min("first_occurrence"); if (firstOccurrence?.Value != null) { stats.FirstOccurrence = firstOccurrence.Value.Value.ToDateTime().SafeAdd(displayTimeOffset.Value); } var lastOccurrence = response.Aggs.Max("last_occurrence"); if (lastOccurrence?.Value != null) { stats.LastOccurrence = lastOccurrence.Value.Value.ToDateTime().SafeAdd(displayTimeOffset.Value); } return(stats); }
public async Task <NumbersTimelineStatsResult> GetNumbersTimelineStatsAsync(IEnumerable <FieldAggregation> fields, DateTime utcStart, DateTime utcEnd, IExceptionlessSystemFilterQuery systemFilter, string userFilter = null, TimeSpan?displayTimeOffset = null, int desiredDataPoints = 100) { if (!displayTimeOffset.HasValue) { displayTimeOffset = TimeSpan.Zero; } var filter = new ElasticQuery() .WithSystemFilter(systemFilter) .WithFilter(userFilter) .WithDateRange(utcStart, utcEnd, EventIndexType.Fields.Date) .WithIndexes(utcStart, utcEnd); // if no start date then figure out first event date if (!filter.DateRanges.First().UseStartDate) { await UpdateFilterStartDateRangesAsync(filter, utcEnd).AnyContext(); } utcStart = filter.DateRanges.First().GetStartDate(); utcEnd = filter.DateRanges.First().GetEndDate(); var interval = GetInterval(utcStart, utcEnd, desiredDataPoints); var descriptor = new SearchDescriptor <PersistentEvent>() .SearchType(SearchType.Count) .IgnoreUnavailable() .Indices(_configuration.Events.Event.GetIndexesByQuery(filter)) .Type(_configuration.Events.Event.Name) .Aggregations(agg => BuildAggregations(agg .DateHistogram("timelime", t => t .Field(ev => ev.Date) .MinimumDocumentCount(0) .Interval(interval.Item1) .TimeZone(HoursAndMinutes(displayTimeOffset.Value)) .Aggregations(agg2 => BuildAggregations(agg2, fields)) ) .Min("first_occurrence", t => t.Field(ev => ev.Date)) .Max("last_occurrence", t => t.Field(ev => ev.Date)), fields) ); _configuration.Events.Event.QueryBuilder.ConfigureSearch(filter, GetQueryOptions(), descriptor); var response = await _configuration.Client.SearchAsync <PersistentEvent>(descriptor).AnyContext(); _logger.Trace(() => response.GetRequest()); if (!response.IsValid) { string message = $"Retrieving stats failed: {response.GetErrorMessage()}"; _logger.Error().Exception(response.ConnectionStatus.OriginalException).Message(message).Property("request", response.GetRequest()).Write(); throw new ApplicationException(message, response.ConnectionStatus.OriginalException); } var stats = new NumbersTimelineStatsResult { Total = response.Total, Numbers = GetNumbers(response.Aggs, fields) }; var timeline = response.Aggs.DateHistogram("timelime"); if (timeline != null) { stats.Timeline.AddRange(timeline.Items.Select(i => new NumbersTimelineItem { Date = i.Date, Total = i.DocCount, Numbers = GetNumbers(i, fields) })); } stats.Start = stats.Timeline.Count > 0 ? stats.Timeline.Min(tl => tl.Date).SafeAdd(displayTimeOffset.Value) : utcStart.SafeAdd(displayTimeOffset.Value); stats.End = utcEnd.SafeAdd(displayTimeOffset.Value); var totalHours = stats.End.Subtract(stats.Start).TotalHours; if (totalHours > 0.0) { stats.AvgPerHour = stats.Total / totalHours; } if (stats.Timeline.Count <= 0) { return(stats); } var firstOccurrence = response.Aggs.Min("first_occurrence"); if (firstOccurrence?.Value != null) { stats.FirstOccurrence = firstOccurrence.Value.Value.ToDateTime().SafeAdd(displayTimeOffset.Value); } var lastOccurrence = response.Aggs.Max("last_occurrence"); if (lastOccurrence?.Value != null) { stats.LastOccurrence = lastOccurrence.Value.Value.ToDateTime().SafeAdd(displayTimeOffset.Value); } return(stats); }