Recherche interne PrestaShop : live search, tolérance fautes et tri commercial

Guide technique pour moderniser la recherche interne PrestaShop : latence du live search, tolérance aux fautes, score commercial et pipeline d’indexation asynchrone.

Écran d'ordinateur affichant une interface de recherche en direct et du code.

Table des matières :

  1. Ce que le cœur PrestaShop sait (mal) faire en recherche interne
  2. Live search (autocomplete) : latence, endpoints, payloads et garde-fous
  3. Tolérance aux fautes : définition, pièges et implémentations réalistes
  4. Tri commercial : construire un score hybride (pertinence + business)
  5. Intégration moteur dédié : indexation incrémentale, asynchrone, et observabilité
  6. Mise en production : compat, SEO, sécurité et stratégie de repli

Ce que le cœur PrestaShop sait (mal) faire en recherche interne

Sur PrestaShop 8.1.x et 9.x (PHP 8.2/8.3), la recherche « native » reste fondamentalement un compromis : un index maison stocké en SQL, reconstruit via les tâches d’indexation, puis interrogé via des requêtes qui finissent très vite par coûter cher dès que vous avez du volume (gros catalogue, multilingue, multiboutique). Les tables et pondérations (mots, positions, poids par champ) sont correctes pour du search-as-a-page classique, mais elles ne couvrent pas les besoins modernes : autocomplétion instantanée, tolérance aux fautes, synonymes, boost merchandising et règles de ranking.

La limite principale, ce n’est pas « MySQL vs Elasticsearch », c’est la nature de la pertinence : l’algorithme natif est orienté matching lexical et pondérations statiques. Il n’y a pas de fuzzy matching robuste (distance d’édition), pas de correction orthographique, pas de gestion simple des variantes (« iphone 15 », « i‑phone15 », « iphne »), et le tri « commercial » se résume souvent à des contournements (ordre par prix, nouveautés, etc.) qui cassent la logique de recherche.

Concrètement, les irritants « terrain » arrivent vite, surtout en FR/BE/CH où les utilisateurs tapent avec accents, tirets, apostrophes typographiques et variantes de saisie :

  • Accents et variantes Unicode : é vs e, apostrophe droite ' vs typographique ’, tiret - vs ‑. Sans normalisation, vous segmentez artificiellement votre demande.
  • Mots composés : t-shirt, tee shirt, tee-shirt ; café vs cafe. Si l’indexation et la requête ne tokenisent pas de manière cohérente, vous perdez des résultats.
  • Références et tailles : 128Go, 128 go, 128g ; XL vs X-L. Le natif ne sait pas « comprendre » ces formes, il ne fait que matcher des tokens.

Côté performance, l’autocomplete multiplie les hits. Sans cache applicatif et sans moteur dédié, vous allez vite retomber sur les classiques : saturation CPU MySQL, contention InnoDB, et requêtes qui se dégradent avec la cardinalité. Avant de bricoler, relisez le détail du modèle d’indexation natif dans Index de recherche PrestaShop : fonctionnement, tables SQL et pondérations — puis mesurez réellement vos coûts SQL via PrestaShop MySQL : analyser slow query log et optimiser index.

Petit repère décisionnel (sans dogme) : si vous avez peu de produits, une seule langue, pas d’autocomplete « exigeant » et des attentes modestes sur la pertinence, le natif peut suffire… à condition d’être monitoré et caché. Dès que vous voulez live search + fautes + tri merchandising stable, vous sortez du périmètre raisonnable du cœur.

Live search (autocomplete) : latence, endpoints, payloads et garde-fous

Un live search crédible, c’est d’abord une contrainte de latence. En e‑commerce, le seuil « acceptable » pour une suggestion au clavier se joue typiquement sous ~100–150 ms côté back (hors réseau), sinon l’utilisateur tape plus vite que votre UI. La différence entre 100 ms et 350 ms se voit immédiatement : vous déclenchez du jitter, des annulations de requêtes, et vous chargez le serveur pour des requêtes que l’utilisateur ne verra jamais.

Ce point est cohérent avec un principe d’ergonomie très ancien mais toujours pertinent :

« 0.1 second is about the limit for having the user feel that the system is reacting instantaneously. » — Nielsen Norman Group, Response Times: The 3 Important Limits (Jakob Nielsen, 1993)
https://www.nngroup.com/articles/response-times-3-important-limits/

Techniquement, sur PrestaShop, la mauvaise approche consiste à faire pointer l’autocomplete sur la même logique que la page de résultats (SearchController), puis à espérer que ça tienne. La bonne approche : un endpoint dédié léger qui renvoie un payload minimal (id, nom, URL, image, prix, disponibilité, éventuellement marque) et rien d’autre.

