From d2f852a274906d6cd1d7d3631f1cd763a1fd3ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Tue, 3 Feb 2015 14:06:50 +0100 Subject: [PATCH] Aggregations: Add 'offset' option to date_histogram, replacing 'pre_offset' and 'post_offset' Add offset option to 'date_histogram' replacing and simplifying the previous 'pre_offset' and 'post_offset' options. This change is part of a larger clean up task for `date_histogram` from issue #9062. --- docs/reference/migration/migrate_2_0.asciidoc | 4 + .../bucket/datehistogram-aggregation.asciidoc | 10 +- .../common/rounding/Rounding.java | 38 +--- .../common/rounding/TimeZoneRounding.java | 16 +- .../histogram/DateHistogramBuilder.java | 29 +-- .../bucket/histogram/DateHistogramParser.java | 12 +- .../bucket/histogram/HistogramParser.java | 2 +- .../common/rounding/RoundingTests.java | 70 +++--- .../rounding/TimeZoneRoundingTests.java | 2 +- .../bucket/DateHistogramOffsetTests.java | 206 +++++++----------- 10 files changed, 155 insertions(+), 234 deletions(-) diff --git a/docs/reference/migration/migrate_2_0.asciidoc b/docs/reference/migration/migrate_2_0.asciidoc index 2e904c81b57..7405f4e179e 100644 --- a/docs/reference/migration/migrate_2_0.asciidoc +++ b/docs/reference/migration/migrate_2_0.asciidoc @@ -121,6 +121,10 @@ to all bucket aggregations: * All other `getKeyAsX()` methods have been removed. * The `getBucketAsKey(String)` methods have been removed on all aggregations except the `filters` and `terms` aggregations. +The `histogram` and the `date_histogram` aggregation now support a simplified `offset` option that replaces the previous `pre_offset` and +`post_offset` rounding options. Instead of having to specify two separate offset shifts of the underlying buckets, the `offset` option +moves the bucket boundaries in positive or negative direction depending on its argument. + === Terms filter lookup caching The terms filter lookup mechanism does not support the `cache` option anymore diff --git a/docs/reference/search/aggregations/bucket/datehistogram-aggregation.asciidoc b/docs/reference/search/aggregations/bucket/datehistogram-aggregation.asciidoc index e00b9703f37..1050b605d1e 100644 --- a/docs/reference/search/aggregations/bucket/datehistogram-aggregation.asciidoc +++ b/docs/reference/search/aggregations/bucket/datehistogram-aggregation.asciidoc @@ -72,10 +72,14 @@ set `pre_zone_adjust_large_interval` to `true`, which will apply the same conver example, to day and above intervals (it can be set regardless of the interval, but only kick in when using day and higher intervals). -==== Pre/Post Offset +==== Offset -Specific offsets can be provided for pre rounding and post rounding. The `pre_offset` for pre rounding, and -`post_offset` for post rounding. The format is the date time format (`1h`, `1d`, etc...). +The `offset` option can be provided for shifting the date bucket intervals boundaries after any other shifts because of +time zones are applies. This for example makes it possible that daily buckets go from 6AM to 6AM the next day instead of starting at 12AM +or that monthly buckets go from the 10th of the month to the 10th of the next month instead of the 1st. + +The `offset` option accepts positive or negative time durations like "1h" for an hour or "1M" for a Month. See <> for more +possible time duration options. ==== Keys diff --git a/src/main/java/org/elasticsearch/common/rounding/Rounding.java b/src/main/java/org/elasticsearch/common/rounding/Rounding.java index 87352123728..755a4be2714 100644 --- a/src/main/java/org/elasticsearch/common/rounding/Rounding.java +++ b/src/main/java/org/elasticsearch/common/rounding/Rounding.java @@ -177,23 +177,21 @@ public abstract class Rounding implements Streamable { out.writeFloat(factor); } } - - public static class PrePostRounding extends Rounding { + + public static class OffsetRounding extends Rounding { final static byte ID = 8; private Rounding rounding; - private long preOffset; - private long postOffset; + private long offset; - PrePostRounding() { // for serialization + OffsetRounding() { // for serialization } - public PrePostRounding(Rounding intervalRounding, long preOffset, long postOffset) { + public OffsetRounding(Rounding intervalRounding, long offset) { this.rounding = intervalRounding; - this.preOffset = preOffset; - this.postOffset = postOffset; + this.offset = offset; } @Override @@ -203,41 +201,29 @@ public abstract class Rounding implements Streamable { @Override public long roundKey(long value) { - return rounding.roundKey(value + preOffset); + return rounding.roundKey(value - offset); } @Override public long valueForKey(long key) { - return postOffset + rounding.valueForKey(key); + return offset + rounding.valueForKey(key); } @Override public long nextRoundingValue(long value) { - return postOffset + rounding.nextRoundingValue(value - postOffset); + return rounding.nextRoundingValue(value - offset) + offset; } @Override public void readFrom(StreamInput in) throws IOException { rounding = Rounding.Streams.read(in); - if (in.getVersion().before(Version.V_1_4_0_Beta1)) { - preOffset = in.readVLong(); - postOffset = in.readVLong(); - } else { - preOffset = in.readLong(); - postOffset = in.readLong(); - } + offset = in.readLong(); } @Override public void writeTo(StreamOutput out) throws IOException { Rounding.Streams.write(rounding, out); - if (out.getVersion().before(Version.V_1_4_0_Beta1)) { - out.writeVLong(preOffset); - out.writeVLong(postOffset); - } else { - out.writeLong(preOffset); - out.writeLong(postOffset); - } + out.writeLong(offset); } } @@ -260,7 +246,7 @@ public abstract class Rounding implements Streamable { case TimeZoneRounding.TimeIntervalTimeZoneRounding.ID: rounding = new TimeZoneRounding.TimeIntervalTimeZoneRounding(); break; case TimeZoneRounding.DayIntervalTimeZoneRounding.ID: rounding = new TimeZoneRounding.DayIntervalTimeZoneRounding(); break; case TimeZoneRounding.FactorRounding.ID: rounding = new FactorRounding(); break; - case PrePostRounding.ID: rounding = new PrePostRounding(); break; + case OffsetRounding.ID: rounding = new OffsetRounding(); break; default: throw new ElasticsearchException("unknown rounding id [" + id + "]"); } rounding.readFrom(in); diff --git a/src/main/java/org/elasticsearch/common/rounding/TimeZoneRounding.java b/src/main/java/org/elasticsearch/common/rounding/TimeZoneRounding.java index 1dee393d8cf..6f251f0f73b 100644 --- a/src/main/java/org/elasticsearch/common/rounding/TimeZoneRounding.java +++ b/src/main/java/org/elasticsearch/common/rounding/TimeZoneRounding.java @@ -51,8 +51,7 @@ public abstract class TimeZoneRounding extends Rounding { private float factor = 1.0f; - private long preOffset; - private long postOffset; + private long offset; private boolean preZoneAdjustLargeInterval = false; @@ -81,13 +80,8 @@ public abstract class TimeZoneRounding extends Rounding { return this; } - public Builder preOffset(long preOffset) { - this.preOffset = preOffset; - return this; - } - - public Builder postOffset(long postOffset) { - this.postOffset = postOffset; + public Builder offset(long offset) { + this.offset = offset; return this; } @@ -115,8 +109,8 @@ public abstract class TimeZoneRounding extends Rounding { timeZoneRounding = new DayIntervalTimeZoneRounding(interval, preTz, postTz); } } - if (preOffset != 0 || postOffset != 0) { - timeZoneRounding = new PrePostRounding(timeZoneRounding, preOffset, postOffset); + if (offset != 0) { + timeZoneRounding = new OffsetRounding(timeZoneRounding, offset); } if (factor != 1.0f) { timeZoneRounding = new FactorRounding(timeZoneRounding, factor); diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramBuilder.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramBuilder.java index eba49b5a2d4..4ddfafa6acf 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramBuilder.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramBuilder.java @@ -41,8 +41,7 @@ public class DateHistogramBuilder extends ValuesSourceAggregationBuilder 31) - * postOffset shifts rounded Value after rounding (here 30 -> 35) + * Simple test case to illustrate how Rounding.Offset works on readable input. + * offset shifts input value back before rounding (so here 6 - 7 -> -1) + * then shifts rounded Value back (here -10 -> -3) */ @Test - public void testPrePostRounding() { - int interval = 10; - int value = 24; - int preOffset = 7; - int postOffset = 5; - Rounding.PrePostRounding rounding = new Rounding.PrePostRounding(new Rounding.Interval(interval), preOffset, postOffset); - final long key = rounding.roundKey(24); - final long roundedValue = rounding.round(24); - String message = "round(" + value + ", interval=" + interval + ") = " + roundedValue; - assertEquals(3, key); - assertEquals(35, roundedValue); - assertEquals(message, postOffset, roundedValue % interval); + public void testOffsetRounding() { + final long interval = 10; + final long offset = 7; + Rounding.OffsetRounding rounding = new Rounding.OffsetRounding(new Rounding.Interval(interval), offset); + assertEquals(-1, rounding.roundKey(6)); + assertEquals(-3, rounding.round(6)); + assertEquals(7, rounding.nextRoundingValue(-3)); + assertEquals(0, rounding.roundKey(7)); + assertEquals(7, rounding.round(7)); + assertEquals(17, rounding.nextRoundingValue(7)); + assertEquals(0, rounding.roundKey(16)); + assertEquals(7, rounding.round(16)); + assertEquals(1, rounding.roundKey(17)); + assertEquals(17, rounding.round(17)); + assertEquals(27, rounding.nextRoundingValue(17)); } + /** + * test OffsetRounding with an internal interval rounding on random inputs + */ @Test - public void testPrePostRoundingRandom() { - final long interval = randomIntBetween(1, 100); - Rounding.Interval internalRounding = new Rounding.Interval(interval); - final long preRounding = randomIntBetween(-100, 100); - final long postRounding = randomIntBetween(-100, 100); - Rounding.PrePostRounding prePost = new Rounding.PrePostRounding(new Rounding.Interval(interval), preRounding, postRounding); - long safetyMargin = Math.abs(interval) + Math.abs(preRounding) + Math.abs(postRounding); // to prevent range overflow / underflow + public void testOffsetRoundingRandom() { for (int i = 0; i < 1000; ++i) { - long l = Math.max(randomLong() - safetyMargin, Long.MIN_VALUE + safetyMargin); - final long key = prePost.roundKey(l); - final long r = prePost.round(l); - String message = "round(" + l + ", interval=" + interval + ") = "+ r; - assertEquals(message, internalRounding.round(l+preRounding), r - postRounding); - assertThat(message, r - postRounding, lessThanOrEqualTo(l + preRounding)); - assertThat(message, r + interval - postRounding, greaterThan(l + preRounding)); - assertEquals(message, r, key*interval + postRounding); + final long interval = randomIntBetween(1, 100); + Rounding.Interval internalRounding = new Rounding.Interval(interval); + final long offset = randomIntBetween(-100, 100); + Rounding.OffsetRounding rounding = new Rounding.OffsetRounding(internalRounding, offset); + long safetyMargin = Math.abs(interval) + Math.abs(offset); // to prevent range overflow + long value = Math.max(randomLong() - safetyMargin, Long.MIN_VALUE + safetyMargin); + final long key = rounding.roundKey(value); + final long key_next = rounding.roundKey(value + interval); + final long r_value = rounding.round(value); + final long nextRoundingValue = rounding.nextRoundingValue(r_value); + assertThat("Rounding should be idempotent", r_value, equalTo(rounding.round(r_value))); + assertThat("Rounded value smaller than unrounded, regardless of offset", r_value - offset, lessThanOrEqualTo(value - offset)); + assertThat("Key and next_key should differ by one", key_next - key, equalTo(1L)); + assertThat("Rounded value <= value < next interval start", r_value + interval, greaterThan(value)); + assertThat("NextRounding value should be interval from rounded value", r_value + interval, equalTo(nextRoundingValue)); } } } diff --git a/src/test/java/org/elasticsearch/common/rounding/TimeZoneRoundingTests.java b/src/test/java/org/elasticsearch/common/rounding/TimeZoneRoundingTests.java index e79ad1e4b1d..02012f5c41a 100644 --- a/src/test/java/org/elasticsearch/common/rounding/TimeZoneRoundingTests.java +++ b/src/test/java/org/elasticsearch/common/rounding/TimeZoneRoundingTests.java @@ -41,7 +41,7 @@ public class TimeZoneRoundingTests extends ElasticsearchTestCase { assertThat(tzRounding.round(utc("2012-01-10T01:01:01")), equalTo(utc("2012-01-09T00:00:00.000Z"))); assertThat(tzRounding.nextRoundingValue(utc("2012-01-09T00:00:00.000Z")), equalTo(utc("2012-01-16T00:00:00.000Z"))); - tzRounding = TimeZoneRounding.builder(DateTimeUnit.WEEK_OF_WEEKYEAR).postOffset(-TimeValue.timeValueHours(24).millis()).build(); + tzRounding = TimeZoneRounding.builder(DateTimeUnit.WEEK_OF_WEEKYEAR).offset(-TimeValue.timeValueHours(24).millis()).build(); assertThat(tzRounding.round(utc("2012-01-10T01:01:01")), equalTo(utc("2012-01-08T00:00:00.000Z"))); assertThat(tzRounding.nextRoundingValue(utc("2012-01-08T00:00:00.000Z")), equalTo(utc("2012-01-15T00:00:00.000Z"))); } diff --git a/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetTests.java b/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetTests.java index beca294ae6e..c6fbc58e73c 100644 --- a/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetTests.java +++ b/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramOffsetTests.java @@ -31,10 +31,12 @@ import org.elasticsearch.test.transport.AssertingLocalTransport; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.After; +import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.util.List; +import java.util.concurrent.ExecutionException; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; @@ -43,14 +45,16 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.IsNull.notNullValue; /** - * The serialisation of pre and post offsets for the date histogram aggregation was corrected in version 1.4 to allow negative offsets and as such the - * serialisation of negative offsets in these tests would break in pre 1.4 versions. These tests are separated from the other DateHistogramTests so the + * The serialisation of offsets for the date histogram aggregation was corrected in version 1.4 to allow negative offsets and as such the + * serialisation of negative offsets in these tests would break in pre 1.4 versions. These tests are separated from the other DateHistogramTests so the * AssertingLocalTransport for these tests can be set to only use versions 1.4 onwards while keeping the other tests using all versions */ @ElasticsearchIntegrationTest.SuiteScopeTest @ElasticsearchIntegrationTest.ClusterScope(scope=ElasticsearchIntegrationTest.Scope.SUITE) public class DateHistogramOffsetTests extends ElasticsearchIntegrationTest { + private static final String DATE_FORMAT = "YY-MM-DD:hh-mm-ss"; + private DateTime date(String date) { return DateFieldMapper.Defaults.DATE_TIME_FORMATTER.parser().parseDateTime(date); } @@ -62,29 +66,36 @@ public class DateHistogramOffsetTests extends ElasticsearchIntegrationTest { .put(AssertingLocalTransport.ASSERTING_TRANSPORT_MIN_VERSION_KEY, Version.V_1_4_0_Beta1).build(); } + @Before + public void beforeEachTest() throws IOException { + prepareCreate("idx2").addMapping("type", "date", "type=date").execute().actionGet(); + } + @After public void afterEachTest() throws IOException { internalCluster().wipeIndices("idx2"); } - @Test - public void singleValue_WithPreOffset() throws Exception { - prepareCreate("idx2").addMapping("type", "date", "type=date").execute().actionGet(); - IndexRequestBuilder[] reqs = new IndexRequestBuilder[5]; - DateTime date = date("2014-03-11T00:00:00+00:00"); - for (int i = 0; i < reqs.length; i++) { - reqs[i] = client().prepareIndex("idx2", "type", "" + i).setSource(jsonBuilder().startObject().field("date", date).endObject()); - date = date.plusHours(1); + private void prepareIndex(DateTime date, int numHours, int stepSizeHours, int idxIdStart) throws IOException, InterruptedException, ExecutionException { + IndexRequestBuilder[] reqs = new IndexRequestBuilder[numHours]; + for (int i = idxIdStart; i < idxIdStart + reqs.length; i++) { + reqs[i - idxIdStart] = client().prepareIndex("idx2", "type", "" + i).setSource(jsonBuilder().startObject().field("date", date).endObject()); + date = date.plusHours(stepSizeHours); } indexRandom(true, reqs); + } + + @Test + public void singleValue_WithPositiveOffset() throws Exception { + prepareIndex(date("2014-03-11T00:00:00+00:00"), 5, 1, 0); SearchResponse response = client().prepareSearch("idx2") .setQuery(matchAllQuery()) .addAggregation(dateHistogram("date_histo") .field("date") - .preOffset("-2h") - .interval(DateHistogramInterval.DAY) - .format("yyyy-MM-dd")) + .offset("2h") + .format(DATE_FORMAT) + .interval(DateHistogramInterval.DAY)) .execute().actionGet(); assertThat(response.getHits().getTotalHits(), equalTo(5l)); @@ -93,143 +104,74 @@ public class DateHistogramOffsetTests extends ElasticsearchIntegrationTest { List buckets = histo.getBuckets(); assertThat(buckets.size(), equalTo(2)); - DateTime key = new DateTime(2014, 3, 10, 0, 0, DateTimeZone.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-10")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2l)); - - key = new DateTime(2014, 3, 11, 0, 0, DateTimeZone.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-11")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3l)); + checkBucketFor(buckets.get(0), new DateTime(2014, 3, 10, 2, 0, DateTimeZone.UTC), 2l); + checkBucketFor(buckets.get(1), new DateTime(2014, 3, 11, 2, 0, DateTimeZone.UTC), 3l); } @Test - public void singleValue_WithPreOffset_MinDocCount() throws Exception { - prepareCreate("idx2").addMapping("type", "date", "type=date").execute().actionGet(); - IndexRequestBuilder[] reqs = new IndexRequestBuilder[5]; - DateTime date = date("2014-03-11T00:00:00+00:00"); - for (int i = 0; i < reqs.length; i++) { - reqs[i] = client().prepareIndex("idx2", "type", "" + i).setSource(jsonBuilder().startObject().field("date", date).endObject()); - date = date.plusHours(1); - } - indexRandom(true, reqs); + public void singleValue_WithNegativeOffset() throws Exception { + prepareIndex(date("2014-03-11T00:00:00+00:00"), 5, -1, 0); SearchResponse response = client().prepareSearch("idx2") .setQuery(matchAllQuery()) .addAggregation(dateHistogram("date_histo") .field("date") - .preOffset("-2h") + .offset("-2h") + .format(DATE_FORMAT) + .interval(DateHistogramInterval.DAY)) + .execute().actionGet(); + + assertThat(response.getHits().getTotalHits(), equalTo(5l)); + + Histogram histo = response.getAggregations().get("date_histo"); + List buckets = histo.getBuckets(); + assertThat(buckets.size(), equalTo(2)); + + checkBucketFor(buckets.get(0), new DateTime(2014, 3, 9, 22, 0, DateTimeZone.UTC), 2l); + checkBucketFor(buckets.get(1), new DateTime(2014, 3, 10, 22, 0, DateTimeZone.UTC), 3l); + } + + /** + * Set offset so day buckets start at 6am. Index first 12 hours for two days, with one day gap. + * @throws Exception + */ + @Test + public void singleValue_WithOffset_MinDocCount() throws Exception { + prepareIndex(date("2014-03-11T00:00:00+00:00"), 12, 1, 0); + prepareIndex(date("2014-03-14T00:00:00+00:00"), 12, 1, 13); + + SearchResponse response = client().prepareSearch("idx2") + .setQuery(matchAllQuery()) + .addAggregation(dateHistogram("date_histo") + .field("date") + .offset("6h") .minDocCount(0) - .interval(DateHistogramInterval.DAY) - .format("yyyy-MM-dd")) + .format(DATE_FORMAT) + .interval(DateHistogramInterval.DAY)) .execute().actionGet(); - assertThat(response.getHits().getTotalHits(), equalTo(5l)); + assertThat(response.getHits().getTotalHits(), equalTo(24l)); Histogram histo = response.getAggregations().get("date_histo"); List buckets = histo.getBuckets(); - assertThat(buckets.size(), equalTo(2)); + assertThat(buckets.size(), equalTo(5)); - DateTime key = new DateTime(2014, 3, 10, 0, 0, DateTimeZone.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-10")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(2l)); - - key = new DateTime(2014, 3, 11, 0, 0, DateTimeZone.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-11")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(3l)); + checkBucketFor(buckets.get(0), new DateTime(2014, 3, 10, 6, 0, DateTimeZone.UTC), 6L); + checkBucketFor(buckets.get(1), new DateTime(2014, 3, 11, 6, 0, DateTimeZone.UTC), 6L); + checkBucketFor(buckets.get(2), new DateTime(2014, 3, 12, 6, 0, DateTimeZone.UTC), 0L); + checkBucketFor(buckets.get(3), new DateTime(2014, 3, 13, 6, 0, DateTimeZone.UTC), 6L); + checkBucketFor(buckets.get(4), new DateTime(2014, 3, 14, 6, 0, DateTimeZone.UTC), 6L); } - @Test - public void singleValue_WithPostOffset() throws Exception { - prepareCreate("idx2").addMapping("type", "date", "type=date").execute().actionGet(); - IndexRequestBuilder[] reqs = new IndexRequestBuilder[5]; - DateTime date = date("2014-03-11T00:00:00+00:00"); - for (int i = 0; i < reqs.length; i++) { - reqs[i] = client().prepareIndex("idx2", "type", "" + i).setSource(jsonBuilder().startObject().field("date", date).endObject()); - date = date.plusHours(6); - } - indexRandom(true, reqs); - - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) - .addAggregation(dateHistogram("date_histo") - .field("date") - .postOffset("2d") - .interval(DateHistogramInterval.DAY) - .format("yyyy-MM-dd")) - .execute().actionGet(); - - assertThat(response.getHits().getTotalHits(), equalTo(5l)); - - Histogram histo = response.getAggregations().get("date_histo"); - List buckets = histo.getBuckets(); - assertThat(buckets.size(), equalTo(2)); - - DateTime key = new DateTime(2014, 3, 13, 0, 0, DateTimeZone.UTC); - Histogram.Bucket bucket = buckets.get(0); + /** + * @param bucket the bucket to check asssertions for + * @param key the expected key + * @param expectedSize the expected size of the bucket + */ + private static void checkBucketFor(Histogram.Bucket bucket, DateTime key, long expectedSize) { assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-13")); + assertThat(bucket.getKeyAsString(), equalTo(key.toString(DATE_FORMAT))); assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(4l)); - - key = new DateTime(2014, 3, 14, 0, 0, DateTimeZone.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-14")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1l)); - } - - @Test - public void singleValue_WithPostOffset_MinDocCount() throws Exception { - prepareCreate("idx2").addMapping("type", "date", "type=date").execute().actionGet(); - IndexRequestBuilder[] reqs = new IndexRequestBuilder[5]; - DateTime date = date("2014-03-11T00:00:00+00:00"); - for (int i = 0; i < reqs.length; i++) { - reqs[i] = client().prepareIndex("idx2", "type", "" + i).setSource(jsonBuilder().startObject().field("date", date).endObject()); - date = date.plusHours(6); - } - indexRandom(true, reqs); - - SearchResponse response = client().prepareSearch("idx2") - .setQuery(matchAllQuery()) - .addAggregation(dateHistogram("date_histo") - .field("date") - .postOffset("2d") - .minDocCount(0) - .interval(DateHistogramInterval.DAY) - .format("yyyy-MM-dd")) - .execute().actionGet(); - - assertThat(response.getHits().getTotalHits(), equalTo(5l)); - - Histogram histo = response.getAggregations().get("date_histo"); - List buckets = histo.getBuckets(); - assertThat(buckets.size(), equalTo(2)); - - DateTime key = new DateTime(2014, 3, 13, 0, 0, DateTimeZone.UTC); - Histogram.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-13")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(4l)); - - key = new DateTime(2014, 3, 14, 0, 0, DateTimeZone.UTC); - bucket = buckets.get(1); - assertThat(bucket, notNullValue()); - assertThat(bucket.getKeyAsString(), equalTo("2014-03-14")); - assertThat(((DateTime) bucket.getKey()), equalTo(key)); - assertThat(bucket.getDocCount(), equalTo(1l)); + assertThat(bucket.getDocCount(), equalTo(expectedSize)); } }