Recherche interne PrestaShop : analytics RGPD et optimisation du moteur

Instrumenter et analyser la recherche PrestaShop, respecter le RGPD et améliorer pertinence et performances — du SQL natif à Elasticsearch.

Écran d'ordinateur avec des graphiques et des analyses de données, entouré de plusieurs appareils.

Table des matières :

  1. Instrumenter la recherche interne PrestaShop sans casser le front
  2. Analytics de recherche interne : KPI actionnables et schéma d’événements
  3. RGPD : ce que vous collectez (vraiment), base légale, et design « privacy by default »
  4. Implémentation technique : table de logs, scrub des termes, et attribution conversion
  5. Optimiser le moteur natif PrestaShop : pertinence, tolérance aux fautes et coûts SQL
  6. Passer à Elasticsearch/Solr/OpenSearch : seuils, architecture d’indexation, et tuning de pertinence
  7. Mise en production : tests, monitoring, et boucle d’amélioration continue

Instrumenter la recherche interne PrestaShop sans casser le front

Sur PrestaShop 8.1/8.2/9.x (PHP 8.2+ recommandé), le point d’entrée est toujours le contrôleur front SearchController (route historique index.php?controller=search ou route “pretty URL” selon Dispatcher). Le module ps_searchbar (ou un thème équivalent) ne “fait” pas la recherche : il génère un formulaire, éventuellement un endpoint d’autocomplétion, puis délègue au contrôleur. Concrètement, pour une instrumentation robuste, il faut mesurer au niveau contrôleur (requête, nombre de résultats, liste d’IDs) et au niveau UI (clic sur un résultat, ajout panier, conversion).

Le cœur PrestaShop n’expose pas un bus d’événements analytics pour la recherche interne. Si vous voulez éviter un override fragile, passez par un module qui s’accroche sur des hooks stables et lit le contexte. Les hooks utiles varient selon votre intégration, mais en pratique vous pouvez capter la requête avec $this->context->controller->php_self === 'search' dans hookDisplayHeader() (front), ou en amont via hookActionFrontControllerInitAfter() pour enregistrer côté serveur avant rendu. Si vous devez enrichir le payload avec les IDs produits, faites-le après que la recherche a été exécutée (côté contrôleur) : c’est le moment où Search::find() a été appelé.

Quelques points qui évitent des “mesures fantômes” (ou des métriques impossibles à comparer) :

  • Normalisez la source de la requête : selon les thèmes/modules, le paramètre peut s’appeler s, search_query, q… L’important est de choisir une valeur canonique (ex. query_normalized) et de tracer aussi la valeur brute si vous avez une base légale et une stratégie de scrub (voir section RGPD).
  • Gérez le multi-boutique / multi-langue : une même requête (“baskets”) n’a pas les mêmes résultats ni la même intention selon id_shop et id_lang. Sans ces dimensions, vos KPI sont vite inutilisables.
  • Distinguez page résultats vs autocomplétion : l’autocomplete peut être servi par un endpoint AJAX (et un algo différent). Mélanger les deux brouille les analyses de latence et de CTR.

Mini-scénario (concret, fréquent) : une boutique FR multi-langue vend des pièces auto. Les utilisateurs tapent “206 1.4 HDI”, “essuie glace 206”, “BOSCH A123”. Sans normalisation (espaces, tirets, casse) et sans dimension langue/boutique, vous concluez à tort que “206” est une requête non pertinente alors que c’est un pattern métier (modèle + motorisation).

La métrique “temps de réponse du moteur de recherche interne” est souvent ignorée alors qu’elle conditionne tout : autocomplétion qui lag, page résultats lente, effet domino sur le panier. PrestaShop natif peut déclencher des requêtes SQL lourdes sur gros catalogues ; si vous voyez des pics de latence ou des locks, commencez par profiler la chaîne SQL et le cache (voir l’approche applicable à la recherche dans l’article interne sur les lenteurs et requêtes SQL). Un bon réflexe : tracer p50 / p95 (pas seulement la moyenne) et séparer latence serveur (calcul + SQL) de latence réseau + rendu (front).

Analytics de recherche interne : KPI actionnables et schéma d’événements

Les KPI réellement actionnables sur une recherche interne e‑commerce sont généralement : (1) taux de “zéro résultat” (no_result_rate), (2) CTR sur liste de résultats (clic produit / recherche), (3) conversion post-recherche (commande attribuée à une recherche dans une fenêtre), (4) refinements (recherche modifiée dans les 30–60 s), (5) latence et taux d’abandon (retour arrière, sortie). Le suivi “pageview-only” (GA4 basique) ne suffit pas : il faut des événements structurés, sinon vous ne pouvez pas optimiser la pertinence ni arbitrer entre tuning du moteur et enrichissement du catalogue.