Quelques bonnes pratiques « payload » qui font une vraie différence en prod :

  • Limiter le nombre de suggestions (ex. top 6–10). Le but est d’aider, pas de reproduire la SERP.
  • Prix : renvoyer le prix déjà formaté (ou au moins le montant + devise), sinon vous multipliez les règles côté front.
  • Image : renvoyer une URL de miniature (format webp si vous avez), pas l’image pleine taille.
  • Disponibilité : un booléen in_stock suffit souvent ; éviter de détailler les quantités en live (coûteux et sensible).

Plus votre JSON est gros, plus vous perdez du temps en sérialisation et sur le réseau, et plus vous augmentez le coût cache/CDN. Une règle simple : pour un endpoint de suggestion, visez un corps de réponse qui reste petit et stable (souvent quelques kilo-octets), afin de pouvoir le mettre en cache court sans effet de bord.

Exemple de stratégie front (thème Classic / Hummingbird) : debounce 150–250 ms, abort des requêtes en vol via AbortController, et cache en mémoire (Map) sur la session de page.

// Exemple minimal : debounce + abort + cache
const cache = new Map();
let ctrl;

async function liveSearch(q) {
  const query = q.trim();
  if (query.length < 2) return [];
  if (cache.has(query)) return cache.get(query);

  if (ctrl) ctrl.abort();
  ctrl = new AbortController();

  const res = await fetch(`/module/monsearch/suggest?q=${encodeURIComponent(query)}`, {
    signal: ctrl.signal,
    headers: { 'Accept': 'application/json' }
  });
  const data = await res.json();
  cache.set(query, data);
  return data;
}

Ensuite, mettez des garde-fous serveur : limitation de débit (IP + session), blocage des patterns bots (strings trop longues, taux d’erreur), et mise en cache courte (ex. 30–120 s) sur des requêtes populaires.

Checklist « garde-fous » (utile dès que vous avez du trafic SEO ou du scraping opportuniste) :

  • Longueur max requête (ex. 64–128 chars) + normalisation Unicode (éviter des variantes invisibles).
  • Rejet des requêtes à très forte entropie (signatures de scraping) et des rafales (burst).
  • Cache applicatif (clé = langue + shop + groupe client + query normalisée).
  • Réponse gracieuse en cas de timeout moteur (retour liste vide + télémétrie), plutôt qu’un 500.

Sur des infrastructures où vous terminez TLS et faites du routage, HAProxy peut vous aider à imposer des ACL et à isoler l’endpoint de suggestion (ex. quotas différents du reste du site) : HAProxy : configuration avancée ACL, health checks et sticky sessions

Tolérance aux fautes : définition, pièges et implémentations réalistes

La tolérance aux fautes (typo tolerance) désigne la capacité du moteur à retrouver les documents pertinents malgré des erreurs de frappe, inversions de caractères, accents, ou variantes orthographiques. Le formalisme courant s’appuie sur la distance de Levenshtein (1965) : le nombre minimal d’opérations (insertion, suppression, substitution) nécessaires pour transformer une chaîne en une autre. Dit autrement : « iphne » doit pouvoir matcher « iphone » avec un coût faible.

Mais, dans un catalogue e‑commerce, la « faute » n’est pas que la typo. En contexte francophone, vous devez souvent gérer :

  • Accents : cafe → café.
  • Élisions : l iphone, l’iphone, iphone (selon tokenisation).
  • Collés / séparés : iphone15 → iphone 15.
  • Clavier AZERTY : substitutions fréquentes sur des touches proches (source d’erreurs plausibles).

Essayer de reproduire ça en SQL pur dans PrestaShop est possible, mais rarement rentable : calculer une distance d’édition à la volée sur un gros catalogue est un anti-pattern (CPU bound). Les contournements type SOUNDEX/phonétique, ou pré-calcul de n‑grams, deviennent vite des usines à gaz et posent des problèmes de qualité (trop de faux positifs, explosion de l’index, collisions).

En pratique, vous avez trois approches réalistes (souvent combinées) :

Approche Avantages Limites
Recherche stricte + synonymes manuels + analytics Simple, prédictible, faible charge Ne couvre pas les fautes, dépend d’un entretien continu
Moteur dédié (Meilisearch / Elasticsearch / Algolia) Typo, analyzers, ranking, performance Pipeline d’indexation à maintenir + coûts infra/ops
Hybride (strict sur SKU, fuzzy sur nom) Très efficace en e‑commerce Nécessite une config fine « par champ »

