Ricerca lenta su PrestaShop: cause, filtri e come misurare

di Presta365 Guide tecniche

Il modulo per questo problema

Ricerca Elasticsearch & Navigazione a Faccette

Ricerca col motore integrato o con Elasticsearch, OpenSearch e Meilisearch, tolleranza ai refusi, filtri a faccette e analisi delle ricerche senza risultati. Si installa col motore integrato e un motore esterno si collega quando serve. PrestaShop 1.7.7, 8 e 9, 12 mesi di aggiornamenti inclusi, poi rinnovo facoltativo.

Scopri il moduloLeggi la guidaProva la demo

Come cerca la ricerca nativa di PrestaShop

Verificato nel codice di PrestaShop 9.1.5 (classes/Search.php) e del modulo ps_facetedsearch 5.0.0; i file sono nelle fonti.

L’indice

PrestaShop non cerca nel testo dei prodotti: cerca in un indice proprio. La tabella search_word contiene le parole del catalogo, distinte per lingua e per negozio, e search_index lega ogni prodotto a ciascuna delle sue parole con un peso. Il peso dipende dal campo (nome, riferimenti, descrizioni, categoria, marca, tag, attributi, caratteristiche, opzioni PS_SEARCH_WEIGHT_*) e un peso 0 esclude il campo dall’indice. Le parole sotto la lunghezza minima (PS_SEARCH_MINWORDLEN) non vengono indicizzate e le «Parole da non indicizzare» (PS_SEARCH_BLACKLIST) vengono scartate. L’indice si aggiorna al salvataggio di un prodotto se l’indicizzazione automatica è attiva e si ricostruisce da Parametri Negozio, Cerca, con «Aggiungi all’indice i prodotti mancanti» e «Ricostruisci l’intero indice», oppure con l’URL per il cron mostrato nella stessa pagina.

La ricerca

Per ogni parola digitata Search::find interroga l’indice con una condizione LIKE sulla parola. Di default il jolly è solo in coda («parola%»). Con «Ricerca nella parola» (PS_SEARCH_START) il jolly compare anche in testa, e l’interfaccia avverte che può richiedere molte risorse; con «Cerca una corrispondenza esatta» (PS_SEARCH_END) sparisce quello in coda. I prodotti trovati per le singole parole vengono incrociati in PHP, poi una sottoquery somma i pesi di ogni prodotto per la rilevanza e una query finale carica i risultati e ne conta il totale. Con l’ordinamento per prezzo l’intero insieme viene caricato e ordinato in PHP prima di essere impaginato.

La ricerca approssimativa

Se la ricerca esatta non trova nulla e «Ricerca Fuzzy» (PS_SEARCH_FUZZY) è attiva, PrestaShop legge dall’indice le parole di lunghezza compatibile (oltre 100.000 parole, solo quelle della stessa lunghezza) e calcola in PHP la distanza di Levenshtein da quella digitata, accettando la più vicina entro una differenza massima (PS_SEARCH_FUZZY_MAX_DIFFERENCE, 5 di default secondo l’interfaccia). I tentativi per ricerca sono limitati da PS_SEARCH_FUZZY_MAX_LOOP, e l’interfaccia avverte che l’opzione consuma risorse.

Perché rallenta quando il catalogo cresce

Sono meccanismi, non numeri: i tempi dipendono da catalogo, lingue, filtri e server.

  • Il jolly in testa. Secondo la documentazione MySQL un confronto LIKE può usare un indice come intervallo solo se l’argomento è una stringa costante che non inizia con un jolly; MariaDB dice lo stesso. L’indice di search_word (lingua, negozio, parola) aiuta con «parola%», non con «%parola%».
  • La dimensione dell’indice. search_index ha una riga per ogni coppia prodotto-parola, con parole distinte per lingua e per negozio: cresce con prodotti, parole per prodotto e lingue.
  • Join e somme dei pesi. Ogni ricerca unisce le tabelle dell’indice a quelle dei prodotti e la rilevanza è una sottoquery per ogni prodotto trovato.
  • Il ciclo approssimativo. Dopo una ricerca vuota, un elenco di parole letto dal database viene confrontato una per una in PHP.
  • L’ordinamento per prezzo in PHP, che carica tutti i risultati invece di impaginarli nella query.
  • I conteggi dei filtri, descritti qui sotto.

