Merge pull request #16955 from cbuescher/timezone-dateRangeAgg

Date range aggregations used to be unable to use a `time_zone` parameter to e.g. be applied 
int date math roundings like in `now/d` (see #10130 as an example). After the aggregation 
refactoring, the time_zone parameter has been pulled up to ValuesSourceAggregatorBuilder 
and can now be used in date range aggregations as well. 

This change adds randomized time zone settings to the existing IT tests to verify that the 
`time_zone` parameter is honored when calculating the bucket boundaries. Also moving 
the DateRangeTests from module-groovy/messy back to core as DateRangeIT, sharing 
common script mocks with DateHistogramIT and adding documentation for the 
`time_zone` parameter in the date range aggregation docs.

Closes #10130
This commit is contained in:
Christoph Büscher 2016-03-07 16:11:55 +01:00
commit 350e3a4850
7 changed files with 243 additions and 303 deletions

View File

@ -1486,7 +1486,6 @@
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]script[/\\]groovy[/\\]GroovyScriptEngineService.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]BucketScriptTests.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]BulkTests.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]DateRangeTests.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]DoubleTermsTests.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]EquivalenceTests.java" checks="LineLength" />
<suppress files="modules[/\\]lang-groovy[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]messy[/\\]tests[/\\]FunctionScoreTests.java" checks="LineLength" />

View File

