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:
commit
350e3a4850
|
@ -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" />
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue