From 7bbc856e025a159ebb8cb2394e72ff3b9ca26b9d Mon Sep 17 00:00:00 2001 From: Russ Cam Date: Wed, 15 Jul 2020 10:58:55 +1000 Subject: [PATCH] Fix DateHistogram aggregation intervals (#4856) This commit fixes FixedInterval and CalendarInterval on DateHistogram aggregation to 1. For FixedInterval to accept only Time 2. For CalendarInterval to accept DateInterval or DateMathTime This aligns with DateHistogramCompositeAggregationSource. Fixes #4839 --- .../date-histogram-aggregation-usage.asciidoc | 6 +- .../DateHistogram/DateHistogramAggregation.cs | 100 ++++++++++++++---- .../DateHistogramAggregationUsageTests.cs | 20 ++-- 3 files changed, 91 insertions(+), 35 deletions(-) diff --git a/docs/aggregations/bucket/date-histogram/date-histogram-aggregation-usage.asciidoc b/docs/aggregations/bucket/date-histogram/date-histogram-aggregation-usage.asciidoc index fbd277d2e21..6ea218a7fa3 100644 --- a/docs/aggregations/bucket/date-histogram/date-histogram-aggregation-usage.asciidoc +++ b/docs/aggregations/bucket/date-histogram/date-histogram-aggregation-usage.asciidoc @@ -32,7 +32,7 @@ Be sure to read the Elasticsearch documentation on {ref_current}/search-aggregat a => a .DateHistogram("projects_started_per_month", date => date .Field(p => p.StartedOn) - .Interval(DateInterval.Month) + .CalendarInterval(DateInterval.Month) .MinimumDocumentCount(2) .Format("yyyy-MM-dd'T'HH:mm:ss") .ExtendedBounds(FixedDate.AddYears(-1), FixedDate.AddYears(1)) @@ -56,7 +56,7 @@ a => a new DateHistogramAggregation("projects_started_per_month") { Field = Field(p => p.StartedOn), - Interval = DateInterval.Month, + CalendarInterval = DateInterval.Month, MinimumDocumentCount = 2, Format = "yyyy-MM-dd'T'HH:mm:ss", ExtendedBounds = new ExtendedBounds @@ -84,7 +84,7 @@ new DateHistogramAggregation("projects_started_per_month") "projects_started_per_month": { "date_histogram": { "field": "startedOn", - "interval": "month", + "calendar_interval": "month", "min_doc_count": 2, "format": "yyyy-MM-dd'T'HH:mm:ss||date_optional_time", "order": { diff --git a/src/Nest/Aggregations/Bucket/DateHistogram/DateHistogramAggregation.cs b/src/Nest/Aggregations/Bucket/DateHistogram/DateHistogramAggregation.cs index 20e7672de82..474d95fd584 100644 --- a/src/Nest/Aggregations/Bucket/DateHistogram/DateHistogramAggregation.cs +++ b/src/Nest/Aggregations/Bucket/DateHistogram/DateHistogramAggregation.cs @@ -14,12 +14,25 @@ namespace Nest [ReadAs(typeof(DateHistogramAggregation))] public interface IDateHistogramAggregation : IBucketAggregation { + /// + /// Extend the bounds of the date histogram beyond the data itself, + /// forcing the aggregation to start building buckets on + /// a specific min and/or max value. + /// Using extended bounds only makes sense when is 0 + /// as empty buckets will never be returned if it is greater than 0. + /// [DataMember(Name ="extended_bounds")] ExtendedBounds ExtendedBounds { get; set; } + /// + /// The field to target + /// [DataMember(Name ="field")] Field Field { get; set; } + /// + /// Return a formatted date string as the key instead an epoch long + /// [DataMember(Name ="format")] string Format { get; set; } @@ -27,30 +40,55 @@ public interface IDateHistogramAggregation : IBucketAggregation [DataMember(Name ="interval")] Union Interval { get; set; } + /// + /// The calendar interval to use when bucketing documents + /// [DataMember(Name ="calendar_interval")] - Union CalendarInterval { get; set; } + Union CalendarInterval { get; set; } + /// + /// The fixed interval to use when bucketing documents + /// [DataMember(Name ="fixed_interval")] - Union FixedInterval { get; set; } + Time FixedInterval { get; set; } + /// + /// The minimum number of documents that a bucket must contain to be returned in the response. + /// The default is 0 meaning that buckets with no documents will be returned. + /// [DataMember(Name ="min_doc_count")] int? MinimumDocumentCount { get; set; } + /// + /// Defines how to treat documents that are missing a value. By default, they are ignored, + /// but it is also possible to treat them as if they have a value. + /// [DataMember(Name ="missing")] DateTime? Missing { get; set; } + /// + /// Change the start value of each bucket by the specified positive (+) or negative offset (-) duration, + /// such as 1h for an hour, or 1d for a day. + /// [DataMember(Name ="offset")] string Offset { get; set; } + /// + /// Defines an order in which returned buckets are sorted. + /// By default the returned buckets are sorted by their key ascending. + /// [DataMember(Name ="order")] HistogramOrder Order { get; set; } - [DataMember(Name ="params")] - IDictionary Params { get; set; } - + /// [DataMember(Name ="script")] IScript Script { get; set; } + /// + /// Used to indicate that bucketing should use a different time zone. + /// Time zones may either be specified as an ISO 8601 UTC offset (e.g. +01:00 or -08:00) + /// or as a timezone id, an identifier used in the TZ database like America/Los_Angeles. + /// [DataMember(Name ="time_zone")] string TimeZone { get; set; } } @@ -63,9 +101,12 @@ internal DateHistogramAggregation() { } public DateHistogramAggregation(string name) : base(name) { } + /// public ExtendedBounds ExtendedBounds { get; set; } + /// public Field Field { get; set; } + /// public string Format { get => !string.IsNullOrEmpty(_format) && @@ -79,15 +120,21 @@ public string Format [Obsolete("Deprecated in version 7.2.0, use CalendarInterval or FixedInterval instead")] public Union Interval { get; set; } - public Union CalendarInterval { get; set; } - public Union FixedInterval { get; set; } - + /// + public Union CalendarInterval { get; set; } + /// + public Time FixedInterval { get; set; } + /// public int? MinimumDocumentCount { get; set; } + /// public DateTime? Missing { get; set; } + /// public string Offset { get; set; } + /// public HistogramOrder Order { get; set; } - public IDictionary Params { get; set; } + /// public IScript Script { get; set; } + /// public string TimeZone { get; set; } internal override void WrapInContainer(AggregationContainer c) => c.DateHistogram = this; @@ -116,29 +163,25 @@ string IDateHistogramAggregation.Format [Obsolete("Deprecated in version 7.2.0, use CalendarInterval or FixedInterval instead")] Union IDateHistogramAggregation.Interval { get; set; } - Union IDateHistogramAggregation.CalendarInterval { get; set; } - Union IDateHistogramAggregation.FixedInterval { get; set; } - + Union IDateHistogramAggregation.CalendarInterval { get; set; } + Time IDateHistogramAggregation.FixedInterval { get; set; } int? IDateHistogramAggregation.MinimumDocumentCount { get; set; } - DateTime? IDateHistogramAggregation.Missing { get; set; } - string IDateHistogramAggregation.Offset { get; set; } - HistogramOrder IDateHistogramAggregation.Order { get; set; } - - IDictionary IDateHistogramAggregation.Params { get; set; } - IScript IDateHistogramAggregation.Script { get; set; } - string IDateHistogramAggregation.TimeZone { get; set; } + /// public DateHistogramAggregationDescriptor Field(Field field) => Assign(field, (a, v) => a.Field = v); + /// public DateHistogramAggregationDescriptor Field(Expression> field) => Assign(field, (a, v) => a.Field = v); + /// public DateHistogramAggregationDescriptor Script(string script) => Assign((InlineScript)script, (a, v) => a.Script = v); + /// public DateHistogramAggregationDescriptor Script(Func scriptSelector) => Assign(scriptSelector, (a, v) => a.Script = v?.Invoke(new ScriptDescriptor())); @@ -149,31 +192,44 @@ public DateHistogramAggregationDescriptor Script(Func Interval(DateInterval interval) => Assign(interval, (a, v) => a.Interval = v); - public DateHistogramAggregationDescriptor CalendarInterval(Time interval) => Assign(interval, (a, v) => a.CalendarInterval = v); - public DateHistogramAggregationDescriptor CalendarInterval(DateInterval interval) => Assign(interval, (a, v) => a.CalendarInterval = v); + /// + public DateHistogramAggregationDescriptor CalendarInterval(DateMathTime interval) => Assign(interval, (a, v) => a.CalendarInterval = v); + + /// + public DateHistogramAggregationDescriptor CalendarInterval(DateInterval? interval) => Assign(interval, (a, v) => a.CalendarInterval = v); + + /// public DateHistogramAggregationDescriptor FixedInterval(Time interval) => Assign(interval, (a, v) => a.FixedInterval = v); - public DateHistogramAggregationDescriptor FixedInterval(DateInterval interval) => Assign(interval, (a, v) => a.FixedInterval = v); + /// public DateHistogramAggregationDescriptor Format(string format) => Assign(format, (a, v) => a.Format = v); + /// public DateHistogramAggregationDescriptor MinimumDocumentCount(int? minimumDocumentCount) => Assign(minimumDocumentCount, (a, v) => a.MinimumDocumentCount = v); + /// public DateHistogramAggregationDescriptor TimeZone(string timeZone) => Assign(timeZone, (a, v) => a.TimeZone = v); + /// public DateHistogramAggregationDescriptor Offset(string offset) => Assign(offset, (a, v) => a.Offset = v); + /// public DateHistogramAggregationDescriptor Order(HistogramOrder order) => Assign(order, (a, v) => a.Order = v); + /// public DateHistogramAggregationDescriptor OrderAscending(string key) => Assign(new HistogramOrder { Key = key, Order = SortOrder.Descending }, (a, v) => a.Order = v); + /// public DateHistogramAggregationDescriptor OrderDescending(string key) => Assign(new HistogramOrder { Key = key, Order = SortOrder.Descending }, (a, v) => a.Order = v); + /// public DateHistogramAggregationDescriptor ExtendedBounds(DateMath min, DateMath max) => Assign(new ExtendedBounds { Minimum = min, Maximum = max }, (a, v) => a.ExtendedBounds = v); + /// public DateHistogramAggregationDescriptor Missing(DateTime? missing) => Assign(missing, (a, v) => a.Missing = v); } } diff --git a/tests/Tests/Aggregations/Bucket/DateHistogram/DateHistogramAggregationUsageTests.cs b/tests/Tests/Aggregations/Bucket/DateHistogram/DateHistogramAggregationUsageTests.cs index 28e6c48d7e6..2381500c6e9 100644 --- a/tests/Tests/Aggregations/Bucket/DateHistogram/DateHistogramAggregationUsageTests.cs +++ b/tests/Tests/Aggregations/Bucket/DateHistogram/DateHistogramAggregationUsageTests.cs @@ -37,7 +37,7 @@ public DateHistogramAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage date_histogram = new { field = "startedOn", - interval = "month", + calendar_interval = "month", min_doc_count = 2, format = "yyyy-MM-dd'T'HH:mm:ss||date_optional_time", //<1> Note the inclusion of `date_optional_time` to `format` order = new { _count = "asc" }, @@ -72,7 +72,7 @@ public DateHistogramAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage protected override Func, IAggregationContainer> FluentAggs => a => a .DateHistogram("projects_started_per_month", date => date .Field(p => p.StartedOn) - .Interval(DateInterval.Month) + .CalendarInterval(DateInterval.Month) .MinimumDocumentCount(2) .Format("yyyy-MM-dd'T'HH:mm:ss") .ExtendedBounds(FixedDate.AddYears(-1), FixedDate.AddYears(1)) @@ -92,7 +92,7 @@ public DateHistogramAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage new DateHistogramAggregation("projects_started_per_month") { Field = Field(p => p.StartedOn), - Interval = DateInterval.Month, + CalendarInterval = DateInterval.Month, MinimumDocumentCount = 2, Format = "yyyy-MM-dd'T'HH:mm:ss", ExtendedBounds = new ExtendedBounds @@ -147,12 +147,12 @@ public DateHistogramAggregationNoSubAggregationsUsageTests(ReadOnlyCluster i, En protected override object AggregationJson => new { - projects_started_per_month = new + projects_started_per_four_weeks = new { date_histogram = new { field = "startedOn", - interval = "month", + fixed_interval = "28d", min_doc_count = 2, format = "yyyy-MM-dd'T'HH:mm:ss||date_optional_time", order = new { _count = "asc" }, @@ -168,9 +168,9 @@ public DateHistogramAggregationNoSubAggregationsUsageTests(ReadOnlyCluster i, En #pragma warning disable 618, 612 protected override Func, IAggregationContainer> FluentAggs => a => a - .DateHistogram("projects_started_per_month", date => date + .DateHistogram("projects_started_per_four_weeks", date => date .Field(p => p.StartedOn) - .Interval(DateInterval.Month) + .FixedInterval(new Time(28, TimeUnit.Day)) .MinimumDocumentCount(2) .Format("yyyy-MM-dd'T'HH:mm:ss") .ExtendedBounds(FixedDate.AddYears(-1), FixedDate.AddYears(1)) @@ -179,10 +179,10 @@ public DateHistogramAggregationNoSubAggregationsUsageTests(ReadOnlyCluster i, En ); protected override AggregationDictionary InitializerAggs => - new DateHistogramAggregation("projects_started_per_month") + new DateHistogramAggregation("projects_started_per_four_weeks") { Field = Field(p => p.StartedOn), - Interval = DateInterval.Month, + FixedInterval = new Time(28, TimeUnit.Day), MinimumDocumentCount = 2, Format = "yyyy-MM-dd'T'HH:mm:ss", ExtendedBounds = new ExtendedBounds @@ -198,7 +198,7 @@ public DateHistogramAggregationNoSubAggregationsUsageTests(ReadOnlyCluster i, En protected override void ExpectResponse(ISearchResponse response) { response.ShouldBeValid(); - var dateHistogram = response.Aggregations.DateHistogram("projects_started_per_month"); + var dateHistogram = response.Aggregations.DateHistogram("projects_started_per_four_weeks"); dateHistogram.Should().NotBeNull(); dateHistogram.Buckets.Should().NotBeNull(); dateHistogram.Buckets.Count.Should().BeGreaterThan(10);