Skip to content

Commit

Permalink
Aggregations: Make ValueParser.DateMath aware of timezone setting
Browse files Browse the repository at this point in the history
This PR adds a timezone field to ValueParser.DateMath that is
set to UTC by default but can be set using the existing constructors.
This makes it possible for extended bounds setting in DateHistogram
to also use date math expressions that e.g. round by day and apply
this rounding in the time zone specified in the date histogram
aggregation request.

Closes #12278
  • Loading branch information
cbuescher committed Aug 21, 2015
1 parent 893534e commit 8ba2c9b
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,14 @@ public static class DateTime extends Patternable<DateTime> {
public static final DateTime DEFAULT = new DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format(), ValueFormatter.DateTime.DEFAULT, ValueParser.DateMath.DEFAULT);

public static DateTime format(String format, DateTimeZone timezone) {
return new DateTime(format, new ValueFormatter.DateTime(format, timezone), new ValueParser.DateMath(format));
return new DateTime(format, new ValueFormatter.DateTime(format, timezone), new ValueParser.DateMath(format, timezone));
}

public static DateTime mapper(DateFieldMapper.DateFieldType fieldType, DateTimeZone timezone) {
return new DateTime(fieldType.dateTimeFormatter().format(), ValueFormatter.DateTime.mapper(fieldType, timezone), ValueParser.DateMath.mapper(fieldType));
return new DateTime(fieldType.dateTimeFormatter().format(), ValueFormatter.DateTime.mapper(fieldType, timezone), ValueParser.DateMath.mapper(fieldType, timezone));
}

