Algolia est un moteur de recherche hébergé : vous lui envoyez vos contenus sous forme d'enregistrements JSON, il les indexe, et l'interface de recherche l'interroge directement depuis le navigateur. Sur un site Drupal ou une application Symfony, il prend le relais quand la recherche native ne suffit plus. Reste à savoir quand il se justifie, et comment l'intégrer proprement.

Ce qu'Algolia change par rapport à la recherche native

Sur un site Drupal, la recherche « native » recouvre plusieurs choses assez différentes :

Solution Principe Limites typiques
Filtre Views « contient » LIKE SQL sur un ou plusieurs champs Pas de pertinence, pas de tolérance aux fautes, lent sur de gros volumes
Module Search du cœur Index maison dans la base Pertinence rudimentaire, module déprécié en Drupal 11
Search API + backend base de données Index dans la base, facettes via le module Facets Correct pour un catalogue modeste, pas de tolérance aux fautes
Search API + Solr Serveur Solr dédié Puissant, mais un serveur de plus à héberger, régler et maintenir
Search API + Algolia Index hébergé, requêtes depuis le navigateur Service payant au-delà de l'offre gratuite, données hors de votre serveur

Algolia apporte, sans réglage lourd, la tolérance aux fautes de frappe, la recherche instantanée, des facettes rapides sur de gros catalogues, un classement réglable (attributs recherchables, critères métier comme la popularité), les synonymes et des statistiques : requêtes fréquentes, requêtes sans résultat. Autre avantage, architectural : les requêtes de recherche ne passent plus par PHP ni par la base de données, un pic de trafic sur la recherche ne charge donc plus votre serveur.

Quand ça vaut le coup, et quand non

Algolia se justifie quand la recherche est un parcours central : catalogue produit ou de formations, base documentaire, annuaire. Surtout si les visiteurs cherchent avec leurs mots (fautes, synonymes, pluriels) et que la recherche actuelle renvoie trop souvent zéro résultat, ou si vous voulez des facettes combinables sans monter et maintenir un Solr.

Il se justifie moins pour un site vitrine où la recherche sert peu, pour des contenus majoritairement privés (faisable, mais plus coûteux, voir plus bas), ou si le budget ne permet pas un coût récurrent lié au nombre d'enregistrements et de requêtes. Pour mémoire, l'offre gratuite d'Algolia (plan Free) inclut, d'après sa page tarifs, 50 000 enregistrements et 10 000 requêtes de recherche par mois ; au-delà, la facturation dépend de l'offre. Une recherche instantanée envoie une requête à chaque frappe : le compteur monte vite.

Intégrer Algolia dans Drupal

Le module Search API Algolia

L'intégration passe par le module contrib Search API Algolia (drupal/search_api_algolia), un backend pour Search API. La branche 3.x est compatible Drupal 10.1+ et Drupal 11 ; la page du projet la présente comme « minimally maintained », c'est-à-dire en maintenance corrective. À l'été 2026, le module est passé à la version 4 du client PHP Algolia : vérifiez que la version installée sur votre site en tient compte, Algolia ayant annoncé la fin du support du client v3.

composer require 'drupal/search_api_algolia:^3.1'
drush en search_api_algolia -y

Point important, écrit sur la page du projet : le module ne gère que l'indexation. L'interface se construit côté navigateur avec les bibliothèques JavaScript d'Algolia. Le module séparé Algolia Search Interface en propose une, mais il est peu utilisé et non couvert par les avis de sécurité de drupal.org : je préfère intégrer InstantSearch dans le thème.

Serveur, index et champs

La configuration suit la logique habituelle de Search API :

  1. Un serveur Search API avec le backend Algolia : identifiant d'application et clé d'API d'écriture. Cette clé ne doit pas finir dans la configuration exportée : surchargez-la dans settings.php à partir d'une variable d'environnement.
  2. Un index par type de contenu recherché (produits, formations, publications), avec les champs utiles : titre, résumé, taxonomies, prix, date, URL, image.
  3. Les processeurs Search API : « Entity status » pour exclure les contenus non publiés, et, si le site a des contenus à accès restreint, « Content access », dont on reparle plus bas.

Côté Algolia, les attributs recherchables, les facettes et le classement métier se règlent dans le tableau de bord, ou mieux par un script versionné, pour recréer un index à l'identique.

Synchronisation et réindexation

Search API suit les entités modifiées et les envoie soit immédiatement (option « Index items immediately »), soit par lots au cron. Pour les opérations de masse, Drush suffit :

drush search-api:status
drush search-api:index produits --batch-size=100
# après un changement de champs ou de structure
drush search-api:reset-tracker produits && drush search-api:index produits