Les moteurs modernes rendent la tolérance aux fautes accessible sans réécrire la théorie, mais pas sans gouvernance : un fuzzy trop permissif peut rendre la recherche bruyante (faux positifs), et un fuzzy trop strict ne corrige rien.

Côté Elasticsearch, un pattern courant pour l’autocomplete + fautes est une requête multi_match avec fuzziness contrôlée (et un prefix_length pour éviter les matchs absurdes) :

{
  "query": {
    "multi_match": {
      "query": "iphne 15",
      "fields": ["name^3", "brand", "reference^5"],
      "fuzziness": "AUTO",
      "prefix_length": 2
    }
  }
}

Le piège opérationnel : la tolérance aux fautes doit être différenciée par champ.

  • Sur un SKU/référence, vous voulez souvent du strict (ou du prefix), pas du fuzzy (sinon vous matchez des références « proches » mais fausses).
  • Sur un nom produit, vous acceptez plus d’erreurs.
  • Sur des attributs numériques (tailles, capacités), vous devez éviter que « 128 » matche « 12 » ou « 1280 » (là, une analyse dédiée — ou un champ numérique — change tout).

Cette granularité (analyzers, mapping, ranking rules) est précisément ce que le cœur PrestaShop ne vous donnera pas sans réimplémenter un moteur. Et si vous vendez sur plusieurs pays, pensez aussi à l’architecture d’index : un index par langue (plus simple) vs un index unique avec champ lang (plus complexe, mais mutualise certains coûts). Dans tous les cas, vous devez décider ce qui est « commun » (référence, marque) et ce qui est « local » (nom traduit, stopwords, règles linguistiques).

Tri commercial : construire un score hybride (pertinence + business)

« Tri commercial » ne veut pas dire « trier par marge » en détruisant la pertinence textuelle. La bonne approche consiste à produire un score hybride : on part d’un score de pertinence (BM25 / TF‑IDF selon moteur), puis on applique des boosts contrôlés (stock, marge, popularité, nouveauté, taux de conversion, disponibilité locale, etc.). Le modèle le plus stable en production est un score additif avec normalisation des signaux, plutôt qu’un multiplicatif brutal.

Exemple concret (à adapter) :

  • text_score : score natif du moteur
  • stock_boost : 0 si hors stock, 1 sinon (ou une courbe log)
  • margin_boost : normalisé entre 0 et 1 par catégorie
  • popularity_boost : basé sur ventes 30j ou CTR search

Puis : final_score = text_score * 0.7 + stock_boost * 0.1 + margin_boost * 0.1 + popularity_boost * 0.1.

Deux points évitent 80% des catastrophes « merchandising » :

  1. Ne boostez jamais un produit non pertinent. Autrement dit, le tri commercial doit réordonner un ensemble déjà pertinent, pas injecter des hors-sujets.
  2. Normalisez vos signaux. Exemple classique : une marge de 200€ n’a pas le même sens en accessoires qu’en électroménager ; la normalisation par catégorie (ou par famille) stabilise le ranking.

Mini-scenario typique : un client tape baskets running homme. Sans garde-fou, si vous boostez trop la marge, vous pouvez remonter des « chaussures ville cuir » plus margées mais hors intention. À l’inverse, un boost modéré sur stock disponible et popularité sur les requêtes running peut faire remonter les bons modèles sans dégrader la perception.

Avec Elasticsearch, ça se fait proprement via function_score (pondérations, decay functions sur dates, filtres « in stock »). Avec Meilisearch, vous utilisez les ranking rules (et souvent des champs numériques) ; avec Algolia, customRanking + règles (Query Rules) pour pinner/booster. Le point clé : vous devez versionner ces règles et les tester, sinon vous allez « casser » la recherche à chaque ajustement merchandising.

Pour industrialiser, vous avez besoin de métriques sur lesquelles itérer : CTR des suggestions, taux de « zéro résultat », conversion après recherche, part des requêtes avec fautes, etc. Le cœur PrestaShop n’expose pas ça proprement, d’où l’intérêt de mettre en place une instrumentation dédiée (événements front + logs back anonymisés) et de lire Recherche interne PrestaShop : mesurer CTR, zéro résultat et conversion ainsi que Recherche interne PrestaShop : analytics RGPD et optimisation du moteur.

Intégration moteur dédié : indexation incrémentale, asynchrone, et observabilité

Si vous branchez Meilisearch/Elasticsearch/Algolia, le « vrai » chantier n’est pas la requête, c’est le pipeline d’indexation. Vous devez indexer par langue, gérer les shops (multiboutique), respecter les règles de visibilité (catalog mode, groupes clients), et surtout traiter les mises à jour sans reindex complet quotidien (sinon vous vous tirez une balle côté charge). L’article Moteur de recherche marketplace : Meilisearch, Elasticsearch ou Algolia pose bien les critères.

