Skip to content

Commit

Permalink
Add support for the auto_date_histogram aggregation elastic/elasticse…
Browse files Browse the repository at this point in the history
  • Loading branch information
Mpdreamz committed Jan 2, 2019
1 parent c7f462c commit a830b20
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/Nest/Aggregations/AggregateDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,20 @@ public TermsAggregate<TKey> Terms<TKey>(string key)

public MultiBucketAggregate<DateHistogramBucket> DateHistogram(string key) => GetMultiBucketAggregate<DateHistogramBucket>(key);

public AutoDateHistogramAggregate AutoDateHistogram(string key)
{
var bucket = TryGet<BucketAggregate>(key);
if (bucket == null) return null;

return new AutoDateHistogramAggregate
{
Buckets = bucket.Items.OfType<DateHistogramBucket>().ToList(),
Meta = bucket.Meta,
Interval = bucket.Interval
};
}


public CompositeBucketAggregate Composite(string key)
{
var bucket = TryGet<BucketAggregate>(key);
Expand Down
7 changes: 7 additions & 0 deletions src/Nest/Aggregations/AggregateJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,12 @@ private IAggregate GetMultiBucketAggregate(JsonReader reader, JsonSerializer ser
} while (reader.TokenType != JsonToken.EndArray);
bucket.Items = items;
reader.Read();
if (reader.TokenType == JsonToken.PropertyName && (string)reader.Value == Parser.Interval)
{
var interval = reader.ReadAsString();
bucket.Interval = new Time(interval);
}

return bucket;
}

Expand Down Expand Up @@ -755,6 +761,7 @@ private static class Parser
public const string DocCountErrorUpperBound = "doc_count_error_upper_bound";
public const string Fields = "fields";
public const string From = "from";
public const string Interval = "interval";

public const string FromAsString = "from_as_string";
public const string Hits = "hits";
Expand Down
14 changes: 14 additions & 0 deletions src/Nest/Aggregations/AggregationContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public interface IAggregationContainer
[JsonProperty("date_histogram")]
IDateHistogramAggregation DateHistogram { get; set; }

[JsonProperty("auto_date_histogram")]
IAutoDateHistogramAggregation AutoDateHistogram { get; set; }

[JsonProperty("date_range")]
IDateRangeAggregation DateRange { get; set; }

Expand Down Expand Up @@ -253,11 +256,15 @@ public class AggregationContainer : IAggregationContainer
public ICompositeAggregation Composite { get; set; }

public ICumulativeSumAggregation CumulativeSum { get; set; }

public IDateHistogramAggregation DateHistogram { get; set; }

public IAutoDateHistogramAggregation AutoDateHistogram { get; set; }

public IDateRangeAggregation DateRange { get; set; }

public IDerivativeAggregation Derivative { get; set; }

public IExtendedStatsAggregation ExtendedStats { get; set; }

public IExtendedStatsBucketAggregation ExtendedStatsBucket { get; set; }
Expand Down Expand Up @@ -386,6 +393,8 @@ public class AggregationContainerDescriptor<T> : DescriptorBase<AggregationConta

IDateHistogramAggregation IAggregationContainer.DateHistogram { get; set; }

IAutoDateHistogramAggregation IAggregationContainer.AutoDateHistogram { get; set; }

IDateRangeAggregation IAggregationContainer.DateRange { get; set; }

IDerivativeAggregation IAggregationContainer.Derivative { get; set; }
Expand Down Expand Up @@ -483,6 +492,11 @@ Func<DateHistogramAggregationDescriptor<T>, IDateHistogramAggregation> selector
) =>
_SetInnerAggregation(name, selector, (a, d) => a.DateHistogram = d);

public AggregationContainerDescriptor<T> AutoDateHistogram(string name,
Func<AutoDateHistogramAggregationDescriptor<T>, IAutoDateHistogramAggregation> selector
) =>
_SetInnerAggregation(name, selector, (a, d) => a.AutoDateHistogram = d);

public AggregationContainerDescriptor<T> Percentiles(string name,
Func<PercentilesAggregationDescriptor<T>, IPercentilesAggregation> selector
) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Newtonsoft.Json;

