> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure searchable data

> The Algolia SearchBundle helps you transform your model into Algolia records. Learn how to customize this process.

## Indexing data

You have to define the entities that should be indexed to Algolia.
You can do this by adding the entities to your configuration file, under the `algolia_search.indices` key.
Each entry under the `indices` key must contain the following attributes:

* `name` is the canonical name of the index in Algolia
* `class` is the full class reference of the entity to index

For example, to index all posts:

```yaml YAML icon=code theme={"system"}
algolia_search:
  indices:
    - name: posts
      class: App\Entity\Post
```

#### `enable_serializer_groups`

Before sending your data to Algolia, each entity is converted to an array using the Symfony built-in serializer.
This option lets you define which attributes to index using the `#[Groups(['searchable'])]` attribute.

For more information, see [Normalizers](#normalizers).

Example:

```yaml YAML icon=code theme={"system"}
algolia_search:
  indices:
    - name: posts
      class: App\Entity\Post
      enable_serializer_groups: true
```

For more information about sending data to Algolia,
see [Index data into Algolia](/doc/framework-integration/symfony/indexing/index-data-into-algolia).

#### Batching

By default, calls to Algolia for indexing or removing data are batched per 500 items.
You can modify the batch size in your configuration.

```yaml YAML icon=code theme={"system"}
algolia_search:
  batchSize: 250
```

The import command also uses this parameter to retrieve data with Doctrine.
If you run out of memory while importing your data, use a smaller `batchSize` value.

#### JMS serializer

The bundle also provides basic support for the JMS serializer.
Note that not all features are supported (like the `#[Groups]` attribute).
In your configuration, pass the name of the JMS serializer service (`jms_serializer` by default).

```yaml YAML icon=code theme={"system"}
algolia_search:
  serializer: jms_serializer
  indices:
    - name: posts
      class: App\Entity\Post
```

## Normalizers

By default all entities are converted to an array with the built-in [Symfony normalizers](https://symfony.com/doc/current/components/serializer.html#normalizers)
(for example `GetSetMethodNormalizer`, `DateTimeNormalizer`, or `ObjectNormalizer`)
which should be enough for simple use cases.
For more control over what you send to Algolia,
or to avoid [circular references](https://symfony.com/doc/current/components/serializer.html#handling-circular-references),
write your own normalizer.

Symfony uses the first normalizer in the array to support your entity or format.
You can [change the order](#normalize) in your service declaration.

<Note>The normalizer is called with *searchableArray* format.</Note>

You have many choices on how to customize your records:

* [Use attributes in the entity](#attributes)
* [Write a custom method in the entity](#normalize)
* [Write a custom normalizer class](#custom-normalizers)

<Note>
  The following features are only supported with the [default Symfony serializer](https://symfony.com/doc/current/components/serializer.html), not with [JMS serializer](https://jmsyst.com/libs/serializer).
</Note>

### Attributes

The simplest way to choose which fields to index is to mark them with the `#[Groups]` attribute.
This feature relies on the built-in `ObjectNormalizer` and its serializer-group support.

<Note>
  Attributes require [`enable_serializer_groups` to be set to `true`](#enable_serializer_groups) in the configuration.
</Note>

```php PHP icon=code theme={"system"}
<?php

namespace App\Entity;

use Symfony\Component\Serializer\Attribute\Groups;

class Post
{
    // ... other properties and methods ...

    #[Groups(['searchable'])]
    public function getTitle(): ?string
    {
        return $this->title;
    }

    #[Groups(['searchable'])]
    public function getSlug(): ?string
    {
        return $this->slug;
    }

    #[Groups(['searchable'])]
    public function getCommentCount(): ?int
    {
        return count($this->comments);
    }
}
```

### Normalize

Another option is to implement a dedicated method that returns the entity as an array.
This feature relies on the `CustomNormalizer` that ships with the serializer component.

Implement the `Symfony\Component\Serializer\Normalizer\NormalizableInterface` interface and write your `normalize` method.

Example based on a simplified version of [this Post entity](https://gist.github.com/julienbourdeau/3d17304951028cf370ed5fe95d104911):

```php PHP icon=code theme={"system"}
<?php

namespace App\Entity;

use Symfony\Component\Serializer\Normalizer\NormalizableInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class Post implements NormalizableInterface
{
    public function normalize(NormalizerInterface $serializer, $format = null, array $context = []): array
    {
        return [
            'title' => $this->getTitle(),
            'content' => $this->getContent(),
            'comment_count' => $this->getComments()->count(),
            'tags' => array_unique(array_map(function ($tag) {
              return $tag->getName();
            }, $this->getTags()->toArray())),

            // Reuse the $serializer
            'author' => $serializer->normalize($this->getAuthor(), $format, $context),
            'published_at' => $serializer->normalize($this->getPublishedAt(), $format, $context),
        ];
    }
}
```

#### Handle multiple formats

In case you are already using this method for something else,
like encoding entities into JSON for instance, you may want to use a different format for both use cases.
You can rely on the format to return different arrays.

```php PHP icon=code theme={"system"}
public function normalize(NormalizerInterface $serializer, $format = null, array $context = []): array
{
    if (\Algolia\SearchBundle\Searchable::NORMALIZATION_FORMAT === $format) {
        return [
            'title' => $this->getTitle(),
            'content' => $this->getContent(),
            'author' => $this->getAuthor()->getFullName(),
        ];
    }

    // Or if it's not for search
    return ['title' => $this->getTitle()];
}
```

### Custom normalizers

You can create a custom normalizer for any entity by implementing the `Symfony\Component\Serializer\Normalizer\NormalizerInterface` interface.
The following snippet shows a simple `UserNormalizer`.

```php PHP icon=code theme={"system"}
<?php
// src/Serializer/Normalizer/UserNormalizer.php

namespace App\Serializer\Normalizer;

use Algolia\SearchBundle\Searchable;
use App\Entity\User;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class UserNormalizer implements NormalizerInterface
{
    public function normalize($object, ?string $format = null, array $context = []): array
    {
        return [
            'id'       => $object->getId(),
            'username' => $object->getUsername(),
        ];
    }

    public function supportsNormalization($data, ?string $format = null, array $context = []): bool
    {
        return $data instanceof User;

        // Or if you want to use it only for indexing:
        // return $data instanceof User && Searchable::NORMALIZATION_FORMAT === $format;
    }

    public function getSupportedTypes(?string $format): array
    {
        return [User::class => true];
    }
}
```

Tag the normalizer to add it to the default serializer.
In your service declaration, add the following.

<CodeGroup>
  ```yaml YAML icon=code theme={"system"}
  services:
    user_normalizer:
      class: App\Serializer\Normalizer\UserNormalizer
      tags:
        - { name: serializer.normalizer }
  ```

  ```xml XML icon=code-xml theme={"system"}
  <services>
      <service id="user_normalizer" class="App\Serializer\Normalizer\UserNormalizer" public="false">
          <tag name="serializer.normalizer" />
      </service>
  </services>
  ```
</CodeGroup>

With this configuration, the serializer uses `UserNormalizer` whenever it encounters a `User`:
either as the top-level indexed entity or as a nested property on another entity being indexed.

## Order of normalizers

Because Symfony uses the first normalizer that supports your entity or format,
the order is important.

The `ObjectNormalizer` is registered with a priority of -1000 and should always be the last fallback.
Most of Symfony's built-in normalizers sit in the negative range below the `CustomNormalizer` (registered at -800), though a few (such as the `UnwrappingDenormalizer`) run at higher priorities.

To keep your normalizers ahead of the bundle's `CustomNormalizer`, register them above -800.
This also puts them ahead of the lower-priority Symfony fallback normalizers, such as `ObjectNormalizer`.
The default priority is 0.

You can change the priority in your service definition.

<CodeGroup>
  ```yaml YAML icon=code theme={"system"}
  services:
    serializer.normalizer.datetime:
      class: Symfony\Component\Serializer\Normalizer\DateTimeNormalizer
      tags:
        - { name: serializer.normalizer, priority: -100 }
  ```

  ```xml XML icon=code-xml theme={"system"}
  <services>
      <service id="serializer.normalizer.datetime" class="Symfony\Component\Serializer\Normalizer\DateTimeNormalizer">
          <!-- Run before serializer.normalizer.object -->
          <tag name="serializer.normalizer" priority="-100" />
      </service>
  </services>
  ```
</CodeGroup>