Sur PrestaShop 8.1/9, la méthode la plus stable consiste à capter les hooks métier (ex. sauvegarde produit, suppression, mise à jour des quantités), puis à pousser des jobs d’indexation dans une file asynchrone. En 2026, ignorer l’asynchrone est difficile à défendre : l’indexation ne doit pas pénaliser le BO (sauvegarde produit) ni le FO (mise à jour stock).

Quelques principes de conception qui évitent les reindex « panique » :

  • Indexation incrémentale : envoyer uniquement les produits modifiés (par ID), pas des dumps complets.
  • Idempotence : un même job rejoué ne doit pas corrompre l’index (upsert, version, horodatage).
  • Découpage : séparer les flux « contenu produit » (plus lourd) et « stock/prix » (plus fréquent).
  • Index versionné (blue/green) : utile pour des changements de mapping ou de ranking sans downtime (swap d’alias côté Elasticsearch, ou bascule d’index côté autres moteurs).

Symfony Messenger est une brique adaptée à cet usage (retries, DLQ, transports) : Symfony Messenger : configuration avancée, choix du transport et performances

Enfin, sans observabilité, vous pilotez à l’aveugle. Exposez au minimum : durée d’indexation, taille des lots, taux d’échec, latence P95/P99 des endpoints de suggestion et de résultats, et backlog de queue. Prometheus est un choix standard pour instrumenter la stack (API + workers) : Prometheus : configuration des alertes, métriques et requêtes PromQL

Côté diagnostic moteur, gardez en tête qu’Elasticsearch n’est pas « juste » un moteur de recherche, mais aussi un moteur d’analyse : profils de requêtes, stats d’index, compréhension des caches, analyse des temps par phase (query vs fetch). Cette capacité d’introspection est souvent ce qui fait gagner des heures quand une latence explose après une montée de charge (soldes, Black Friday, pics localisés sur une région/POP).

Mise en production : compat, SEO, sécurité et stratégie de repli

Déployez d’abord en shadow mode : vous servez encore la recherche native à l’utilisateur, mais vous envoyez les mêmes requêtes au nouveau moteur pour comparer (latence, top results, taux de zéro résultat). C’est le seul moyen raisonnable de ne pas « casser » le business à J0. Si vous êtes sur PrestaShop 9 (Symfony 6.4, thèmes modernisés), profitez-en pour isoler le composant front du search afin de pouvoir switcher de backend sans retoucher l’UX.

Deux détails pratiques pour un shadow mode utile (et pas juste « on loggue des trucs ») :

  • Comparez à la fois les résultats (top 3 / top 10) et la latence (P50/P95). Un moteur peut être « meilleur » mais trop lent.
  • Échantillonnez intelligemment : par langue, par type de requête (marque, SKU, longue traîne), et par canal (mobile/desktop).

Côté SEO, verrouillez un point : les pages de recherche internes ne doivent généralement pas être indexées (risque de contenu pauvre, duplication, facettes involontaires). Gardez un noindex, follow sur ces pages, et évitez que l’autocomplete génère des URLs crawlables. Pour la partie « pages d’atterrissage » réellement voulues (catégories, facettes maîtrisées), traitez ça séparément (voir Pages d’atterrissage SEO : maîtriser facettes, indexation Google et maillage interne).

Côté sécurité et résilience, ne négligez pas les basiques : validation stricte de l’input (taille, charset, normalisation Unicode), rate limiting, et protections WAF si vous avez du trafic opportuniste. Un endpoint de live search est une cible facile pour du scraping ou du déni de service « low and slow ». Si vous exposez une API interne ou des endpoints module, alignez-vous sur des principes de durcissement (clés, quotas, logs) comme dans API : sécuriser apikey, limiter le débit et renforcer la conformité.

Dernier point (souvent ignoré) : préparez un plan de repli. Le jour où votre cluster Elasticsearch est rouge, ou votre Meilisearch indisponible, il vous faut un fallback (même dégradé) vers la recherche native ou un mode « catégories + best-sellers ». En pratique, ça se traduit par :

  • un timeout court côté FO (ex. 200–400 ms sur suggest, un peu plus sur results),
  • un circuit breaker (désactivation temporaire du moteur si erreurs en rafale),
  • une bascule contrôlée (feature flag) et testée.

Donald Knuth rappelait : « Premature optimization is the root of all evil. » (Knuth, 1974). Traduisez-le ici en discipline : mesurez, automatisez les bascules, et testez la panne volontairement. Une recherche interne ne vaut rien si elle n’est pas stable sous charge, observable, et réversible.



À lire aussi