@ -18,8 +18,6 @@
*/
package org.elasticsearch.search.aggregations.bucket;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Scorer;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.joda.DateMathParser;
@ -28,22 +26,14 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.mapper.core.DateFieldMapper;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.CompiledScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.LeafSearchScript;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptEngineRegistry;
import org.elasticsearch.script.ScriptEngineService;
import org.elasticsearch.script.ScriptModule;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.aggregations.bucket.DateScriptMocks.DateScriptsMockPlugin;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram.Bucket;
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
import org.elasticsearch.search.lookup.LeafSearchLookup;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.test.ESIntegTestCase;
import org.hamcrest.Matchers;
import org.joda.time.DateTime;
@ -55,7 +45,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -142,8 +131,7 @@ public class DateHistogramIT extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Arrays.asList(
ExtractFieldScriptPlugin.class,
FieldValueScriptPlugin.class);
DateScriptsMockPlugin.class);
}
@After
@ -466,10 +454,12 @@ public class DateHistogramIT extends ESIntegTestCase {
}
public void testSingleValuedFieldWithValueScript() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateHistogram("histo")
.field("date")
.script(new Script("", ScriptType.INLINE, FieldValueScriptEngine.NAME, null))
.script(new Script(DateScriptMocks.PlusOneMonthScript.NAME, ScriptType.INLINE, "native", params))
.dateHistogramInterval(DateHistogramInterval.MONTH)).execute().actionGet();
assertSearchResponse(response);
@ -600,10 +590,12 @@ public class DateHistogramIT extends ESIntegTestCase {
* doc 6: [ Apr 23, May 24]
*/
public void testMultiValuedFieldWithValueScript() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "dates");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateHistogram("histo")
.field("dates")
.script(new Script("", ScriptType.INLINE, FieldValueScriptEngine.NAME, null))
.script(new Script(DateScriptMocks.PlusOneMonthScript.NAME, ScriptType.INLINE, "native", params))
.dateHistogramInterval(DateHistogramInterval.MONTH)).execute().actionGet();
assertSearchResponse(response);
@ -652,8 +644,11 @@ public class DateHistogramIT extends ESIntegTestCase {
* Mar 23
*/
public void testScriptSingleValue() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateHistogram("histo").script(new Script("date", ScriptType.INLINE, ExtractFieldScriptEngine.NAME, null)).dateHistogramInterval(DateHistogramInterval.MONTH))
.addAggregation(dateHistogram("histo").script(new Script(DateScriptMocks.ExtractFieldScript.NAME,
ScriptType.INLINE, "native", params)).dateHistogramInterval(DateHistogramInterval.MONTH))
.execute().actionGet();
assertSearchResponse(response);
@ -687,8 +682,11 @@ public class DateHistogramIT extends ESIntegTestCase {
}
public void testScriptMultiValued() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "dates");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateHistogram("histo").script(new Script("dates", ScriptType.INLINE, ExtractFieldScriptEngine.NAME, null)).dateHistogramInterval(DateHistogramInterval.MONTH))
.addAggregation(dateHistogram("histo").script(new Script(DateScriptMocks.ExtractFieldScript.NAME,
ScriptType.INLINE, "native", params)).dateHistogramInterval(DateHistogramInterval.MONTH))
.execute().actionGet();
assertSearchResponse(response);
@ -1148,256 +1146,4 @@ public class DateHistogramIT extends ESIntegTestCase {
Histogram histo = response.getAggregations().get("histo");
assertThat(histo.getBuckets().size(), greaterThan(0));
}
/**
* Mock plugin for the {@link ExtractFieldScriptEngine}
*/
public static class ExtractFieldScriptPlugin extends Plugin {
@Override
public String name() {
return ExtractFieldScriptEngine.NAME;
}
@Override
public String description() {
return "Mock script engine for " + DateHistogramIT.class;
}
public void onModule(ScriptModule module) {
module.addScriptEngine(new ScriptEngineRegistry.ScriptEngineRegistration(ExtractFieldScriptEngine.class, ExtractFieldScriptEngine.TYPES));
}
}
/**
* This mock script returns the field that is specified by name in the script body
*/
public static class ExtractFieldScriptEngine implements ScriptEngineService {
public static final String NAME = "extract_field";
public static final List<String> TYPES = Collections.singletonList(NAME);
@Override
public void close() throws IOException {
}
@Override
public List<String> getTypes() {
return TYPES;
}
@Override
public List<String> getExtensions() {
return TYPES;
}
@Override
public boolean isSandboxed() {
return true;
}
@Override
public Object compile(String script, Map<String, String> params) {
return script;
}
@Override
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> params) {
throw new UnsupportedOperationException();
}
@Override
public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, Map<String, Object> vars) {
return new SearchScript() {
@Override
public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException {
final LeafSearchLookup leafLookup = lookup.getLeafSearchLookup(context);
return new LeafSearchScript() {
@Override
public void setNextVar(String name, Object value) {
}
@Override
public Object run() {
String fieldName = (String) compiledScript.compiled();
return leafLookup.doc().get(fieldName);
}
@Override
public void setScorer(Scorer scorer) {
}
@Override
public void setSource(Map<String, Object> source) {
}
@Override
public void setDocument(int doc) {
if (leafLookup != null) {
leafLookup.setDocument(doc);
}
}
@Override
public long runAsLong() {
throw new UnsupportedOperationException();
}
@Override
public float runAsFloat() {
throw new UnsupportedOperationException();
}
@Override
public double runAsDouble() {
throw new UnsupportedOperationException();
}
};
}
@Override
public boolean needsScores() {
return false;
}
};
}
@Override
public void scriptRemoved(CompiledScript script) {
}
}
/**
* Mock plugin for the {@link FieldValueScriptEngine}
*/
public static class FieldValueScriptPlugin extends Plugin {
@Override
public String name() {
return FieldValueScriptEngine.NAME;
}
@Override
public String description() {
return "Mock script engine for " + DateHistogramIT.class;
}
public void onModule(ScriptModule module) {
module.addScriptEngine(new ScriptEngineRegistry.ScriptEngineRegistration(FieldValueScriptEngine.class, FieldValueScriptEngine.TYPES));
}
}
/**
* This mock script returns the field value and adds one month to the returned date
*/
public static class FieldValueScriptEngine implements ScriptEngineService {
public static final String NAME = "field_value";
public static final List<String> TYPES = Collections.singletonList(NAME);
@Override
public void close() throws IOException {
}
@Override
public List<String> getTypes() {
return TYPES;
}
@Override
public List<String> getExtensions() {
return TYPES;
}
@Override
public boolean isSandboxed() {
return true;
}
@Override
public Object compile(String script, Map<String, String> params) {
return script;
}
@Override
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> params) {
throw new UnsupportedOperationException();
}
@Override
public SearchScript search(CompiledScript compiledScript, SearchLookup lookup, Map<String, Object> vars) {
return new SearchScript() {
private Map<String, Object> vars = new HashMap<>(2);
@Override
public LeafSearchScript getLeafSearchScript(LeafReaderContext context) throws IOException {
final LeafSearchLookup leafLookup = lookup.getLeafSearchLookup(context);
return new LeafSearchScript() {
@Override
public Object unwrap(Object value) {
throw new UnsupportedOperationException();
}
@Override
public void setNextVar(String name, Object value) {
vars.put(name, value);
}
@Override
public Object run() {
throw new UnsupportedOperationException();
}
@Override
public void setScorer(Scorer scorer) {
}
@Override
public void setSource(Map<String, Object> source) {
}
@Override
public void setDocument(int doc) {
if (leafLookup != null) {
leafLookup.setDocument(doc);
}
}
@Override
public long runAsLong() {
return new DateTime((long) vars.get("_value"), DateTimeZone.UTC).plusMonths(1).getMillis();
}
@Override
public float runAsFloat() {
throw new UnsupportedOperationException();
}
@Override
public double runAsDouble() {
return new DateTime(new Double((double) vars.get("_value")).longValue(), DateTimeZone.UTC).plusMonths(1).getMillis();
}
};
}
@Override
public boolean needsScores() {
return false;
}
};
}
@Override
public void scriptRemoved(CompiledScript script) {
}
}
}

