### Description

We have had this longstanding issue that running a top-level `filter` aggregation is often (not always, e.g. not if you need the total hit count too) a performance bug, as Elasticsearch would iterate over docs that match the `query` and would then check each of these docs against the filter of the `filter` aggregation. It's usually a better idea to put the filter directly into the query, where it can be used to drive iteration if appropriate.

We could do this automatically by implementing the `LeafCollector#competitiveIterator` API that was introduced for dynamic pruning, in order to automatically start intersecting the query with the filter of the `filter` aggregator once enough hits have been counted.
server/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterByFilterAggregator.java
/**
         * Subclasses should override this to adapt the
         * {@link FilterByFilterAggregator} into another sort of aggregator
         * if required.
         */
        protected abstract T adapt(CheckedFunction<AggregatorFactories, FilterByFilterAggregator, IOException> delegate) throws IOException;
        public final void add(String key, Query query) throws IOException {
            if (valid == false) {
                return;
            }
            if (query instanceof AbstractScriptFieldQuery) {
                /*
                 * We know that runtime fields aren't fast to query at all
                 * but we expect all other sorts of queries are at least as
                 * fast as the native aggregator.
                 */
                valid = false;
                return;
            }
            add(QueryToFilterAdapter.build(aggCtx.searcher(), key, query));
        }
        final void add(QueryToFilterAdapter filter) throws IOException {
            if (valid == false) {
                return;
            }
            QueryToFilterAdapter mergedFilter = filter.union(rewrittenTopLevelQuery);
            if (mergedFilter.isInefficientUnion()) {
                /*
                 * For now any complex union kicks us out of filter by filter
                 * mode. Its possible that this de-optimizes many "filters"