namespace Nest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[ContractJsonConverter(typeof(AggregationJsonConverter<AutoDateHistogramAggregation>))]
public interface IAutoDateHistogramAggregation : IBucketAggregation
{
[JsonProperty("field")]
Field Field { get; set; }

[JsonProperty("format")]
string Format { get; set; }

[JsonProperty("missing")]
DateTime? Missing { get; set; }

[JsonProperty("offset")]
string Offset { get; set; }

[JsonProperty("params")]
IDictionary<string, object> Params { get; set; }

[JsonProperty("script")]
IScript Script { get; set; }

[JsonProperty("time_zone")]
string TimeZone { get; set; }
}

public class AutoDateHistogramAggregation : BucketAggregationBase, IAutoDateHistogramAggregation
{
private string _format;

internal AutoDateHistogramAggregation() { }

public AutoDateHistogramAggregation(string name) : base(name) { }

public Field Field { get; set; }

//see: https://github.com/elastic/elasticsearch/issues/9725
public string Format
{
get => !string.IsNullOrEmpty(_format) &&
!_format.Contains("date_optional_time") &&
(Missing.HasValue)
? _format + "||date_optional_time"
: _format;
set => _format = value;
}

public DateTime? Missing { get; set; }
public string Offset { get; set; }
public IDictionary<string, object> Params { get; set; }
public IScript Script { get; set; }
public string TimeZone { get; set; }

internal override void WrapInContainer(AggregationContainer c) => c.AutoDateHistogram = this;
}