View File

@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.messy.tests;
package org.elasticsearch.search.aggregations.bucket;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.groovy.GroovyPlugin;
import org.elasticsearch.script.ScriptService.ScriptType;
import org.elasticsearch.search.aggregations.bucket.DateScriptMocks.DateScriptsMockPlugin;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.range.Range;
import org.elasticsearch.search.aggregations.bucket.range.Range.Bucket;
@ -36,8 +37,9 @@ import org.joda.time.DateTimeZone;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@ -55,12 +57,7 @@ import static org.hamcrest.core.IsNull.nullValue;
*
*/
@ESIntegTestCase.SuiteScopeTestCase
public class DateRangeTests extends ESIntegTestCase {
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Collections.singleton(GroovyPlugin.class);
}
public class DateRangeIT extends ESIntegTestCase {
private static IndexRequestBuilder indexDoc(int month, int day, int value) throws Exception {
return client().prepareIndex("idx", "type").setSource(jsonBuilder()
@ -72,7 +69,11 @@ public class DateRangeTests extends ESIntegTestCase {
}
private static DateTime date(int month, int day) {
return new DateTime(2012, month, day, 0, 0, DateTimeZone.UTC);
return date(month, day, DateTimeZone.UTC);
}
private static DateTime date(int month, int day, DateTimeZone timezone) {
return new DateTime(2012, month, day, 0, 0, timezone);
}
private static int numDocs;
@ -107,18 +108,26 @@ public class DateRangeTests extends ESIntegTestCase {
ensureSearchable();
}
@Override
protected Collection<Class<? extends Plugin>> nodePlugins() {
return Arrays.asList(
DateScriptsMockPlugin.class);
}
public void testDateMath() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
DateRangeAggregatorBuilder rangeBuilder = dateRange("range");
if (randomBoolean()) {
rangeBuilder.field("date");
} else {
rangeBuilder.script(new Script("doc['date'].value"));
rangeBuilder.script(new Script(DateScriptMocks.ExtractFieldScript.NAME, ScriptType.INLINE, "native", params));
}
SearchResponse response = client()
.prepareSearch("idx")
.addAggregation(
rangeBuilder.addUnboundedTo("a long time ago", "now-50y").addRange("recently", "now-50y", "now-1y")
.addUnboundedFrom("last year", "now-1y")).execute().actionGet();
.addUnboundedFrom("last year", "now-1y").timeZone(DateTimeZone.forID("EST"))).execute().actionGet();
assertSearchResponse(response);
@ -286,17 +295,25 @@ public class DateRangeTests extends ESIntegTestCase {
}
public void testSingleValueFieldWithDateMath() throws Exception {
String[] ids = DateTimeZone.getAvailableIDs().toArray(new String[DateTimeZone.getAvailableIDs().size()]);
DateTimeZone timezone = DateTimeZone.forID(randomFrom(ids));
int timeZoneOffset = timezone.getOffset(date(2, 15));
// if time zone is UTC (or equivalent), time zone suffix is "Z", else something like "+03:00", which we get with the "ZZ" format
String feb15Suffix = timeZoneOffset == 0 ? "Z" : date(2,15, timezone).toString("ZZ");
String mar15Suffix = timeZoneOffset == 0 ? "Z" : date(3,15, timezone).toString("ZZ");
long expectedFirstBucketCount = timeZoneOffset < 0 ? 3L : 2L;
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateRange("range")
.field("date")
.addUnboundedTo("2012-02-15")
.addRange("2012-02-15", "2012-02-15||+1M")
.addUnboundedFrom("2012-02-15||+1M"))
.addUnboundedFrom("2012-02-15||+1M")
.timeZone(timezone))
.execute().actionGet();
assertSearchResponse(response);
Range range = response.getAggregations().get("range");
assertThat(range, notNullValue());
assertThat(range.getName(), equalTo("range"));
@ -305,30 +322,31 @@ public class DateRangeTests extends ESIntegTestCase {
Range.Bucket bucket = buckets.get(0);
assertThat(bucket, notNullValue());
assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z"));
assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000" + feb15Suffix));
assertThat(((DateTime) bucket.getFrom()), nullValue());
assertThat(((DateTime) bucket.getTo()), equalTo(date(2, 15)));
assertThat(((DateTime) bucket.getTo()), equalTo(date(2, 15, timezone).toDateTime(DateTimeZone.UTC)));
assertThat(bucket.getFromAsString(), nullValue());
assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z"));
assertThat(bucket.getDocCount(), equalTo(2L));
assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000" + feb15Suffix));
assertThat(bucket.getDocCount(), equalTo(expectedFirstBucketCount));
bucket = buckets.get(1);
assertThat(bucket, notNullValue());
assertThat((String) bucket.getKey(), equalTo("2012-02-15T00:00:00.000Z-2012-03-15T00:00:00.000Z"));
assertThat(((DateTime) bucket.getFrom()), equalTo(date(2, 15)));
assertThat(((DateTime) bucket.getTo()), equalTo(date(3, 15)));
assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000Z"));
assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000Z"));
assertThat((String) bucket.getKey(), equalTo("2012-02-15T00:00:00.000" + feb15Suffix +
"-2012-03-15T00:00:00.000" + mar15Suffix));
assertThat(((DateTime) bucket.getFrom()), equalTo(date(2, 15, timezone).toDateTime(DateTimeZone.UTC)));
assertThat(((DateTime) bucket.getTo()), equalTo(date(3, 15, timezone).toDateTime(DateTimeZone.UTC)));
assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000" + feb15Suffix));
assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000" + mar15Suffix));
assertThat(bucket.getDocCount(), equalTo(2L));
bucket = buckets.get(2);
assertThat(bucket, notNullValue());
assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*"));
assertThat(((DateTime) bucket.getFrom()), equalTo(date(3, 15)));
assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000" + mar15Suffix + "-*"));
assertThat(((DateTime) bucket.getFrom()), equalTo(date(3, 15, timezone).toDateTime(DateTimeZone.UTC)));
assertThat(((DateTime) bucket.getTo()), nullValue());
assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z"));
assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000" + mar15Suffix));
assertThat(bucket.getToAsString(), nullValue());
assertThat(bucket.getDocCount(), equalTo(numDocs - 4L));
assertThat(bucket.getDocCount(), equalTo(numDocs - 2L - expectedFirstBucketCount));
}
public void testSingleValueFieldWithCustomKey() throws Exception {
@ -520,10 +538,12 @@ public class DateRangeTests extends ESIntegTestCase {
public void testMultiValuedFieldWithValueScript() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "dates");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateRange("range")
.field("dates")
.script(new Script("new DateTime(_value.longValue(), DateTimeZone.UTC).plusMonths(1).getMillis()"))
.script(new Script(DateScriptMocks.PlusOneMonthScript.NAME, ScriptType.INLINE, "native", params))
.addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15))).execute()
.actionGet();
@ -575,9 +595,11 @@ public class DateRangeTests extends ESIntegTestCase {
*/
public void testScriptSingleValue() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "date");
SearchResponse response = client().prepareSearch("idx")
.addAggregation(dateRange("range")
.script(new Script("doc['date'].value"))
.script(new Script(DateScriptMocks.ExtractFieldScript.NAME, ScriptType.INLINE, "native", params))
.addUnboundedTo(date(2, 15))
.addRange(date(2, 15), date(3, 15))
.addUnboundedFrom(date(3, 15)))
@ -634,11 +656,14 @@ public class DateRangeTests extends ESIntegTestCase {
*/
public void testScriptMultiValued() throws Exception {
Map<String, Object> params = new HashMap<>();
params.put("fieldname", "dates");
SearchResponse response = client()
.prepareSearch("idx")
.addAggregation(
dateRange("range").script(new Script("doc['dates'].values")).addUnboundedTo(date(2, 15))
.addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15))).execute().actionGet();
dateRange("range").script(new Script(DateScriptMocks.ExtractFieldScript.NAME, ScriptType.INLINE, "native", params))
.addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15))
.addUnboundedFrom(date(3, 15))).execute().actionGet();
assertSearchResponse(response);

