### Description

Our most common aggregations should be optimized for single-valued fields. In general, our aggregations look like this:

```java
    @Override
    public LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucketCollector sub) throws IOException {
        if (valuesSource == null) {
            return LeafBucketCollector.NO_OP_COLLECTOR;
        }
        SortedNumericDocValues values = valuesSource.longValues(ctx);
        return new LeafBucketCollectorBase(sub, values) {
            @Override
            public void collect(int doc, long owningBucketOrd) throws IOException {
                if (values.advanceExact(doc)) {
                    int valuesCount = values.docValueCount();
                    for (int i = 0; i < valuesCount; ++i) {
                        long value = values.nextValue();
                        // do something with value
                    }
                }
            }
        };
    }
```

The idea consists of optimizing for single-valued fields by unwrapping the single-valued doc-value iterator:

```java
    @Override
    public LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucketCollector sub) throws IOException {
        if (valuesSource == null) {
            return LeafBucketCollector.NO_OP_COLLECTOR;
        }
        SortedNumericDocValues values = valuesSource.longValues(ctx);
        NumericDocValues singleValues = DocValues.unwrapSingleton(values); // or FieldData.unwrapSingleton for doubles
        if (singleValues != null) {
            return new LeafBucketCollectorBase(sub, values) {
                @Override
                public void collect(int doc, long owningBucketOrd) throws IOException {
                    if (singleValues.advanceExact(doc)) {
                        long value = singleValues.longValue();
                        // do something with value
                    }
                }
            };
        } else {
            return new LeafBucketCollectorBase(sub, values) {
                @Override
                public void collect(int doc, long owningBucketOrd) throws IOException {
                    if (values.advanceExact(doc)) {
                        int valuesCount = values.docValueCount();
                        for (int i = 0; i < valuesCount; ++i) {
                            long value = values.nextValue();
                            // do something with value
                        }
                    }
                }
            };
        }
    }
```

The `terms` aggregation already does this, and e.g. `min` and `max` aggregations use similar tricks that also help optimize the single-valued case. But `date_histogram`, which is one of our most common aggregations, doesn't. Let's specialize the single-value case for date histograms too?
server/src/test/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregatorTests.java
.missing("2020-02-13T10:11:12");
        MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NUMBER_FIELD_NAME, NumberFieldMapper.NumberType.INTEGER);
        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 testUnsupportedType() {
        RangeAggregationBuilder aggregationBuilder = new RangeAggregationBuilder("range").field("not_a_number").addRange(-2d, 5d);
        MappedFieldType fieldType = new KeywordFieldMapper.KeywordFieldType("not_a_number");
        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] is not supported for aggregation [range]", e.getMessage());
    }
    public void testBadMissingField() {
        RangeAggregationBuilder aggregationBuilder = new RangeAggregationBuilder("range").field(NUMBER_FIELD_NAME)
            .addRange(-2d, 5d)
            .missing("bogus");
        MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NUMBER_FIELD_NAME, NumberFieldMapper.NumberType.INTEGER);
        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)));