Date Histogram Aggregation Usage

edit

A multi-bucket aggregation similar to the histogram except it can only be applied on date values. From a functionality perspective, this histogram supports the same features as the normal histogram. The main difference is that the interval can be specified by date/time expressions.

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 Date Histogram Aggregation.

Fluent DSL example

edit
s => s
.Size(0)
.Aggregations(aggs => aggs
    .DateHistogram("projects_started_per_month", date => date
        .Field(p => p.StartedOn)
        .Interval(DateInterval.Month)
        .MinimumDocumentCount(2)
        .Format("yyyy-MM-dd'T'HH:mm:ss")
        .ExtendedBoundsDateMath(FixedDate.AddYears(-1), FixedDate.AddYears(1))
        .Order(HistogramOrder.CountAscending)
        .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))
                )
            )
        )
    )
)

Object Initializer syntax example

edit
new SearchRequest<Project>
{
    Size = 0,
    Aggregations = new DateHistogramAggregation("projects_started_per_month")
    {
        Field = Field<Project>(p => p.StartedOn),
        Interval = DateInterval.Month,
        MinimumDocumentCount = 2,
        Format = "yyyy-MM-dd'T'HH:mm:ss",
        ExtendedBoundsDateMath = new ExtendedBounds<DateMath>
        {
            Minimum = FixedDate.AddYears(-1),
            Maximum = FixedDate.AddYears(1),
        },
        Order = HistogramOrder.CountAscending,
        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)
            }
        }
    }
}

Example json output.

{
  "size": 0,
  "aggs": {
    "projects_started_per_month": {
      "date_histogram": {
        "field": "startedOn",
        "interval": "month",
        "min_doc_count": 2,
        "format": "yyyy-MM-dd'T'HH:mm:ss||date_optional_time",
        "order": {
          "_count": "asc"
        },
        "extended_bounds": {
          "min": "2014-06-06T12:01:02.123",
          "max": "2016-06-06T12:01:02.123"
        },
        "missing": "2015-06-06T12:01:02.123"
      },
      "aggs": {
        "project_tags": {
          "nested": {
            "path": "tags"
          },
          "aggs": {
            "tags": {
              "terms": {
                "field": "tags.name"
              }
            }
          }
        }
      }
    }
  }
}

Handling responses

edit

Using the .Aggs aggregation helper on ISearchResponse<T>, we can fetch our aggregation results easily in the correct type. Be sure to read more about .Aggs vs .Aggregations

response.ShouldBeValid();

var dateHistogram = response.Aggs.DateHistogram("projects_started_per_month");
dateHistogram.Should().NotBeNull();
dateHistogram.Buckets.Should().NotBeNull();
dateHistogram.Buckets.Count.Should().BeGreaterThan(10);
dateHistogram.Buckets.Should().NotBeNull();
dateHistogram.Buckets.Count.Should().BeGreaterThan(0);
foreach (var item in dateHistogram.Buckets)
{
    item.Date.Should().NotBe(default(DateTime));
    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);
}