View File

@ -22,9 +22,12 @@ package org.elasticsearch.search.aggregations.bucket;
import org.elasticsearch.search.aggregations.BaseAggregationTestCase;
import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator.Range;
import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeAggregatorBuilder;
import org.joda.time.DateTimeZone;
public class DateRangeTests extends BaseAggregationTestCase<DateRangeAggregatorBuilder> {
private final static String[] timeZoneIds = DateTimeZone.getAvailableIDs().toArray(new String[DateTimeZone.getAvailableIDs().size()]);
@Override
protected DateRangeAggregatorBuilder createTestAggregatorBuilder() {
int numRanges = randomIntBetween(1, 10);
@ -56,6 +59,9 @@ public class DateRangeTests extends BaseAggregationTestCase<DateRangeAggregatorB
if (randomBoolean()) {
factory.missing(randomIntBetween(0, 10));
}
if (randomBoolean()) {
factory.timeZone(DateTimeZone.forID(randomFrom(timeZoneIds)));
}
return factory;
}

View File

@ -0,0 +1,133 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.AbstractSearchScript;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.NativeScriptFactory;
import org.elasticsearch.script.ScriptModule;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.util.HashMap;
import java.util.Map;
/**
* Mock scripts shared by DateRangeIT and DateHistogramIT
*/
public class DateScriptMocks {
/**
* Mock plugin for the {@link DateScriptMocks.ExtractFieldScript} and {@link DateScriptMocks.PlusOneMonthScript}
*/
public static class DateScriptsMockPlugin extends Plugin {
@Override
public String name() {
return "DateScriptMocks";
}
@Override
public String description() {
return "A mock script plugin.";
}
public void onModule(ScriptModule module) {
module.registerScript(ExtractFieldScript.NAME, ExtractFieldScriptFactory.class);
module.registerScript(PlusOneMonthScript.NAME, PlusOneMonthScriptFactory.class);
}
}
public static class ExtractFieldScriptFactory implements NativeScriptFactory {
@Override
public ExecutableScript newScript(@Nullable Map<String, Object> params) {
return new ExtractFieldScript((String) params.get("fieldname"));
}
@Override
public boolean needsScores() {
return false;
}
}
public static class ExtractFieldScript extends AbstractSearchScript {
public static final String NAME = "extract_field";
private String fieldname;
public ExtractFieldScript(String fieldname) {
this.fieldname = fieldname;
}
@Override
public Object run() {
return doc().get(fieldname);
}
}
public static class PlusOneMonthScriptFactory implements NativeScriptFactory {
@Override
public ExecutableScript newScript(Map<String, Object> params) {
return new PlusOneMonthScript((String) params.get("fieldname"));
}
@Override
public boolean needsScores() {
return false;
}
}
/**
* This mock script takes date field value and adds one month to the returned date
*/
public static class PlusOneMonthScript extends AbstractSearchScript {
public static final String NAME = "date_plus_1_month";
private String fieldname;
private Map<String, Object> vars = new HashMap<>();
public PlusOneMonthScript(String fieldname) {
this.fieldname = fieldname;
}
@Override
public void setNextVar(String name, Object value) {
vars.put(name, value);
}
@Override
public long runAsLong() {
return new DateTime((long) vars.get("_value"), DateTimeZone.UTC).plusMonths(1).getMillis();
}
@Override
public double runAsDouble() {
return new DateTime(new Double((double) vars.get("_value")).longValue(), DateTimeZone.UTC).plusMonths(1).getMillis();
}
@Override
public Object run() {
return new UnsupportedOperationException();
}
}
}

View File

@ -39,7 +39,6 @@ import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.core.StringFieldMapper;
import org.elasticsearch.index.mapper.core.TextFieldMapper;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;

View File

@ -111,3 +111,35 @@ Zone:: 'Z' outputs offset without a colon, 'ZZ' outputs the offset with a colon,
Zone names:: Time zone names ('z') cannot be parsed.
Any characters in the pattern that are not in the ranges of ['a'..'z'] and ['A'..'Z'] will be treated as quoted text. For instance, characters like ':', '.', ' ', '#' and '?' will appear in the resulting time text even they are not embraced within single quotes.
[[time-zones]]
==== Time zone in date range aggregations
Dates can be converted from another time zone to UTC by specifying the `time_zone` parameter.
Time zones may either be specified as an ISO 8601 UTC offset (e.g. +01:00 or -08:00) or as one of
the the http://joda-time.sourceforge.net/timezones.html[time zone ids] from the TZ database.
The `time_zone` parameter is also applied to rounding in date math expressions. As an example,
to round to the beginning of the day in the CET time zone, you can do the following:
[source,js]
--------------------------------------------------
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"time_zone": "CET",
"ranges": [
{ "to": "2016-02-15/d" }, <1>
{ "from": "2016-02-15/d", "to" : "now/d" <2>},
{ "from": "now/d" },
]
}
}
}
}
--------------------------------------------------
<1> This date will be converted to `2016-02-15T00:00:00.000+01:00`.
<2> `now/d` will be rounded to the beginning of the day in the CET time zone.