Integrations / Frameworks / Symfony / Multiple Models in One Index

Multiple Models in One Index

Aggregators

An Aggregator is a clean way to implement site-wide search in a single index with multiple entity types.

Defining Aggregators

To create a new aggregator, add a new class to your entities directory. The getEntities method of this class should return all the entity classes you want to index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Algolia\SearchBundle\Entity\Aggregator;

/**
 * @ORM\Entity
 */
class News extends Aggregator
{
    /**
     * Returns the entities class names that should be aggregated.
     *
     * @return string[]
     */
    public static function getEntities()
    {
        return [
            Post::class,
            Comment::class,
        ];
    }
}

If you’re using a NoSQL database like MongoDB, you have to use the Algolia\SearchBundle\Document\Aggregator class instead of Algolia\SearchBundle\Entity\Aggregator.

Finally, add the new aggregator class name into the algolia_search.yml file:

1
2
3
- indices:
    - name: news
      class: App\Entity\News

Searching

An aggregator is a standard entity class. You can begin searching entities on the aggregator using the search method onyour SearchService:

1
2
3
4
5
6
7
$this->searchService->index($objectManager, $post);
$this->searchService->index($objectManager, $comment);

$results = $this->searchService->search($objectManager, News::class, 'query');

// $results[0] contains a \App\Entity\Post.
// $results[1] contains a \App\Entity\Comment.

Be careful, the $result array may contain different types of entities instances.

If you want to get the raw results from Algolia, use the rawSearch() method. However, this time, each result may contain a different structure:

1
$results = $this->searchService->rawSearch(News::class, 'query');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    "hits": [
        {
            "id": 1,
            "title": "Article title",
            "slug": "article-title",
            "content": "Article content by query",
            "objectID": "App\\Entity\\Article::1",
            ...
        },
        {
            "id": 1,
            "content": "Comment content by query",
            "objectID": "App\\Entity\\Comment::1",
            ...
        },
        ...
    ]
}

To ensure that each result has a similar structure, you may need to implement the method normalize on each entity or override it on the aggregator class.

Conditional indexing

Conditional indexing on Aggregators works just like a normal entity, using the index_if key. You may want to use $this->entity to have access to the current entity being aggregated. Here is an example using the method approach:

1
2
3
4
- indices:
    - name: news
      class: App\Entity\News
      index_if: isPublished
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * @ORM\Entity
 */
class News extends Aggregator
{
    // ...

    public function isPublished()
    {
        // Index only published articles.
        if ($this->entity instanceof Article) {
            return $this->entity->isPublished();
        }

        // If is not an article, index anyway.
        return true;
    }
}

For more information on conditional indexing, check out the documentation.

Did you find this page helpful?