### 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/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregator.java
* ranges. There is also {@link FromFilters} which isn't a subclass
 * but is also a functional aggregator for {@code range}.
 * {@link RangeAggregator#build} will build the fastest of the three
 * that is compatible with the requested configuration.
 */
public abstract class RangeAggregator extends BucketsAggregator {
    /**
     * Minimum number of docs in the index per range before we attempt to use
     * a filter-based collection mechanism. This exists mostly to keep fast
     * range aggregations fast. Each filter has an overhead in the ball park
     * of half a millisecond just to build its {@link ScorerSupplier}. If there
     * are only a couple of thousand docs in the range then it tends not to
     * be worth it to kick in the optimization.
     * <p>
     * The value of this field was experimentally derived but the experiment
     * wasn't particularly rigorous. We had a performance test that collected
     * 123 buckets with an average of 900 documents per bucket that jumped
     * from 35ms to 90ms. I figure that 5000 is fairly close to where the break
     * even point is.
     */
    public static final double DOCS_PER_RANGE_TO_USE_FILTERS = 5000;
    /**
     * The maximum {@code long} that can accurately fit into the
     * {@code double} precision floating point bounds.
     */
    public static final long MAX_ACCURATE_BOUND = 1L << 53;
    public static final ParseField RANGES_FIELD = new ParseField("ranges");
    public static final ParseField KEYED_FIELD = new ParseField("keyed");
    static final DoubleUnaryOperator IDENTITY = DoubleUnaryOperator.identity();
    public static class Range implements Writeable, ToXContentObject {