Filtri: perché un conteggio può non tornare

ps_facetedsearch calcola ogni conteggio con una query COUNT(DISTINCT p.id_product) raggruppata per valore, applicando i filtri selezionati tranne quello del gruppo che sta contando. Conta prodotti, non combinazioni. Un numero può divergere dai risultati per questi motivi, tutti visibili nel codice:

  • Cache dei blocchi filtro. Con l’opzione attiva (lo è dopo l’installazione) i blocchi con i loro conteggi sono salvati nella tabella layered_filter_block per categorie, marchi e fornitori (non per la ricerca) e svuotati dagli hook di salvataggio di prodotti, categorie, attributi, caratteristiche e regole di prezzo. Tra gli hook registrati non ce n’è uno sulla variazione di quantità: dopo aggiornamenti di magazzino fuori dalla scheda prodotto il blocco può restare quello vecchio finché non usi «Pulisci la cache» o il cron di svuotamento.
  • Indice prezzi. Il filtro prezzo usa layered_price_index con un join interno: un prodotto assente dall’indice non compare quando il filtro è attivo. L’indice si aggiorna al salvataggio del prodotto e dal pannello «Indici e cache» («Indice tutti i prezzi mancanti», «Ricostruire l’intero indice dei prezzi»); il pannello consiglia cron regolari, con una ricostruzione notturna.

Cosa cambia con un motore esterno

Elasticsearch, OpenSearch e Meilisearch tengono l’indice in un servizio separato, progettato per cercare testo.

  • Indice invertito. Per la documentazione di Elasticsearch è una struttura che associa ogni termine ai documenti che lo contengono, con un dizionario ordinato dei termini e, per ciascuno, un elenco di documenti. Il testo cercato è analizzato come quello indicizzato.
  • Rilevanza. Elasticsearch e OpenSearch usano BM25 come modello predefinito.
  • Refusi. In Elasticsearch il parametro fuzziness è una distanza di edit di Levenshtein calcolata dal motore; il valore AUTO è quello generalmente consigliato (da 0 a 2 caratteri la parola deve coincidere, da 3 a 5 è ammesso un errore, oltre 5 due). OpenSearch ha lo stesso parametro. In Meilisearch la tolleranza ai refusi è attiva di default: un errore per termini di almeno 5 caratteri, due da 9.
  • Lingue. Elasticsearch offre analizzatori per lingua, tra cui italiano, francese, tedesco, inglese, spagnolo e olandese.
  • Conteggi. Le aggregazioni producono riepiloghi come «quanti prodotti in ogni categoria» nella stessa richiesta di ricerca, con il parametro aggs. OpenSearch avverte che consumano più CPU e memoria delle query.
  • Costi. È un servizio in più da installare, aggiornare e tenere acceso, e l’indice va tenuto allineato al catalogo: ogni prodotto esiste nel database e nel motore. Per cataloghi piccoli la ricerca nativa spesso basta.

Come misurare prima di decidere

  1. Tempo della pagina di ricerca. Da un terminale: curl -o /dev/null -s -w %{time_starttransfer}\\n https://example.com/index.php\?controller=search\&s=trapano. Per la documentazione di curl, time_starttransfer è il tempo in secondi fino al primo byte, compreso quello che il server impiega a calcolare il risultato. Prova da 5 a 10 ricerche reali, ciascuna tre volte: un codice prodotto, una parola con un errore di battitura, due parole (con il segno più al posto dello spazio), una marca.
  2. Log delle query lente. In MySQL e MariaDB è disattivato di default e il limite predefinito di long_query_time è 10 secondi: abbassalo per la prova, poi usa EXPLAIN sulle query trovate per vedere se usano un indice.
  3. Profilo di PrestaShop. La costante _PS_DEBUG_PROFILING_ in config/defines.inc.php, per il commento del file, mostra informazioni sulle prestazioni sotto ogni pagina. Usala su una copia di prova.
  4. Statistiche delle ricerche. Il modulo «Ricerca negozio» (statssearch) salva parola cercata, numero di risultati e data a ogni ricerca; in Statistiche elenca le parole cercate più di una volta.
  5. Un filtro a mano. Annota il conteggio accanto a un valore, cliccalo e confronta con i prodotti elencati; ripeti dopo aver cambiato un prezzo o una giacenza.
  6. La demo. Prova le stesse ricerche e gli stessi filtri sulla demo, che ha un catalogo grande e il modulo attivo.