Pour éviter les KPI “qui ne mènent à aucune action”, associez chaque indicateur à une décision possible :

  • no_result_rate élevé → synonymes, correction orthographique, enrichissement du catalogue (titres, références), redirections vers catégories.
  • CTR bas mais results_count élevé → problème de ranking (mauvais boosts), snippets produits pauvres (titre, image, prix), ou incohérence avec l’intention.
  • Beaucoup de refinements → autocomplétion insuffisante, tolérance aux fautes faible, ou résultats trop génériques en top positions.
  • Latence p95 haute → index SQL, cache, facettes, ou moteur externe sous-dimensionné.

Pour la modélisation, restez simple : un événement search_submit (query normalisée, langue, device, nb résultats, latence) et un événement search_click (query hashée ou identifiant de recherche, product_id, position). Puis add_to_cart/purchase en reliant avec un search_request_id éphémère (UUID v4). Cette approche permet d’analyser le CTR par position, d’identifier les requêtes qui “matchent” mais ne convertissent pas, et d’objectiver les régressions lors d’une mise à jour, d’un reindex ou d’un changement d’algorithme.

Un schéma d’événements minimal (lisible et exploitable) peut se documenter comme une table interne :

Événement Quand Champs utiles (exemples) À surveiller (qualité)
search_submit Soumission recherche / arrivée page résultats request_id, query_scrubbed, id_shop, id_lang, results_count, response_ms, has_filters response_ms manquant, results_count incohérent, query non scrub
search_click Clic sur un produit depuis les résultats request_id, product_id, position, list_type (results/autocomplete) positions à 0, product_id absent
search_refine Nouvelle recherche dans une fenêtre courte previous_request_id, request_id sur-tracking (éviter chaque frappe)
purchase (attribution) Commande validée order_id, request_id (si dispo), attribution_window_s attribution “à tout” → fausse hausse

Côté calcul, gardez des définitions stables (sinon vous ne comparez jamais des pommes avec des pommes) :

  • no_result_rate = recherches_0_résultat / recherches_total
  • CTR = recherches_avec_clic / recherches_total (ou bien clics_total / recherches_total si vous autorisez plusieurs clics)
  • “refinement” = nouvelle recherche dans ≤ 60 s et différente de la précédente (sinon, juste un double-submit)

Pour la partie pure “métriques”, vous pouvez vous aligner sur la terminologie et les méthodes détaillées dans l’article interne “mesurer CTR, zéro résultat et conversion”. Cela vous aide aussi à définir une fenêtre d’attribution raisonnable (ex. 24h ou “jusqu’à la fin de session”), sans tomber dans l’attribution opportuniste.

Côté implémentation PrestaShop, le point “propre” est de générer un identifiant de recherche côté serveur et de l’injecter dans le template (Smarty/Twig selon votre stack) pour relier clics et commande. Exemple minimal (module) : dans un hook header, si on est sur la page recherche, on expose un JSON dans un <script type="application/json" id="search-ctx">…</script> avec request_id, query, results_count, response_ms. Ensuite, un JS léger écoute les clics sur .product-miniature a et POST un événement à un endpoint module (controller front) en incluant request_id et product_id.

Astuce “qualité de données” : au moment du clic, envoyez aussi la position (1..N) et le type de liste (résultats paginés vs autocomplete). Sinon, impossible d’évaluer l’effet d’un changement de ranking (un CTR global peut rester stable alors que la distribution par position se dégrade).

RGPD : ce que vous collectez (vraiment), base légale, et design « privacy by default »

Une recherche interne peut contenir des données personnelles, même si vous ne stockez pas explicitement un email. Les utilisateurs tapent des noms, numéros de téléphone, références de commande, voire des données de santé selon les secteurs. Le RGPD définit les données personnelles comme : « toute information se rapportant à une personne physique identifiée ou identifiable » (article 4(1) du RGPD, texte officiel : texte officiel du RGPD (EUR-Lex)). Par design, vous devez donc traiter les termes de recherche comme potentiellement sensibles, et mettre en place minimisation, limitation de conservation et mesures de sécurité.

Avant même de parler “outil”, clarifiez trois décisions de gouvernance (très opérationnelles) :

  1. Finalité : amélioration de la pertinence et de la qualité de service (recherche interne) vs marketing/comportemental.
  2. Niveau d’identification : aucun identifiant persistant / session non persistante / rapprochement compte client.
  3. Périmètre de collecte : page résultats seulement, ou aussi autocomplétion, clics, add-to-cart, commandes attribuées.

