
Dopo anni di utilizzo di WordPress, ho recentemente analizzato più a fondo il suo sistema di ricerca nativo. Purtroppo, ne ho avuto la conferma: per molte funzionalità di base, WordPress si rivela un CMS sopravvalutato e il suo motore di ricerca ne è un esempio lampante.
Non si tratta di un dettaglio da poco. Considerato che WordPress nasce per gestire blog, magazine e siti di informazione, un sistema di ricerca interno efficiente non è un optional, ma uno strumento fondamentale per l’esperienza utente.
Una delle prime limitazioni che salta all’occhio è l’impossibilità, tramite le opzioni standard, di limitare la ricerca ai soli articoli, escludendo le pagine. Un’opzione così basilare dovrebbe essere a portata di clic, senza dover ricorrere a modifiche del codice.
Ma il problema più serio risiede nel modo in cui WordPress interroga il database. La ricerca standard, infatti, si basa su una query SQL che utilizza l’operatore LIKE con caratteri jolly (%) prima e dopo il termine cercato (es. %parola%).
Cosa significa in pratica? Significa che la ricerca non trova la parola esatta, ma qualsiasi occorrenza di quella sequenza di caratteri. Se un utente cerca “occhio”, i risultati includeranno anche parole come “finocchio”, “pidocchio” o “spocchioso”. Questo comportamento, che mescola risultati pertinenti e irrilevanti, è inaccettabile per un sito che vuole offrire contenuti di qualità.
Ricerca WordPress: Come Ottenere Risultati Pertinenti
Certo, la risposta più comune è: “c’è un plugin per questo”. Ma la vera domanda è: quanti plugin dobbiamo installare per rendere funzionale una piattaforma che dovrebbe già esserlo? La continua necessità di ricorrere a estensioni per colmare lacune così basilari è un sintomo preoccupante dello stato di WordPress.
Identificato il problema, vediamo qual è la soluzione. Esiste una strada per migliorare la ricerca senza installare l’ennesimo plugin? Assolutamente sì.
La soluzione più pulita ed efficiente è aggiungere una funzione personalizzata direttamente nel file functions.php del nostro tema child. In questo modo, eviteremo plugin esterni e le nostre modifiche non verranno sovrascritte con gli aggiornamenti del tema.
Codice per la ricerca esatta di WordPress dei termini e limitarla ai soli articoli:
<?php
/**
* Modifica la query di ricerca per cercare solo parole intere
* e limitare la ricerca ai soli articoli.
*/
function mio_tema_ricerca_avanzata( $query ) {
if ( !is_admin() && $query->is_main_query() && $query->is_search() ) {
// Limita la ricerca al tipo di contenuto 'post' (articoli)
$query->set( 'post_type', 'post' );
}
}
add_action( 'pre_get_posts', 'mio_tema_ricerca_avanzata' );
/**
* Modifica la clausola WHERE della query di ricerca per la corrispondenza di parole intere.
*
* @param string $search La clausola WHERE della ricerca.
* @param WP_Query $wp_query L'oggetto WP_Query.
* @return string La clausola WHERE modificata.
*/
function mio_tema_forza_ricerca_parole_intere( $search, $wp_query ) {
global $wpdb;
if ( !is_admin() && $wp_query->is_main_query() && $wp_query->is_search() && $wp_query->get( 's' ) ) {
// Ottieni i termini di ricerca grezzi
$search_terms = $wp_query->get( 'search_terms' );
if ( empty( $search_terms ) ) {
return $search; // Nessun termine di ricerca, non fare nulla
}
$n = '%'; // Carattere jolly standard di SQL LIKE
$search_modified = '';
$search_modified .= "AND (";
$leading_or = '';
foreach ( $search_terms as $term ) {
$esc_term = esc_sql( $term );
$regexp_term = '[[:<:]]' . $esc_term . '[[:>:]]';
$search_modified .= $leading_or;
$search_modified .= "($wpdb->posts.post_title REGEXP '$regexp_term')";
$search_modified .= " OR ($wpdb->posts.post_content REGEXP '$regexp_term')";
// Puoi aggiungere altri campi qui se necessario, es. post_excerpt:
// $search_modified .= " OR ($wpdb->posts.post_excerpt REGEXP '$regexp_term')";
$leading_or = ' OR '; // Per i termini successivi
}
$search_modified .= ")";
if ( !empty($search_terms) ) { // Se abbiamo termini da cercare
// Costruiamo la query per cercare nei titoli O nei contenuti
$like_clauses = [];
foreach ( $search_terms as $term ) {
$esc_term = esc_sql( $term );
$regexp_term = '[[:<:]]' . $esc_term . '[[:>:]]';
$like_clauses[] = $wpdb->prepare( "(($wpdb->posts.post_title REGEXP %s) OR ($wpdb->posts.post_content REGEXP %s))", $regexp_term, $regexp_term );
}
if ( !empty($like_clauses) ) {
$search = " AND (" . implode( ' OR ', $like_clauses ) . ")";
}
}
}
return $search;
}
add_filter( 'posts_search', 'mio_tema_forza_ricerca_parole_intere', 10, 2 );
?>
Spiegazione del codice:
mio_tema_ricerca_avanzata
(hookpre_get_posts
):- Questa funzione viene eseguita prima che la query principale venga eseguita.
if ( !is_admin() && $query->is_main_query() && $query->is_search() )
: Ci assicuriamo di modificare solo le query di ricerca del frontend e non quelle nel pannello di amministrazione o query secondarie.$query->set( 'post_type', 'post' );
: Imposta il tipo di contenuto da cercare solo supost
(articoli). Puoi modificarlo se hai custom post type, ad esempioarray('post', 'mio_custom_post')
.
mio_tema_forza_ricerca_parole_intere
(hookposts_search
):- Questo filtro permette di modificare direttamente la porzione
WHERE
della query SQL generata da WordPress per la ricerca. global $wpdb;
: Necessario per accedere all’oggetto database di WordPress e ai suoi metodi.if ( !is_admin() && $wp_query->is_main_query() && $wp_query->is_search() && $wp_query->get( 's' ) )
: Controlli simili al precedente, ma verifichiamo anche che ci sia effettivamente un termine di ricerca ($wp_query->get( 's' )
).$search_terms = $wp_query->get( 'search_terms' );
: WordPress scompone la stringa di ricerca in un array di termini.$esc_term = esc_sql( $term );
: È fondamentale “escapare” i termini di ricerca per prevenire SQL injection.$regexp_term = '[[:<:]]' . $esc_term . '[[:>:]]';
: Questa è la parte cruciale.[[:<:]]
e[[:>:]]
sono metacaratteri delle espressioni regolari di MySQL che corrispondono rispettivamente all’inizio e alla fine di una parola. In questo modo, se cerchi “arte”, troverà “arte” ma non “partecipare” o “martello”.
($wpdb->posts.post_title REGEXP '$regexp_term') OR ($wpdb->posts.post_content REGEXP '$regexp_term')
: Costruiamo la condizione SQL per cercare il termine esatto nel titolo o nel contenuto del post.$search = " AND (" . implode( ' OR ', $like_clauses ) . ")";
: Ricostruiamo la parte della clausolaWHERE
relativa alla ricerca testuale. Se l’utente cerca più parole, WordPress di default le cerca conOR
(basta che ne sia presente una). Questo codice mantiene quel comportamento, ma ogni parola viene cercata come parola intera. Se volessi che tutte le parole fossero presenti (AND), dovresti cambiareOR
inAND
nell’implode
.- Importante sulla compatibilità del database:
[[:<:]]
e[[:>:]]
sono specifici di MySQL per REGEXP. Se il tuo sito WordPress usa un database diverso (come PostgreSQL o SQLite con qualche plugin), la sintassi REGEXP per i “word boundaries” potrebbe essere differente (es.\b
in PostgreSQL). MySQL/MariaDB è comunque il database più comune per WordPress.
- Questo filtro permette di modificare direttamente la porzione
Come usare questo codice:
- Crea un Tema Child: Se non ne hai già uno, segui questa guida.
- Modifica
functions.php
: Apri il filefunctions.php
del tuo tema child. - Incolla il codice: Aggiungi tutto il blocco PHP riportato sopra.
- Salva e Testa: Salva il file e prova la ricerca sul tuo sito.