In order to implement multi threaded search we need to adjust the logic that creates the slices.
The new slice logic should take the following into account:

- A minimum doc count per slice. In order to avoid a number of aggregations to produce in-accurate results, a slice should at least contain 10% of the total documents in the Lucene index.
- A maximum number of slices. The number of slices that a shard level search creates should be capped. To avoid creating more slices than available search threads.
server/src/main/java/org/elasticsearch/search/internal/ContextIndexSearcher.java
return search(weight, collectorManager, firstCollector);
    }
    /**
     * Similar to the lucene implementation but it will wait for all threads to fisinsh before returning even if an error is thrown.
     * In that case, other exceptions will be ignored and the first exception is thrown after all threads are finished.
     * */
    private <C extends Collector, T> T search(Weight weight, CollectorManager<C, T> collectorManager, C firstCollector) throws IOException {
        if (queueSizeBasedExecutor == null || leafSlices.length <= 1) {
            search(leafContexts, weight, firstCollector);
            return collectorManager.reduce(Collections.singletonList(firstCollector));
        } else {
            final List<C> collectors = new ArrayList<>(leafSlices.length);
            collectors.add(firstCollector);
            final ScoreMode scoreMode = firstCollector.scoreMode();
            for (int i = 1; i < leafSlices.length; ++i) {
                final C collector = collectorManager.newCollector();
                collectors.add(collector);
                if (scoreMode != collector.scoreMode()) {
                    throw new IllegalStateException("CollectorManager does not always produce collectors with the same score mode");
                }
            }
            final List<FutureTask<C>> listTasks = new ArrayList<>();
            for (int i = 0; i < leafSlices.length; ++i) {
                final LeafReaderContext[] leaves = leafSlices[i].leaves;
                final C collector = collectors.get(i);
                FutureTask<C> task = new FutureTask<>(() -> {
                    search(Arrays.asList(leaves), weight, collector);
                    return collector;
                });
                listTasks.add(task);