Sur le tracking, évitez le réflexe “cookie analytics partout”. La CNIL rappelle dans sa doctrine “Cookies et autres traceurs” que le consentement doit être libre, spécifique, éclairé et univoque, et que l’absence d’action (ex. continuer à naviguer) ne suffit pas à caractériser un consentement valide (référence : CNIL — Cookies et autres traceurs). Traduction technique : avant consentement, ne déposez pas d’identifiant persistant, ne faites pas de recoupement cross-session, et ne faites pas remonter des identifiants publicitaires. Si votre objectif est uniquement d’améliorer la recherche interne (qualité de service), vous pouvez viser une collecte cookieless côté serveur, strictement limitée et difficilement ré-identifiante.

Concrètement, un design RGPD robuste pour analytics de recherche interne ressemble à ça :

  • Normalisation + filtrage du terme : trim, lower, suppression accents si besoin, puis redaction (regex emails/téléphones) avant stockage.
  • Pseudonymisation : si vous devez relier des événements d’une même session, utilisez un identifiant de session applicatif non persistant (UUID en mémoire / session PHP) et ne stockez pas l’IP en clair.
  • Conservation : gardez les logs bruts (termes) très peu de temps (ex. 30–90 jours), et conservez plus longtemps uniquement des agrégats (top queries, taux zéro résultat). En France, la durée de 13 mois est souvent évoquée comme borne maximale pour certains usages de mesure d’audience ; dans tous les cas, documentez votre durée dans le registre et automatisez la purge.

Deux précisions “terrain” (souvent oubliées) :

  • Droits des personnes : même si vous pseudonymisez, prévoyez une manière de répondre à une demande d’accès/suppression si vous avez un identifiant reliant des données à une personne (compte client, commande). Si vous n’avez que des agrégats anonymes, documentez-le : c’est aussi une stratégie.
  • Transferts hors UE/EEE : si vous envoyez des événements à un prestataire (SaaS analytics, moteur managé), regardez l’hébergement et les transferts. C’est un point concret de conformité, surtout pour des boutiques françaises.

Implémentation technique : table de logs, scrub des termes, et attribution conversion

Sur PrestaShop 8.1+/9.x, implémentez l’enregistrement des recherches côté serveur pour ne pas dépendre d’un JS qui peut être bloqué. Pré-requis : accès DB, capacité à déployer un module, et une pré-production pour valider la charge. Risque : toute écriture synchrone en DB sur un endpoint fréquent peut devenir un point chaud. Si votre trafic est élevé, prévoyez une file (Redis / RabbitMQ) ou au minimum un INSERT asynchrone via buffer.

Schéma SQL minimal (InnoDB) :

CREATE TABLE ps_search_analytics (
  id_search_analytics BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  request_id BINARY(16) NOT NULL,
  created_at DATETIME NOT NULL,
  id_lang INT UNSIGNED NOT NULL,
  id_shop INT UNSIGNED NOT NULL,
  query_raw VARCHAR(255) NOT NULL,
  query_scrubbed VARCHAR(255) NOT NULL,
  results_count INT UNSIGNED NOT NULL,
  response_ms INT UNSIGNED NOT NULL,
  session_hash BINARY(32) NULL,
  PRIMARY KEY (id_search_analytics),
  UNIQUE KEY uniq_request (request_id),
  KEY idx_created (created_at),
  KEY idx_shop_lang (id_shop, id_lang, created_at),
  KEY idx_query (query_scrubbed)
) ENGINE=InnoDB;

Points clés : request_id stocké en BINARY(16) (UUID compact), session_hash optionnel (SHA‑256 d’un identifiant de session + sel rotatif). query_raw peut être supprimé si vous ne voulez jamais conserver le terme non scrub (c’est souvent le meilleur choix). Le scrub doit éliminer les emails (/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i), téléphones, IBAN, et tout motif métier connu.

Pour éviter de “scrubber au hasard”, listez explicitement vos motifs à risque (et testez-les) :

  • email, téléphone (formats FR et internationaux), codes postaux + nom/prénom si votre secteur le voit souvent,
  • numéros de commande (souvent structurés), identifiants clients,
  • tout champ “libre” sur lequel vos clients ont l’habitude de coller une info (ex. “mon colis 12345 pas reçu”).

Sur la performance DB, une bonne pratique consiste à séparer le log “submit” (volumétrie élevée mais un seul événement par recherche) et le log “click” (volumétrie variable) si vous avez besoin de détails. Par exemple, une table ps_search_click (requestid, productid, position, createdat) indexée sur (createdat) et (product_id). Ça évite d’alourdir ps_search_analytics avec des champs multi-valeurs ou du JSON difficile à requêter proprement.

