diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java index a149d357e0759..4cfde6df0f867 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/DateRangeIT.java @@ -532,319 +532,6 @@ public void testMultiValuedField() throws Exception { assertThat(bucket.getDocCount(), equalTo(numDocs - 2L)); } - /* - Feb 2, Mar 3, 1 - Mar 2, Apr 3, 2 - Mar 15, Apr 16, 3 - Apr 2, May 3, 4 - Apr 15, May 16 5 - Apr 23, May 24 6 - */ - - - public void testMultiValuedFieldWithValueScript() throws Exception { - Map params = new HashMap<>(); - params.put("fieldname", "dates"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateRange("range") - .field("dates") - .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.DOUBLE_PLUS_ONE_MONTH, params)) - .addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)).addUnboundedFrom(date(3, 15))).get(); - - assertSearchResponse(response); - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(1L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(2L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(numDocs - 1L)); - } - - - - /* - Feb 2, Mar 3, 1 - Mar 2, Apr 3, 2 - Mar 15, Apr 16, 3 - Apr 2, May 3, 4 - Apr 15, May 16 5 - Apr 23, May 24 6 - */ - - public void testScriptSingleValue() throws Exception { - Map params = new HashMap<>(); - params.put("fieldname", "date"); - SearchResponse response = client().prepareSearch("idx") - .addAggregation(dateRange("range") - .script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) - .addUnboundedTo(date(2, 15)) - .addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15))) - .get(); - - assertSearchResponse(response); - - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(2L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(2L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(numDocs - 4L)); - } - - - - - - /* - Jan 2, Feb 3, 1 - Feb 2, Mar 3, 2 - Feb 15, Mar 16, 3 - Mar 2, Apr 3, 4 - Mar 15, Apr 16 5 - Mar 23, Apr 24 6 - */ - - public void testScriptMultiValued() throws Exception { - Map params = new HashMap<>(); - params.put("fieldname", "dates"); - SearchResponse response = client() - .prepareSearch("idx") - .addAggregation( - dateRange("range").script(new Script(ScriptType.INLINE, "mockscript", DateScriptMocksPlugin.EXTRACT_FIELD, params)) - .addUnboundedTo(date(2, 15)).addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15))).get(); - - assertSearchResponse(response); - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(2L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(3L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(numDocs - 2L)); - } - - public void testUnmapped() throws Exception { - client().admin().cluster().prepareHealth("idx_unmapped").setWaitForYellowStatus().get(); - - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(dateRange("range") - .field("date") - .addUnboundedTo(date(2, 15)) - .addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15))) - .get(); - - assertSearchResponse(response); - - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(0L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(0L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(0L)); - } - - public void testUnmappedWithStringDates() throws Exception { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(dateRange("range") - .field("date") - .addUnboundedTo("2012-02-15") - .addRange("2012-02-15", "2012-03-15") - .addUnboundedFrom("2012-03-15")) - .get(); - - assertSearchResponse(response); - - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(0L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(0L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(0L)); - } - - public void testPartiallyUnmapped() throws Exception { - SearchResponse response = client().prepareSearch("idx", "idx_unmapped") - .addAggregation(dateRange("range") - .field("date") - .addUnboundedTo(date(2, 15)) - .addRange(date(2, 15), date(3, 15)) - .addUnboundedFrom(date(3, 15))) - .get(); - - assertSearchResponse(response); - - - Range range = response.getAggregations().get("range"); - assertThat(range, notNullValue()); - assertThat(range.getName(), equalTo("range")); - List buckets = range.getBuckets(); - assertThat(buckets.size(), equalTo(3)); - - Range.Bucket bucket = buckets.get(0); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); - assertThat(((ZonedDateTime) bucket.getFrom()), nullValue()); - assertThat(((ZonedDateTime) bucket.getTo()), equalTo(date(2, 15))); - assertThat(bucket.getFromAsString(), nullValue()); - assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); - assertThat(bucket.getDocCount(), equalTo(2L)); - - 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(((ZonedDateTime) bucket.getFrom()), equalTo(date(2, 15))); - assertThat(((ZonedDateTime) 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(bucket.getDocCount(), equalTo(2L)); - - bucket = buckets.get(2); - assertThat(bucket, notNullValue()); - assertThat((String) bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); - assertThat(((ZonedDateTime) bucket.getFrom()), equalTo(date(3, 15))); - assertThat(((ZonedDateTime) bucket.getTo()), nullValue()); - assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); - assertThat(bucket.getToAsString(), nullValue()); - assertThat(bucket.getDocCount(), equalTo(numDocs - 4L)); - } - public void testEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") .setQuery(matchAllQuery()) diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregatorTests.java index 2fb281170b732..2862625a685c6 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregatorTests.java @@ -21,274 +21,607 @@ import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.document.SortedNumericDocValuesField; -import org.apache.lucene.document.SortedSetDocValuesField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.CheckedConsumer; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.mapper.BooleanFieldMapper; import org.elasticsearch.index.mapper.DateFieldMapper; -import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.NumberFieldMapper; +import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType; +import org.elasticsearch.script.JodaCompatibleZonedDateTime; +import org.elasticsearch.script.MockScriptEngine; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptEngine; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorTestCase; import org.elasticsearch.search.aggregations.support.AggregationInspectionHelper; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; +import org.elasticsearch.search.lookup.LeafDocLookup; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.core.IsNull.nullValue; public class DateRangeAggregatorTests extends AggregatorTestCase { - private String NUMBER_FIELD_NAME = "number"; - private String UNMAPPED_FIELD_NAME = "field_not_appearing_in_this_index"; - private String DATE_FIELD_NAME = "date"; - - private long milli1 = ZonedDateTime.of(2015, 11, 13, 16, 14, 34, 0, ZoneOffset.UTC).toInstant().toEpochMilli(); - private long milli2 = ZonedDateTime.of(2016, 11, 13, 16, 14, 34, 0, ZoneOffset.UTC).toInstant().toEpochMilli(); + private static final String NUMBER_FIELD_NAME = "number"; + private static final String DATE_FIELD_NAME = "date"; + + private static final long milli1 = ZonedDateTime.of(2015, 11, 13, 16, 14, 34, 0, ZoneOffset.UTC).toInstant().toEpochMilli(); + private static final long milli2 = ZonedDateTime.of(2016, 11, 13, 16, 14, 34, 0, ZoneOffset.UTC).toInstant().toEpochMilli(); + + private static final List> DATE_FIELD_VALUES = List.of( + List.of( + ZonedDateTime.of(2012, 1, 2, 0, 0, 0, 0, ZoneOffset.UTC), + ZonedDateTime.of(2012, 2, 3, 0, 0, 0, 0, ZoneOffset.UTC) + ), + List.of( + ZonedDateTime.of(2012, 2, 2, 0, 0, 0, 0, ZoneOffset.UTC), + ZonedDateTime.of(2012, 3, 3, 0, 0, 0, 0, ZoneOffset.UTC) + ), + List.of( + ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC), + ZonedDateTime.of(2012, 3, 16, 0, 0, 0, 0, ZoneOffset.UTC) + ), + List.of( + ZonedDateTime.of(2012, 3, 2, 0, 0, 0, 0, ZoneOffset.UTC), + ZonedDateTime.of(2012, 4, 3, 0, 0, 0, 0, ZoneOffset.UTC) + ) + ); + + private static final String VALUE_SCRIPT_NAME = "value_script"; + private static final String FIELD_SCRIPT_NAME = "field_script"; public void testNoMatchingField() throws IOException { - testBothResolutions(new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 7))); - iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 2))); - iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 3))); - }, range -> { - List ranges = range.getBuckets(); - assertEquals(2, ranges.size()); - assertEquals(0, ranges.get(0).getDocCount()); - assertEquals(0, ranges.get(1).getDocCount()); - assertFalse(AggregationInspectionHelper.hasValue(range)); - }); + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("test_range_agg"); + aggregationBuilder.field(DATE_FIELD_NAME); + aggregationBuilder.addRange("2015-01-01", "2015-12-31"); + aggregationBuilder.addRange("2019-01-01", "2019-12-31"); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 7))); + iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 2))); + iw.addDocument(singleton(new SortedNumericDocValuesField("bogus_field_name", 3))); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(2, ranges.size()); + assertEquals(0, ranges.get(0).getDocCount()); + assertEquals(0, ranges.get(1).getDocCount()); + assertFalse(AggregationInspectionHelper.hasValue(range)); + } + ); } public void testMatchesSortedNumericDocValues() throws IOException { - testBothResolutions(new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli1))); - iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli2))); - }, range -> { - List ranges = range.getBuckets(); - assertEquals(2, ranges.size()); - assertEquals(1, ranges.get(0).getDocCount()); - assertEquals(0, ranges.get(1).getDocCount()); - assertTrue(AggregationInspectionHelper.hasValue(range)); - }); + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("test_range_agg"); + aggregationBuilder.field(DATE_FIELD_NAME); + aggregationBuilder.addRange("2015-01-01", "2015-12-31"); + aggregationBuilder.addRange("2019-01-01", "2019-12-31"); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli1))); + iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli2))); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(2, ranges.size()); + assertEquals(1, ranges.get(0).getDocCount()); + assertEquals(0, ranges.get(1).getDocCount()); + assertTrue(AggregationInspectionHelper.hasValue(range)); + } + ); } public void testMatchesNumericDocValues() throws IOException { - testBothResolutions(new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new NumericDocValuesField(DATE_FIELD_NAME, milli1))); - iw.addDocument(singleton(new NumericDocValuesField(DATE_FIELD_NAME, milli2))); - }, range -> { - List ranges = range.getBuckets(); - assertEquals(2, ranges.size()); - assertEquals(1, ranges.get(0).getDocCount()); - assertEquals(0, ranges.get(1).getDocCount()); - assertTrue(AggregationInspectionHelper.hasValue(range)); - }); - } + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("test_range_agg"); + aggregationBuilder.field(DATE_FIELD_NAME); + aggregationBuilder.addRange("2015-01-01", "2015-12-31"); + aggregationBuilder.addRange("2019-01-01", "2019-12-31"); - public void testMissingDateStringWithDateField() throws IOException { - DateFieldMapper.Builder builder = new DateFieldMapper.Builder(DATE_FIELD_NAME) - .withResolution(DateFieldMapper.Resolution.MILLISECONDS); - DateFieldMapper.DateFieldType fieldType = builder.fieldType(); - fieldType.setHasDocValues(true); - fieldType.setName(DATE_FIELD_NAME); + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new NumericDocValuesField(DATE_FIELD_NAME, milli1))); + iw.addDocument(singleton(new NumericDocValuesField(DATE_FIELD_NAME, milli2))); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(2, ranges.size()); + assertEquals(1, ranges.get(0).getDocCount()); + assertEquals(0, ranges.get(1).getDocCount()); + assertTrue(AggregationInspectionHelper.hasValue(range)); + } + ); + } - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field(DATE_FIELD_NAME) - .missing("2015-11-13T16:14:34") - .addRange("2015-11-13", "2015-11-14"); + public void testMissingDateStringWithDateField() throws IOException { + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field(DATE_FIELD_NAME) + .missing("2015-11-13T16:14:34") + .addRange("2015-11-13", "2015-11-14"); - testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli1))); iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, milli2))); // Missing will apply to this document - iw.addDocument(singleton(new SortedNumericDocValuesField(NUMBER_FIELD_NAME, 7))); - }, range -> { + iw.addDocument(emptySet()); + }, + range -> { List ranges = range.getBuckets(); assertEquals(1, ranges.size()); assertEquals(2, ranges.get(0).getDocCount()); assertTrue(AggregationInspectionHelper.hasValue(range)); - }, fieldType); - } - - public void testNumberFieldDateRanges() throws IOException { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field(NUMBER_FIELD_NAME) - .addRange("2015-11-13", "2015-11-14"); - - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); - - expectThrows(NumberFormatException.class, - () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> fail("Should have thrown exception"), fieldType)); + } + ); } - public void testNumberFieldNumberRanges() throws IOException { + public void testNumberFieldNumberRanges() throws IOException { DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") .field(NUMBER_FIELD_NAME) .addRange(0, 5); - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); - - testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> { - List ranges = range.getBuckets(); - assertEquals(1, ranges.size()); - assertEquals(1, ranges.get(0).getDocCount()); - assertTrue(AggregationInspectionHelper.hasValue(range)); - }, fieldType); + numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(1, ranges.size()); + assertEquals(1, ranges.get(0).getDocCount()); + assertTrue(AggregationInspectionHelper.hasValue(range)); + } + ); } - public void testMissingDateStringWithNumberField() throws IOException { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field(NUMBER_FIELD_NAME) - .addRange("2015-11-13", "2015-11-14") - .missing("1979-01-01T00:00:00"); - - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); - - expectThrows(NumberFormatException.class, - () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { + public void testUnmapped() throws IOException { + final List> rangeTypes = List.of( + builder -> builder.addRange("2015-01-01", "2015-12-31"), + builder -> builder.addRange( + ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC), + ZonedDateTime.of(2015, 12, 31, 0, 0, 0, 0, ZoneOffset.UTC) + ) + ); + + for (Consumer rangeType : rangeTypes) { + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field("does_not_exist"); + rangeType.accept(aggregationBuilder); + + numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> fail("Should have thrown exception"), fieldType)); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(1, ranges.size()); + assertEquals(0, ranges.get(0).getDocCount()); + assertFalse(AggregationInspectionHelper.hasValue(range)); + } + ); } + } - public void testUnmappedWithMissingNumber() throws IOException { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field("does_not_exist") - .addRange("2015-11-13", "2015-11-14") - .missing(1447438575000L); // 2015-11-13 6:16:15 - - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); - - testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { + public void testUnmappedWithMissingNumber() throws IOException { + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field("does_not_exist") + .addRange("2015-11-13", "2015-11-14") + .missing(1447438575000L); // 2015-11-13 6:16:15 + + numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> { + }, + range -> { List ranges = range.getBuckets(); assertEquals(1, ranges.size()); assertEquals(2, ranges.get(0).getDocCount()); assertTrue(AggregationInspectionHelper.hasValue(range)); - }, fieldType); - } + } + ); + } - public void testUnmappedWithMissingDate() throws IOException { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field("does_not_exist") - .addRange("2015-11-13", "2015-11-14") - .missing("2015-11-13T10:11:12"); + public void testUnmappedWithMissingDate() throws IOException { + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field("does_not_exist") + .addRange("2015-11-13", "2015-11-14") + .missing("2015-11-13T10:11:12"); + + numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); + }, + range -> { + List ranges = range.getBuckets(); + assertEquals(1, ranges.size()); + assertEquals(2, ranges.get(0).getDocCount()); + assertTrue(AggregationInspectionHelper.hasValue(range)); + } + ); + } - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); + public void testBadMissingField() { + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field(NUMBER_FIELD_NAME) + .addRange("2020-01-01T00:00:00", "2020-01-02T00:00:00") + .missing("bogus"); + + expectThrows( + NumberFormatException.class, + () -> numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); + iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); + }, + range -> fail("Should have thrown exception") + ) + ); + } - testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { + public void testUnmappedWithBadMissingField() { + DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field("does_not_exist") + .addRange("2020-01-01T00:00:00", "2020-01-02T00:00:00") + .missing("bogus"); + + expectThrows( + ElasticsearchParseException.class, + () -> numberFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> { - List ranges = range.getBuckets(); - assertEquals(1, ranges.size()); - assertEquals(2, ranges.get(0).getDocCount()); - assertTrue(AggregationInspectionHelper.hasValue(range)); - }, fieldType); - } + }, + range -> fail("Should have thrown exception") + ) + ); + } - public void testKeywordField() { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field("not_a_number") - .addRange("2015-11-13", "2015-11-14"); - - MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType(); - fieldType.setName("not_a_number"); - fieldType.setHasDocValues(true); - - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new SortedSetDocValuesField("string", new BytesRef("foo")))); - }, range -> fail("Should have thrown exception"), fieldType)); - assertEquals("Field [not_a_number] of type [keyword(indexed,tokenized)] is not supported for aggregation [date_range]", - e.getMessage()); - } + public void testPartiallyUnmapped() throws IOException { + final MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER); + fieldType.setName(NUMBER_FIELD_NAME); - public void testBadMissingField() { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field(NUMBER_FIELD_NAME) - .addRange("2020-01-01T00:00:00", "2020-01-02T00:00:00") - .missing("bogus"); + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field(fieldType.name()) + .addRange(0, 5); - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); + try (Directory mappedDirectory = newDirectory(); Directory unmappedDirectory = newDirectory()) { + try (RandomIndexWriter mappedWriter = new RandomIndexWriter(random(), mappedDirectory)) { + mappedWriter.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); + mappedWriter.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); + } - expectThrows(NumberFormatException.class, - () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> fail("Should have thrown exception"), fieldType)); - } + new RandomIndexWriter(random(), unmappedDirectory).close(); - public void testUnmappedWithBadMissingField() { - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") - .field("does_not_exist") - .addRange("2020-01-01T00:00:00", "2020-01-02T00:00:00") - .missing("bogus"); + try (IndexReader mappedReader = DirectoryReader.open(mappedDirectory); + IndexReader unmappedReader = DirectoryReader.open(unmappedDirectory); + MultiReader multiReader = new MultiReader(mappedReader, unmappedReader)) { - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.INTEGER); - fieldType.setName(NUMBER_FIELD_NAME); + final IndexSearcher searcher = newSearcher(multiReader, true, true); - expectThrows(ElasticsearchParseException.class, - () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> { - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 7))); - iw.addDocument(singleton(new NumericDocValuesField(NUMBER_FIELD_NAME, 1))); - }, range -> fail("Should have thrown exception"), fieldType)); + final InternalRange result = + searchAndReduce(searcher, new MatchAllDocsQuery(), aggregationBuilder, fieldType); + final List ranges = result.getBuckets(); + assertEquals(1, ranges.size()); + assertEquals(1, ranges.get(0).getDocCount()); + assertTrue(AggregationInspectionHelper.hasValue(result)); + } } + } - private void testBothResolutions(Query query, - CheckedConsumer buildIndex, - Consumer> verify) - throws IOException { - testCase(query, buildIndex, verify, DateFieldMapper.Resolution.MILLISECONDS); - testCase(query, buildIndex, verify, DateFieldMapper.Resolution.NANOSECONDS); + public void testValueScriptSingleValuedField() throws IOException { + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field(DATE_FIELD_NAME) + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, VALUE_SCRIPT_NAME, emptyMap())); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + for (List values : DATE_FIELD_VALUES) { + iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, values.get(0).toInstant().toEpochMilli()))); + } + }, + range -> { + final List ranges = range.getBuckets(); + assertThat(ranges, hasSize(3)); + + { + final Range.Bucket bucket = ranges.get(0); + assertThat(bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), nullValue()); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), nullValue()); + assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(1); + assertThat(bucket.getKey(), equalTo("2012-02-15T00:00:00.000Z-2012-03-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(2); + assertThat(bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), nullValue()); + assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), nullValue()); + assertThat(bucket.getDocCount(), equalTo(2L)); + } + } + ); } - private void testCase(Query query, - CheckedConsumer buildIndex, - Consumer> verify, - DateFieldMapper.Resolution resolution) throws IOException { - DateFieldMapper.Builder builder = new DateFieldMapper.Builder(DATE_FIELD_NAME) + public void testValueScriptMultiValuedField() throws IOException { + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .field(DATE_FIELD_NAME) + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, VALUE_SCRIPT_NAME, emptyMap())); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + for (List values : DATE_FIELD_VALUES) { + final Set document = values.stream() + .map(value -> new SortedNumericDocValuesField(DATE_FIELD_NAME, value.toInstant().toEpochMilli())) + .collect(toSet()); + iw.addDocument(document); + } + }, + range -> { + final List ranges = range.getBuckets(); + assertThat(ranges, hasSize(3)); + + { + final Range.Bucket bucket = ranges.get(0); + assertThat(bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), nullValue()); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), nullValue()); + assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(1); + assertThat(bucket.getKey(), equalTo("2012-02-15T00:00:00.000Z-2012-03-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(2L)); + } + + { + final Range.Bucket bucket = ranges.get(2); + assertThat(bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), nullValue()); + assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), nullValue()); + assertThat(bucket.getDocCount(), equalTo(3L)); + } + } + ); + } + + public void testFieldScriptSingleValuedField() throws IOException { + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, FIELD_SCRIPT_NAME, singletonMap("field", DATE_FIELD_NAME))); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + for (List values : DATE_FIELD_VALUES) { + iw.addDocument(singleton(new SortedNumericDocValuesField(DATE_FIELD_NAME, values.get(0).toInstant().toEpochMilli()))); + } + }, + range -> { + final List ranges = range.getBuckets(); + assertThat(ranges, hasSize(3)); + + { + final Range.Bucket bucket = ranges.get(0); + assertThat(bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), nullValue()); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), nullValue()); + assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(1); + assertThat(bucket.getKey(), equalTo("2012-02-15T00:00:00.000Z-2012-03-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(2); + assertThat(bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), nullValue()); + assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), nullValue()); + assertThat(bucket.getDocCount(), equalTo(2L)); + } + } + ); + } + + public void testFieldScriptMultiValuedField() throws IOException { + final DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("date_range") + .addUnboundedTo("2012-02-15") + .addRange("2012-02-15", "2012-03-15") + .addUnboundedFrom("2012-03-15") + .script(new Script(ScriptType.INLINE, MockScriptEngine.NAME, FIELD_SCRIPT_NAME, singletonMap("field", DATE_FIELD_NAME))); + + dateFieldTestCase( + aggregationBuilder, + new MatchAllDocsQuery(), + iw -> { + for (List values : DATE_FIELD_VALUES) { + final Set document = values.stream() + .map(value -> new SortedNumericDocValuesField(DATE_FIELD_NAME, value.toInstant().toEpochMilli())) + .collect(toSet()); + iw.addDocument(document); + } + }, + range -> { + final List ranges = range.getBuckets(); + assertThat(ranges, hasSize(3)); + + { + final Range.Bucket bucket = ranges.get(0); + assertThat(bucket.getKey(), equalTo("*-2012-02-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), nullValue()); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), nullValue()); + assertThat(bucket.getToAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + + { + final Range.Bucket bucket = ranges.get(1); + assertThat(bucket.getKey(), equalTo("2012-02-15T00:00:00.000Z-2012-03-15T00:00:00.000Z")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 2, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getFromAsString(), equalTo("2012-02-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getDocCount(), equalTo(2L)); + } + + { + final Range.Bucket bucket = ranges.get(2); + assertThat(bucket.getKey(), equalTo("2012-03-15T00:00:00.000Z-*")); + assertThat(bucket.getFrom(), equalTo(ZonedDateTime.of(2012, 3, 15, 0, 0, 0, 0, ZoneOffset.UTC))); + assertThat(bucket.getTo(), nullValue()); + assertThat(bucket.getFromAsString(), equalTo("2012-03-15T00:00:00.000Z")); + assertThat(bucket.getToAsString(), nullValue()); + assertThat(bucket.getDocCount(), equalTo(3L)); + } + } + ); + } + + private void dateFieldTestCase(DateRangeAggregationBuilder aggregationBuilder, + Query query, + CheckedConsumer buildIndex, + Consumer> verify) + throws IOException { + dateFieldTestCaseWithResolution(aggregationBuilder, query, buildIndex, verify, DateFieldMapper.Resolution.MILLISECONDS); + dateFieldTestCaseWithResolution(aggregationBuilder, query, buildIndex, verify, DateFieldMapper.Resolution.NANOSECONDS); + } + + private void dateFieldTestCaseWithResolution(DateRangeAggregationBuilder aggregationBuilder, + Query query, + CheckedConsumer buildIndex, + Consumer> verify, + DateFieldMapper.Resolution resolution) throws IOException { + final DateFieldMapper.Builder builder = new DateFieldMapper.Builder(DATE_FIELD_NAME) .withResolution(resolution); - DateFieldMapper.DateFieldType fieldType = builder.fieldType(); + final DateFieldMapper.DateFieldType fieldType = builder.fieldType(); fieldType.setHasDocValues(true); fieldType.setName(DATE_FIELD_NAME); - DateRangeAggregationBuilder aggregationBuilder = new DateRangeAggregationBuilder("test_range_agg"); - aggregationBuilder.field(DATE_FIELD_NAME); - aggregationBuilder.addRange("2015-01-01", "2015-12-31"); - aggregationBuilder.addRange("2019-01-01", "2019-12-31"); - testCase(aggregationBuilder, query, buildIndex, verify, fieldType); + testCase(aggregationBuilder, query, buildIndex, verify, singleton(fieldType)); + } + + private void numberFieldTestCase(DateRangeAggregationBuilder aggregationBuilder, + Query query, + CheckedConsumer buildIndex, + Consumer> verify) + throws IOException { + final MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberType.INTEGER); + fieldType.setName(NUMBER_FIELD_NAME); + testCase(aggregationBuilder, query, buildIndex, verify, singleton(fieldType)); } private void testCase(DateRangeAggregationBuilder aggregationBuilder, Query query, CheckedConsumer buildIndex, Consumer> verify, - MappedFieldType fieldType) throws IOException { + Collection fieldTypes) throws IOException { try (Directory directory = newDirectory()) { RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); buildIndex.accept(indexWriter); @@ -297,11 +630,50 @@ private void testCase(DateRangeAggregationBuilder aggregationBuilder, try (IndexReader indexReader = DirectoryReader.open(directory)) { IndexSearcher indexSearcher = newSearcher(indexReader, true, true); - InternalRange agg = searchAndReduce(indexSearcher, - query, aggregationBuilder, fieldType); + final MappedFieldType[] fieldTypeArray = fieldTypes.toArray(new MappedFieldType[0]); + final InternalRange agg = searchAndReduce( + indexSearcher, query, aggregationBuilder, fieldTypeArray); verify.accept(agg); } } } + + @Override + protected List getSupportedValuesSourceTypes() { + return singletonList(CoreValuesSourceType.NUMERIC); + } + + @Override + protected List unsupportedMappedFieldTypes() { + final List unsupported = new ArrayList<>(singleton(BooleanFieldMapper.CONTENT_TYPE)); + unsupported.addAll(Arrays.stream(NumberType.values()) + .map(numberType -> new NumberFieldMapper.NumberFieldType(numberType).typeName()) + .collect(toList())); + return unsupported; + } + + @Override + protected AggregationBuilder createAggBuilderForTypeTest(MappedFieldType fieldType, String fieldName) { + return new DateRangeAggregationBuilder("_name") + .field(fieldName) + .addRange("2015-01-01", "2015-12-31"); + } + + @Override + protected ScriptService getMockScriptService() { + final Map, Object>> scripts = Map.of( + VALUE_SCRIPT_NAME, vars -> new DateTime(((Number) vars.get("_value")).longValue(), DateTimeZone.UTC).plusMonths(1).getMillis(), + FIELD_SCRIPT_NAME, vars -> { + final String fieldName = (String) vars.get("field"); + final LeafDocLookup lookup = (LeafDocLookup) vars.get("doc"); + return lookup.get(fieldName).stream() + .map(value -> ((JodaCompatibleZonedDateTime) value).plusMonths(1)) + .collect(toList()); + } + ); + final MockScriptEngine engine = new MockScriptEngine(MockScriptEngine.NAME, scripts, emptyMap()); + final Map engines = singletonMap(engine.getType(), engine); + return new ScriptService(Settings.EMPTY, engines, ScriptModule.CORE_CONTEXTS); + } } diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index 6b3e0cde294d9..3b3385cf3e532 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -414,7 +414,9 @@ protected A search(IndexSe a.postCollection(); @SuppressWarnings("unchecked") A internalAgg = (A) a.buildAggregation(0L); - InternalAggregationTestCase.assertMultiBucketConsumer(internalAgg, bucketConsumer); + if (false == (a instanceof NonCollectingAggregator)) { + InternalAggregationTestCase.assertMultiBucketConsumer(internalAgg, bucketConsumer); + } return internalAgg; } @@ -485,7 +487,10 @@ protected A searchAndReduc a.postCollection(); InternalAggregation agg = a.buildAggregation(0L); aggs.add(agg); - InternalAggregationTestCase.assertMultiBucketConsumer(agg, shardBucketConsumer); + + if (false == (a instanceof NonCollectingAggregator)) { + InternalAggregationTestCase.assertMultiBucketConsumer(agg, shardBucketConsumer); + } } if (aggs.isEmpty()) { return null;