How to Search across multiple ElasticSearch Indexes with Symfony FOS\ElasticaBundle
ElasticSearch v6.0 deprecated multiple types in one index. You can read more here: Removal of mapping types
How to deal with this breaking change?
Create MultiIndex.php file in your Symfony App project
<?php
declare(strict_types=1);
/*
 * Created by Exploit.cz <insekticid AT exploit.cz>
 */
namespace App\Search\Elastica;
use Elastica\Exception\InvalidException;
use Elastica\Index;
use Elastica\ResultSet\BuilderInterface;
use Elastica\Search;
class MultiIndex extends Index
{
    /**
     * Array of indices.
     *
     * @var array
     */
    protected $_indices = [];
    /**
     * Adds a index to the list.
     *
     * @param \Elastica\Index|string $index Index object or string
     *
     * @throws \Elastica\Exception\InvalidException
     *
     * @return $this
     */
    public function addIndex($index)
    {
        if ($index instanceof Index) {
            $index = $index->getName();
        }
        if (!is_scalar($index)) {
            throw new InvalidException('Invalid param type');
        }
        $this->_indices[] = (string) $index;
        return $this;
    }
    /**
     * Add array of indices at once.
     *
     * @param array $indices
     *
     * @return $this
     */
    public function addIndices(array $indices = [])
    {
        foreach ($indices as $index) {
            $this->addIndex($index);
        }
        return $this;
    }
    /**
     * Return array of indices.
     *
     * @return array List of index names
     */
    public function getIndices()
    {
        return $this->_indices;
    }
    /**
     * @param string|array|\Elastica\Query $query
     * @param int|array                    $options
     * @param BuilderInterface             $builder
     *
     * @return Search
     */
    public function createSearch($query = '', $options = null, BuilderInterface $builder = null)
    {
        $search = new Search($this->getClient(), $builder);
        //$search->addIndex($this);
        $search->addIndices($this->getIndices());
        $search->setOptionsAndQuery($options, $query);
        return $search;
    }
}
Create App\Search\Transformer\ElasticaToModelTransformerCollection.php (removed from FosElasticaBundle)
<?php
namespace App\Search\Transformer;
use FOS\ElasticaBundle\HybridResult;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface;
/**
 * Holds a collection of transformers for an index wide transformation.
 *
 * @author Tim Nagel <tim@nagel.com.au>
 * @author Insekticid <insekticid+fos@exploit.cz>
 */
class ElasticaToModelTransformerCollection implements ElasticaToModelTransformerInterface
{
    /**
     * @var ElasticaToModelTransformerInterface[]
     */
    protected $transformers = [];
    /**
     * @param ElasticaToModelTransformerInterface[] $transformers
     */
    public function __construct(array $transformers)
    {
        $this->transformers = $transformers;
    }
    /**
     * {@inheritdoc}
     */
    public function getObjectClass(): string
    {
        return implode(',', array_map(function (ElasticaToModelTransformerInterface $transformer) {
            return $transformer->getObjectClass();
        }, $this->transformers));
    }
    /**
     * {@inheritdoc}
     */
    public function getIdentifierField(): string
    {
        return array_map(function (ElasticaToModelTransformerInterface $transformer) {
            return $transformer->getIdentifierField();
        }, $this->transformers)[0];
    }
    /**
     * {@inheritdoc}
     */
    public function transform(array $elasticaObjects)
    {
        $sorted = [];
        foreach ($elasticaObjects as $object) {
            $sorted[$object->getIndex()][] = $object;
        }
        $transformed = [];
        foreach ($sorted as $type => $objects) {
            $transformedObjects = $this->transformers[$type]->transform($objects);
            $identifierGetter = 'get'.ucfirst($this->transformers[$type]->getIdentifierField());
            $transformed[$type] = array_combine(
                array_map(
                    function ($o) use ($identifierGetter) {
                        return $o->$identifierGetter();
                    },
                    $transformedObjects
                ),
                $transformedObjects
            );
        }
        $result = [];
        foreach ($elasticaObjects as $object) {
            if (array_key_exists((string) $object->getId(), $transformed[$object->getIndex()])) {
                $result[] = $transformed[$object->getIndex()][(string) $object->getId()];
            }
        }
        return $result;
    }
    /**
     * {@inheritdoc}
     */
    public function hybridTransform(array $elasticaObjects)
    {
        $objects = $this->transform($elasticaObjects);
        $result = [];
        for ($i = 0, $j = count($elasticaObjects); $i < $j; ++$i) {
            if (!isset($objects[$i])) {
                continue;
            }
            $result[] = new HybridResult($elasticaObjects[$i], $objects[$i]);
        }
        return $result;
    }
}
Refactor fos_elastica.yaml and move all types from the same index to your newly created index (separate it).
Example:
- before: index:recipes, types: [recipe, recipes]
 - after:
- index: recipe, type: recipe
 - index: recipes, type: recipes
 
 
Now add MultiIndex service into services.yaml file in config directory and change recipe and recipes to your newly created index names.
    App\Search\Elastica\MultiIndex:
        arguments:
            $name: 'recipe'
        calls:
            - [ addIndices, [['@fos_elastica.index.recipe', '@fos_elastica.index.recipes']]]
    Elastica\SearchableInterface: '@App\Search\Elastica\MultiIndex'
    #FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection:
    App\Search\Transformer\ElasticaToModelTransformerCollection:
        arguments:
            - {
                recipe: '@fos_elastica.elastica_to_model_transformer.recipe',
                recipes: '@fos_elastica.elastica_to_model_transformer.recipes'
              }
    FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface: '@FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection'
    FOS\ElasticaBundle\Finder\TransformedFinder: ~
        #arguments:
        #    - '@App\Search\Elastica\MultiIndex'
        #    - '@FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection'
    FOS\ElasticaBundle\Finder\PaginatedFinderInterface: '@FOS\ElasticaBundle\Finder\TransformedFinder'
Now create SearchRepository.php
<?php
declare(strict_types=1);
use Elastica\Query\Match;
use FOS\ElasticaBundle\Repository;
class SearchRepository extends Repository
{
    public function search(string $searchTerm, int $page = 1, int $limit = 48) : ?array
    {
        if ($searchTerm) {
            $fieldQuery = new Match();
            $fieldQuery->setFieldQuery('name', $searchTerm);
            $items     = $this->findPaginated($fieldQuery);
            $items->setMaxPerPage($limit);
            $items->setCurrentPage($page);
            return $items;
        }
        return null;
    }
}
Now you can use this repository in your Controller
<?php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Repository\SearchRepository;
class SearchController extends AbstractController
{
    /**
     * @var SearchRepository
     */
    protected $searchRepository;
    public function __construct(SearchRepository $searchRepository)
    {
        $this->searchRepository = $searchRepository;
    }
}
Github issues Multiple index paginated search #1521, Multi type search to multi index search #1385