Pour l’attribution conversion, évitez de “tagger” l’utilisateur. Faites une attribution commande ↔ requête à partir d’un identifiant éphémère stocké dans le panier (ou session) seulement après consentement si c’est persistant. Exemple : à la soumission d’une recherche, stockez request_id dans Context::cookie uniquement si l’utilisateur a consenti, sinon en $_SESSION. À l’ajout au panier depuis la page résultats, conservez le dernier request_id dans le panier (custom field via table module). À la validation de commande, copiez request_id dans une table ps_order_search_attribution (orderid, requestid, created_at). Cette approche respecte le principe de minimisation : vous ne stockez pas “qui”, vous stockez “quelle recherche a mené à quelle commande”.

Enfin, rendez la conformité opérationnelle : automatisez une purge (cron) sur les logs détaillés, par exemple “suppression des recherches > 90 jours”, tout en gardant des agrégats quotidiens (par requête scrubbed + shop + lang). Cela donne des tendances utiles sans garder de traces fines trop longtemps.

Optimiser le moteur natif PrestaShop : pertinence, tolérance aux fautes et coûts SQL

La recherche native PrestaShop repose sur un index applicatif en tables SQL (mots, associations, pondérations). Elle est pragmatique mais limitée : pas de BM25 avancé, synonymes multi-termes pénibles, fuzziness approximative, et pertinence souvent instable sur des catalogues multi-langues. Avant de jeter le core, commencez par comprendre son index et ses pondérations (structure des tables, logique de scoring, paramètres BO), puis mesurez ce que vous gagnez en jouant sur le contenu (noms/attributs) et l’index (réindex, stopwords, longueurs minimales). Référence interne indispensable : index de recherche PrestaShop — fonctionnement, tables SQL et pondérations.

En pratique, les gains “les moins chers” viennent souvent de la qualité catalogue, pas d’un changement de moteur :

  • Titres produits : inclure les variantes réellement cherchées (marque + modèle + type) sans bourrage.
  • Références : s’assurer qu’elles sont indexées si votre audience cherche par SKU/réf.
  • Attributs : si les utilisateurs cherchent “taille 42”, “noir”, “inox”, vos attributs doivent être exploitables (ou redirigez vers des filtres/facettes).
  • Nettoyage : supprimer doublons, produits inactifs indexés, catégories incohérentes.

La tolérance aux fautes et les suggestions live sont le deuxième levier. L’autocomplétion n’est pas qu’un gadget UX : elle réduit mécaniquement le taux de requêtes “foireuses” (typos, pluriels, accents) et donc le taux de zéro résultat. Côté PrestaShop, vous pouvez compléter le module de barre de recherche (ou un module custom) par : normalisation Unicode, suppression diacritiques, n-grams simples pour typos fréquentes, et un dictionnaire de synonymes versionné. Pour un plan d’implémentation concret (et les pièges de perf), appuyez-vous sur l’article interne “optimiser suggestions live et tolérance aux fautes”.

Enfin, la performance SQL est non négociable. Sur gros catalogues, la page recherche peut devenir une des pages les plus coûteuses après le checkout, surtout si vous combinez recherche + facettes (ps_facetedsearch) + tri + prix. Faites des EXPLAIN, surveillez les index manquants, et évitez les réindexations en pleine journée. Si vous devez réindexer souvent (import produits), basculez sur un cron nocturne et/ou une stratégie incrémentale. Pour aller plus loin sur la partie diagnostic (profiling, traces, requêtes lentes), la méthodologie de l’article interne “diagnostiquer lenteurs et requêtes SQL” s’applique directement aux requêtes d’index et aux requêtes de résultat.

Passer à Elasticsearch/Solr/OpenSearch : seuils, architecture d’indexation, et tuning de pertinence

Il y a un point où le moteur natif atteint ses limites : catalogue à plusieurs centaines de milliers de produits, multi-boutique multi-langue, besoin de synonymes métiers, tolérance aux fautes de type “fuzzy”, et exigences de latence (autocomplétion < 100 ms). Dans ces cas, un moteur dédié (Elasticsearch/OpenSearch/Solr) apporte un modèle de scoring moderne, des analyzers linguistiques, et des fonctionnalités indispensables (synonym_graph, edge n‑grams, boost par champs, règles de ranking). Les critères et bonnes pratiques de pertinence sont détaillés ici : moteur de recherche interne — Elasticsearch/Solr bonnes pratiques de pertinence.