L'indexation immédiate est confortable, mais chaque sauvegarde devient un appel réseau vers Algolia. Sur un import de masse, désactivez-la et passez par Drush.

L'interface : InstantSearch ou Autocomplete

InstantSearch.js fournit les briques d'une page de résultats (champ, résultats, facettes, pagination, tri) ; Autocomplete sert plutôt au champ de l'en-tête, avec un menu de suggestions. Dans Drupal, ces bibliothèques se déclarent dans le fichier *.libraries.yml du thème et s'attachent à un bloc ou à un template dédié. Le navigateur n'utilise qu'une clé de recherche seule, jamais la clé d'écriture.

Trois cas concrets côté Drupal

Drupal Commerce : catalogue produits

La première décision porte sur la granularité : un enregistrement par produit, avec les variations agrégées (tailles disponibles, prix minimum), ou un enregistrement par variation, regroupées à l'affichage grâce aux paramètres distinct et attributeForDistinct d'Algolia. Le premier choix est plus simple ; le second permet de filtrer finement sur une couleur ou une taille précise.

Le prix et le stock posent la question de la fraîcheur. Un stock qui bouge à chaque commande génère beaucoup de mises à jour. Indexer une information simplifiée (« disponible » ou non) plutôt que la quantité exacte limite ces appels, et le prix définitif reste de toute façon calculé par Commerce au moment de l'ajout au panier. Les tris par prix passent par des répliques de l'index ; une réplique standard compte comme autant d'enregistrements supplémentaires.

Site institutionnel : publications, actualités, documents

Ici, l'enjeu est de chercher dans des contenus hétérogènes et souvent longs. Algolia limite la taille d'un enregistrement (10 Ko sur l'offre gratuite, 100 Ko par enregistrement sur les offres payantes avec une moyenne de 10 Ko, selon son support) : un rapport de cinquante pages ne tient pas dans un seul enregistrement. La méthode recommandée consiste à découper le texte en paragraphes, un enregistrement par morceau, puis à dédoublonner à l'affichage avec distinct. Le texte des PDF n'est pas extrait par Algolia : il faut l'extraire côté Drupal avant l'indexation.

Pour un site multilingue, prévoyez un index par langue. Le module propose une option de suffixe de langue sur le nom de l'index, ce qui permet aussi des réglages de langue distincts (pluriels, mots vides) pour chaque index.

Blog ou site média

Le besoin est plus simple : plein texte, facettes par rubrique, auteur et date, tri par fraîcheur. Algolia apporte surtout la tolérance aux fautes. C'est aussi le cas où le coût se discute le plus : avec peu de recherches, Search API sur la base de données, bien réglé, peut suffire.

Intégrer Algolia dans Symfony

Côté Symfony, deux voies. Le bundle officiel algolia/search-bundle est maintenu : sa version 8.1 cible Symfony 7 et 8, PHP 8.2 minimum, et repose sur le client PHP v4. Il indexe les entités Doctrine déclarées dans sa configuration et fournit les commandes search:import et search:clearindex. Pour maîtriser finement les enregistrements et rendre l'indexation asynchrone, j'utilise plutôt le client PHP avec Messenger. Extraits pour Symfony 7.4, PHP 8.2+ et algolia/algoliasearch-client-php v4 :

Déclarer le client et écouter Doctrine

# config/services.yaml
services:
    Algolia\AlgoliaSearch\Api\SearchClient:
        factory: ['Algolia\AlgoliaSearch\Api\SearchClient', 'create']
        arguments: ['%env(ALGOLIA_APP_ID)%', '%env(ALGOLIA_WRITE_KEY)%']
namespace App\Search;

use App\Entity\Product;
use App\Message\IndexProduct;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Events;
use Symfony\Component\Messenger\MessageBusInterface;

#[AsEntityListener(event: Events::postPersist, method: 'sync', entity: Product::class)]
#[AsEntityListener(event: Events::postUpdate, method: 'sync', entity: Product::class)]
#[AsEntityListener(event: Events::preRemove, method: 'sync', entity: Product::class)]
final class ProductSearchListener
{
    public function __construct(private MessageBusInterface $bus) {}

    public function sync(Product $product): void
    {
        $this->bus->dispatch(new IndexProduct($product->getId()));
    }
}

Le listener n'appelle pas Algolia : il publie un message, routé vers un transport asynchrone (App\Message\IndexProduct: async dans framework.messenger.routing). La sauvegarde reste rapide et une indisponibilité d'Algolia ne bloque pas l'administration.

Le handler : relire, puis indexer ou supprimer

namespace App\MessageHandler;

use Algolia\AlgoliaSearch\Api\SearchClient;
use App\Message\IndexProduct;
use App\Repository\ProductRepository;
use App\Search\ProductRecordFactory;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
final class IndexProductHandler
{
    public function __construct(
        private ProductRepository $products,
        private ProductRecordFactory $records,
        private SearchClient $algolia,
    ) {}

    public function __invoke(IndexProduct $message): void
    {
        $product = $this->products->find($message->productId);

        if (null === $product || !$product->isPublished()) {
            $this->algolia->deleteObject('products', (string) $message->productId);

            return;
        }

        $this->algolia->saveObject('products', $this->records->create($product));
    }
}

Le handler relit l'entité : il indexe l'état réel au moment du traitement, et suppression comme dépublication aboutissent au retrait de l'index. Un piège classique : postPersist et postUpdate sont déclenchés pendant le flush, avant la validation de la transaction. Si le worker est très rapide, il peut lire l'ancienne version. Pour l'éviter, collectez les identifiants et publiez les messages dans postFlush.

Pour la réindexation complète, une commande bin/console app:search:reindex qui appelle replaceAllObjects() reconstruit un index temporaire, y copie réglages, synonymes et règles, puis remplace l'index d'origine : pas de période où la recherche est vide. Le nombre d'enregistrements double pendant l'opération.

La clé de recherche sécurisée

Si certains contenus sont réservés à des groupes d'utilisateurs, le navigateur ne doit pas recevoir une clé capable de tout voir. Une clé sécurisée, générée côté serveur à partir de la clé de recherche seule (jamais de la clé d'administration), embarque un filtre que l'utilisateur ne peut pas retirer :