public DateTime(String pattern, ValueFormatter formatter, ValueParser parser) {
private DateTime(String pattern, ValueFormatter formatter, ValueParser parser) {
super(pattern, formatter, parser);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
*/
package org.elasticsearch.search.aggregations.support.format;

import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.mapper.ip.IpFieldMapper;
import org.elasticsearch.search.aggregations.AggregationExecutionException;
import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
Expand Down Expand Up @@ -80,16 +82,21 @@ public double parseDouble(String value, SearchContext searchContext) {
*/
static class DateMath implements ValueParser {

public static final DateMath DEFAULT = new ValueParser.DateMath(new DateMathParser(DateFieldMapper.Defaults.DATE_TIME_FORMATTER));
public static final DateMath DEFAULT = new ValueParser.DateMath(new DateMathParser(DateFieldMapper.Defaults.DATE_TIME_FORMATTER), DateTimeZone.UTC);

private DateMathParser parser;

public DateMath(String format) {
this(new DateMathParser(Joda.forPattern(format)));
private DateTimeZone timezone = DateTimeZone.UTC;

public DateMath(String format, DateTimeZone timezone) {
this(new DateMathParser(Joda.forPattern(format)), timezone);
}

public DateMath(DateMathParser parser) {
public DateMath(DateMathParser parser, @Nullable DateTimeZone timeZone) {
this.parser = parser;
if (timeZone != null) {
this.timezone = timeZone;
}
}

@Override
Expand All @@ -100,16 +107,16 @@ public Long call() throws Exception {
return searchContext.nowInMillis();
}
};
return parser.parse(value, now);
return parser.parse(value, now, false, timezone);
}

@Override
public double parseDouble(String value, SearchContext searchContext) {
return parseLong(value, searchContext);
}

public static DateMath mapper(DateFieldMapper.DateFieldType fieldType) {
return new DateMath(new DateMathParser(fieldType.dateTimeFormatter()));
public static DateMath mapper(DateFieldMapper.DateFieldType fieldType, @Nullable DateTimeZone timezone) {
return new DateMath(new DateMathParser(fieldType.dateTimeFormatter()), timezone);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
Expand All @@ -42,6 +44,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -558,7 +561,7 @@ public void singleValuedField_WithValueScript() throws Exception {
assertThat(bucket.getDocCount(), equalTo(3l));
}



/*
[ Jan 2, Feb 3]
Expand Down Expand Up @@ -904,7 +907,7 @@ public void script_MultiValued() throws Exception {
assertThat(bucket.getDocCount(), equalTo(3l));
}



/*
[ Jan 2, Feb 3]
Expand Down Expand Up @@ -1195,6 +1198,70 @@ public void singleValueField_WithExtendedBounds() throws Exception {
}
}

/**
* Test date histogram aggregation with hour interval, timezone shift and
* extended bounds (see https://github.com/elastic/elasticsearch/issues/12278)
*/
@Test
public void singleValueField_WithExtendedBoundsTimezone() throws Exception {

String index = "test12278";
prepareCreate(index)
.setSettings(Settings.builder().put(indexSettings()).put("index.number_of_shards", 1).put("index.number_of_replicas", 0))
.execute().actionGet();

DateMathParser parser = new DateMathParser(Joda.getStrictStandardDateFormatter());

final Callable<Long> callable = new Callable<Long>() {
@Override
public Long call() throws Exception {
return System.currentTimeMillis();
}
};

// we pick a random timezone offset of +12/-12 hours and insert two documents
// one at 00:00 in that time zone and one at 12:00
List<IndexRequestBuilder> builders = new ArrayList<>();
int timeZoneHourOffset = randomIntBetween(-12, 12);
DateTimeZone timezone = DateTimeZone.forOffsetHours(timeZoneHourOffset);
DateTime timeZoneStartToday = new DateTime(parser.parse("now/d", callable, false, timezone), DateTimeZone.UTC);
DateTime timeZoneNoonToday = new DateTime(parser.parse("now/d+12h", callable, false, timezone), DateTimeZone.UTC);
builders.add(indexDoc(index, timeZoneStartToday, 1));
builders.add(indexDoc(index, timeZoneNoonToday, 2));
indexRandom(true, builders);
ensureSearchable(index);

SearchResponse response = null;
// retrieve those docs with the same time zone and extended bounds
response = client()
.prepareSearch(index)
.setQuery(QueryBuilders.rangeQuery("date").from("now/d").to("now/d").includeLower(true).includeUpper(true).timeZone(timezone.getID()))
.addAggregation(
dateHistogram("histo").field("date").interval(DateHistogramInterval.hours(1)).timeZone(timezone.getID()).minDocCount(0)
.extendedBounds("now/d", "now/d+23h")
).execute().actionGet();
assertSearchResponse(response);

assertThat("Expected 24 buckets for one day aggregation with hourly interval", response.getHits().totalHits(), equalTo(2l));

Histogram histo = response.getAggregations().get("histo");
assertThat(histo, notNullValue());
assertThat(histo.getName(), equalTo("histo"));
List<? extends Bucket> buckets = histo.getBuckets();
assertThat(buckets.size(), equalTo(24));

for (int i = 0; i < buckets.size(); i++) {
Histogram.Bucket bucket = buckets.get(i);
assertThat(bucket, notNullValue());
assertThat("Bucket " + i +" had wrong key", (DateTime) bucket.getKey(), equalTo(new DateTime(timeZoneStartToday.getMillis() + (i * 60 * 60 * 1000), DateTimeZone.UTC)));
if (i == 0 || i == 12) {
assertThat(bucket.getDocCount(), equalTo(1l));
} else {
assertThat(bucket.getDocCount(), equalTo(0l));
}
}
}

@Test
public void singleValue_WithMultipleDateFormatsFromMapping() throws Exception {

Expand Down Expand Up @@ -1233,7 +1300,7 @@ public void testIssue6965() {
.execute().actionGet();

assertSearchResponse(response);

DateTimeZone tz = DateTimeZone.forID("+01:00");

Histogram histo = response.getAggregations().get("histo");
Expand Down

0 comments on commit 8ba2c9b

Please sign in to comment.