Prima di cambiare motore conviene anche provare le impostazioni: spegnere «Ricerca nella parola» se non serve, ridurre le parole approssimative ammesse, azzerare il peso dei campi che non ti servono e ricostruire l’indice.

Come lo fa il modulo Ricerca Elasticsearch & Navigazione a Faccette

  • Motore a scelta: integrato (legge il database di PrestaShop, nessun servizio in più), Elasticsearch, OpenSearch o Meilisearch.
  • Con Elasticsearch e OpenSearch risultati e conteggi dei filtri arrivano dalla stessa richiesta: le aggregazioni viaggiano nel corpo della ricerca.
  • I conteggi sono in modalità automatica: attivi con un motore esterno, spenti con il motore integrato, dove costerebbero un conteggio per gruppo di filtri. Si possono forzare dalla scheda Faccette.
  • Refusi: fuzziness del motore esterno; con il motore integrato un dizionario di trigrammi, consultato solo dopo una ricerca senza risultati, che sceglie la parola più vicina con la distanza di Levenshtein su un insieme ristretto. Corregge una parola alla volta, non ordina per somiglianza e non guarda le descrizioni.
  • Se il motore esterno non risponde, un interruttore lo esclude per 30 secondi e, con il fallback attivo (lo è di default), le ricerche ricadono sul motore integrato.
  • L’indice segue il salvataggio e la cancellazione dei prodotti e le variazioni di giacenza; un cron protetto da token e un assistente di reindicizzazione lo ricostruiscono.
  • Analisi delle ricerche con le più frequenti e quelle senza risultati, e template di filtri per categoria.

Vedi la scheda del modulo, leggi la guida all’installazione e alla configurazione o prova la demo.

Fonti

Domande frequenti

Da quanti prodotti conviene un motore esterno?

Non esiste una soglia valida per tutti: dipende da prodotti, lingue, numero di filtri e server. Per cataloghi piccoli la ricerca nativa spesso basta. Meglio misurare prima, con le prove qui sopra.

La ricerca approssimativa di PrestaShop rallenta ogni ricerca?

No, interviene solo quando la ricerca esatta non trova nulla: allora legge le parole candidate dal database e calcola in PHP la distanza di Levenshtein. Il costo pesa sulle ricerche senza risultati.

Perché il numero accanto a un filtro non corrisponde ai prodotti elencati?

Le cause visibili nel codice sono la cache dei blocchi filtro, non svuotata da alcune modifiche di magazzino, e un indice prezzi o attributi non aggiornato. Il gruppo già filtrato viene inoltre contato senza il proprio filtro.

Un motore esterno è sempre più veloce?

Non per forza. Sposta la ricerca su un indice pensato per il testo, ma è un servizio in più da tenere acceso e allineato, e le aggregazioni consumano più CPU e memoria delle query.

Il modulo basta da solo?

No. Con il motore integrato la ricerca resta una ricerca sul database di PrestaShop, con filtri e correzione dei refusi migliori ma senza cambiare il meccanismo di fondo. Un motore esterno va installato, aggiornato e tenuto acceso, e sinonimi e analizzatori vanno configurati. Prima conviene misurare.

Lascia un commento

I commenti sono moderati prima della pubblicazione.

← Blog

Caricamento in corso ...
Torna all'inizio