$securedKey = $this->algolia->generateSecuredApiKey(
    $this->searchOnlyKey,
    [
        'filters' => sprintf('visibility:public OR group_ids:%d', $user->getGroupId()),
        'validUntil' => time() + 3600,
        'restrictIndices' => ['products'],
    ],
);

L'attribut group_ids doit être déclaré comme attribut de facette (en filterOnly suffit). Côté Drupal, le processeur « Content access » de Search API ajoute des informations de droits aux éléments indexés, mais c'est toujours une clé sécurisée de ce type qui doit appliquer le filtre, puisque la requête part du navigateur.

Les pièges à anticiper

  • Quotas : la facturation porte sur les enregistrements (répliques comprises) et les requêtes de recherche. Les opérations d'indexation ne sont pas facturées sur les offres actuelles, mais Algolia applique une limite de protection et peut rejeter les écritures en cas d'excès (erreur HTTP 429).
  • Taille des enregistrements : n'indexez que ce qui sert à chercher, filtrer ou afficher, pas le HTML complet des pages.
  • Synchronisation : une indexation partie en erreur ne se voit pas. Surveillez la file Messenger ou le suivi Search API, et prévoyez une réindexation complète planifiée.
  • Contenus privés : un contenu indexé sans filtre est lisible par quiconque possède la clé de recherche, qui est publique par nature. Dans le doute, ne l'indexez pas.
  • RGPD : contenus indexés et statistiques sont hébergés par Algolia. Choisissez la région à la création de l'application (selon le support Algolia, les offres récentes en libre-service se limitent aux centres de données des États-Unis, du Royaume-Uni ou d'Europe de l'Ouest), n'indexez aucune donnée personnelle inutile, et soumettez le suivi des clics au consentement.

Retour d'expérience : un catalogue de formations

Chez Abilways, pour le site de Skolae Formation, j'ai travaillé sur la refonte de l'expérience de recherche du catalogue de formations avec Algolia. Le catalogue ne naissait pas dans Drupal : il provenait de l'API d'un partenaire. J'ai donc développé un module Drupal qui importe ce catalogue, puis l'indexe via Search API dans Algolia. Le même projet comprenait l'optimisation du tunnel de commande.

Ce type d'architecture a une conséquence à poser dès le départ : la qualité de la recherche dépend de celle des données importées. Dans ce schéma, un champ mal renseigné à la source devient une facette incomplète. L'import doit donc normaliser les données avant l'indexation : une bonne partie de la pertinence se joue là, avant tout réglage dans le tableau de bord.

Par où commencer

Avant de choisir Algolia, regardez ce que cherchent réellement vos visiteurs et ce que la recherche actuelle leur répond. Sur un projet neuf, prévoyez la recherche dès l'architecture, comme le reste d'une création de site Drupal ou Symfony. Sur un site en cours de mise à jour vers Drupal 11, vérifiez le module de recherche avec les autres, selon la méthode décrite pour les modules incompatibles avec Drupal 11. L'audit flash peut inclure un état des lieux de la recherche : données indexées, synchronisation, droits d'accès.