public class AutoDateHistogramAggregationDescriptor<T>
: BucketAggregationDescriptorBase<AutoDateHistogramAggregationDescriptor<T>, IAutoDateHistogramAggregation, T>
, IAutoDateHistogramAggregation
where T : class
{
private string _format;

Field IAutoDateHistogramAggregation.Field { get; set; }

//see: https://github.com/elastic/elasticsearch/issues/9725
string IAutoDateHistogramAggregation.Format
{
get => !string.IsNullOrEmpty(_format) &&
!_format.Contains("date_optional_time") &&
(Self.Missing.HasValue)
? _format + "||date_optional_time"
: _format;
set => _format = value;
}

DateTime? IAutoDateHistogramAggregation.Missing { get; set; }

string IAutoDateHistogramAggregation.Offset { get; set; }

IDictionary<string, object> IAutoDateHistogramAggregation.Params { get; set; }

IScript IAutoDateHistogramAggregation.Script { get; set; }

string IAutoDateHistogramAggregation.TimeZone { get; set; }

public AutoDateHistogramAggregationDescriptor<T> Field(Field field) => Assign(a => a.Field = field);

public AutoDateHistogramAggregationDescriptor<T> Field(Expression<Func<T, object>> field) => Assign(a => a.Field = field);

public AutoDateHistogramAggregationDescriptor<T> Script(string script) => Assign(a => a.Script = (InlineScript)script);

public AutoDateHistogramAggregationDescriptor<T> Script(Func<ScriptDescriptor, IScript> scriptSelector) =>
Assign(a => a.Script = scriptSelector?.Invoke(new ScriptDescriptor()));

public AutoDateHistogramAggregationDescriptor<T> Format(string format) => Assign(a => a.Format = format);

public AutoDateHistogramAggregationDescriptor<T> TimeZone(string timeZone) => Assign(a => a.TimeZone = timeZone);

public AutoDateHistogramAggregationDescriptor<T> Offset(string offset) => Assign(a => a.Offset = offset);

public AutoDateHistogramAggregationDescriptor<T> Missing(DateTime? missing) => Assign(a => a.Missing = missing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Nest
{
public class AutoDateHistogramAggregate : MultiBucketAggregate<DateHistogramBucket>
{
public Time Interval { get; internal set; }
}
}
1 change: 1 addition & 0 deletions src/Nest/Aggregations/Bucket/BucketAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@ public class BucketAggregate : IAggregate
public IReadOnlyCollection<IBucket> Items { get; set; } = EmptyReadOnly<IBucket>.Collection;
public IReadOnlyDictionary<string, object> Meta { get; set; } = EmptyReadOnly<string, object>.Dictionary;
public long? SumOtherDocCount { get; set; }
public Time Interval { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class DateHistogramAggregationDescriptor<T>
ExtendedBounds<DateMath> IDateHistogramAggregation.ExtendedBounds { get; set; }
Field IDateHistogramAggregation.Field { get; set; }

//see: https://github.com/elastic/elasticsearch/issues/9725
string IDateHistogramAggregation.Format
{
get => !string.IsNullOrEmpty(_format) &&
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
using System;
using System.Linq;
using FluentAssertions;
using Nest;
using Tests.Core.Extensions;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Domain;
using Tests.Framework.Integration;
using static Nest.Infer;
using static Tests.Domain.Helpers.TestValueHelper;

namespace Tests.Aggregations.Bucket.AutoDateHistogram
{
/**
* A multi-bucket aggregation similar to the Date Histogram Aggregation except instead of providing an interval to
* use as the width of each bucket, a target number of buckets is provided indicating the number of buckets needed
* and the interval of the buckets is automatically chosen to best achieve that target. The number of buckets
* returned will always be less than or equal to this target number.
*
* NOTE: When specifying a `format` **and** `extended_bounds` or `missing`, in order for Elasticsearch to be able to parse
* the serialized `DateTime` of `extended_bounds` or `missing` correctly, the `date_optional_time` format is included
* as part of the `format` value.
*
* Be sure to read the Elasticsearch documentation on {ref_current}/search-aggregations-bucket-autodatehistogram-aggregation.html[Auto Date Histogram Aggregation].
*/
public class AutoDateHistogramAggregationUsageTests : ProjectsOnlyAggregationUsageTestBase
{
public AutoDateHistogramAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

protected override object AggregationJson => new
{
projects_started_per_month = new
{
auto_date_histogram = new
{
field = "startedOn",
format = "yyyy-MM-dd'T'HH:mm:ss||date_optional_time", //<1> Note the inclusion of `date_optional_time` to `format`
missing = FixedDate
},
aggs = new
{
project_tags = new
{
nested = new
{
path = "tags"
},
aggs = new
{
tags = new
{
terms = new { field = "tags.name" }
}
}
}
}
}
};

protected override Func<AggregationContainerDescriptor<Project>, IAggregationContainer> FluentAggs => a => a
.AutoDateHistogram("projects_started_per_month", date => date
.Field(p => p.StartedOn)
.Format("yyyy-MM-dd'T'HH:mm:ss")
.Missing(FixedDate)
.Aggregations(childAggs => childAggs
.Nested("project_tags", n => n
.Path(p => p.Tags)
.Aggregations(nestedAggs => nestedAggs
.Terms("tags", avg => avg.Field(p => p.Tags.First().Name))
)
)
)
);

protected override AggregationDictionary InitializerAggs =>
new AutoDateHistogramAggregation("projects_started_per_month")
{
Field = Field<Project>(p => p.StartedOn),
Format = "yyyy-MM-dd'T'HH:mm:ss",
Missing = FixedDate,
Aggregations = new NestedAggregation("project_tags")
{
Path = Field<Project>(p => p.Tags),
Aggregations = new TermsAggregation("tags")
{
Field = Field<Project>(p => p.Tags.First().Name)
}
}
};

protected override void ExpectResponse(ISearchResponse<Project> response)
{
/** ==== Handling responses
* The `AggregateDictionary found on `.Aggregations` on `ISearchResponse<T>` has several helper methods
* so we can fetch our aggregation results easily in the correct type.
* <<handling-aggregate-response, Be sure to read more about these helper methods>>
*/
response.ShouldBeValid();

var dateHistogram = response.Aggregations.AutoDateHistogram("projects_started_per_month");
dateHistogram.Should().NotBeNull();
dateHistogram.Interval.Should().NotBeNull();
dateHistogram.Buckets.Should().NotBeNull();
dateHistogram.Buckets.Count.Should().BeGreaterThan(1);
foreach (var item in dateHistogram.Buckets)
{
item.Date.Should().NotBe(default);
item.DocCount.Should().BeGreaterThan(0);

var nested = item.Nested("project_tags");
nested.Should().NotBeNull();

var nestedTerms = nested.Terms("tags");
nestedTerms.Buckets.Count.Should().BeGreaterThan(0);
}
}
}
}

0 comments on commit a830b20

Please sign in to comment.