Un repère simple pour décider (sans dogme) : si vos optimisations “contenu + index + SQL” n’arrivent plus à améliorer à la fois la pertinence et la latence sur vos requêtes critiques (top 50), vous êtes probablement à un seuil où un moteur dédié se justifie économiquement.

Architecture recommandée (simple et maintenable) : (1) un pipeline d’indexation asynchrone, (2) un schéma d’index versionné (products_v42), (3) un alias stable (products_current) pour faire du blue/green d’index, (4) une stratégie de rafraîchissement (refresh interval) adaptée, (5) une gestion des suppressions et du stock. Côté PrestaShop, l’erreur classique est de réindexer “en live” sur chaque mise à jour produit en requête synchrone. Préférez un job batch (cron) + un log de delta (table product_update_log) ou une file Redis. Si vous avez déjà une chaîne d’import API-first, réutilisez-la pour alimenter le moteur (approche similaire à un pipeline catalogue supervisé : pipeline API-first).

Le tuning de pertinence doit rester mesurable. Basez-vous sur vos logs (zéro résultat, CTR bas, refinements) pour construire un jeu de tests et des “relevance judgments” (même basiques). Dans Elasticsearch, BM25 est le défaut ; vous allez surtout travailler le mapping (analyzers par langue), les boosts (nom > référence > description), et les synonymes (attention aux faux positifs).

Point “gouvernance” (important en France/UE) : un moteur externe n’est pas un problème en soi, mais la maîtrise des accès et des logs l’est. Hébergement (UE/EEE si possible), journalisation, droits d’accès, chiffrement au repos, et contrôle des API. Sur ce point, gardez la même discipline que pour n’importe quelle API : limitation de débit, secrets, et principe du moindre privilège (cf. bonnes pratiques génériques : API — sécuriser apikey et limiter le débit). Pensez aussi au plan de continuité : que se passe-t-il si le cluster de recherche est indisponible ? (fallback sur la recherche SQL, message utilisateur, désactivation autocomplete, cache).

Mise en production : tests, monitoring, et boucle d’amélioration continue

Avant de déployer l’instrumentation analytics ou un nouveau moteur, verrouillez une checklist “SRE” minimale : sauvegarde DB, plan de rollback, et validation en pré-production avec un dump réaliste. Toute modification de recherche touche une zone très visible et très sollicitée; un bug peut se traduire par une perte immédiate de CA. Les bonnes pratiques d’approche incrémentale (préprod, tests, rollback) sont les mêmes que pour une mise à jour core (voir checklist de mise à jour PrestaShop).

Ajoutez des tests “métier” simples mais efficaces, en plus des tests techniques :

  • une liste de requêtes de référence (top requêtes + requêtes sensibles) avec un résultat attendu (au moins “doit retourner quelque chose”),
  • un contrôle que les produits top (meilleures ventes) restent trouvables,
  • un contrôle sur 2–3 langues si vous êtes multi-langue,
  • un test de charge minimal sur l’endpoint de recherche et l’autocomplete (ne serait-ce que pour observer la p95).

Sur la surveillance, ne vous contentez pas d’un dashboard marketing. Monitorer = erreurs + latence + volumétrie + dérives. Exemples de signaux : taux de 500 sur l’endpoint de recherche, p95 de réponse du contrôleur search, p95 de l’autocomplete, taux de “zéro résultat” par langue, et dérive du top 20 requêtes. Côté infra, surveillez MySQL (slow query log) ou le cluster Elastic (heap, GC, thread pools). Côté application, ajoutez des logs structurés et une alerte (Slack/email) dès que no_result_rate dépasse un seuil (par ex. +30% vs moyenne 7 jours). Pour mettre en place l’alerting temps réel côté PrestaShop, l’approche “module d’alerting” peut servir de base : module d’alerting temps réel (Slack/Discord/Email).

Enfin, la boucle d’amélioration doit être gérée comme du produit technique : un backlog de requêtes “zéro résultat” à traiter (synonymes, enrichissement catalogue, redirections vers catégories), des tests A/B sur les boosts (si moteur dédié), et une revue mensuelle de conformité (durées de conservation, purge, accès). Si vous intégrez de l’IA (ex. réécriture de requêtes, intent classification), traitez-le comme un traitement additionnel avec DPIA si nécessaire et une gouvernance claire (le cadrage RGPD + architecture d’intégration est abordé ici : IA PrestaShop — modules/API et conformité RGPD). Le résultat attendu n’est pas “plus de tracking”, mais un moteur plus pertinent, mesuré, et défendable juridiquement.


À lire aussi