Ciao,
avevo bisogno di un motore di ricerca per i contenuti del mio un sito, così che l'utente possa trovare subito gli articoli più utili. Dopo molte ricerche non sono riuscito a trovare un motore semplice da usare e soprattutto da adattare alla struttura del mio sito, del database, delle procedure di inserimento dei dati, ecc. Il tutorial su “freephp”, ad esempio, per quanto perfetto ed efficientissimo, mi è parso un po’ troppo complicato...ma questo dipende dal fatto che scrivo php per gioco, che sono insomma un dilettante...
Ho trovato però questo articolo, http://www.onlamp.com/pub/a/php/2002...rchengine.html , l’ho capito quasi subito e mi è sembrato che si potesse adattare bene ai miei problemi. Solo che l’articolo si riferiva all’indicizzazione di pagine html mentre a me serviva per dati già presenti nel db. E poi l’articolo aveva questo limite, di non considerare una stringa di ricerca composta da più parole, cosa invece frequentissima.
Io alla fine ho messo su questo script, che risolve qualcosa e che forse può servire ad altri e che però ha molti limiti su cui vi chiedo di darmi una mano, se vorrete ovviamente..
Innanzitutto, abbiamo bisogno di quattro tabelle molto semplici e leggere, aggiuntive rispetto a quelle già presenti nel db:
1.Una ‘pagina’ che conserva gli id e i titoli dei contenuti di testo del database (siano aziende, articoli, news, quello che volete).
2.Una ‘parole’, che contiene tutte le parole presenti nei contenuti inserite nei singoli record e i rispettivi id.
3.Una ‘occorrenze’ che incrocia l’id della tb ‘pagina’ con l’id dell tb ‘parole’.
4.Una ‘finale’ che è un po’ una tabella di riepilogo in cui raccolgo il risultato per la visualizzazione.
Il primo problema che lo script deve risolvere è indicizzare i contenuti in modo automatico ad ogni nuovo inserimento oppure modifica. Allora, subito dopo la query che inserisce i nuovi contenuti nella tabella di destinazione, ho aggiunto queste righe per avere un indice automatico, cioè per inserire ogni parola che sia nuova nella tb ‘parole’. Ripeto che sono debitore all’articolo citato prima di cui ho conservato molto codice.
//raccolgo i campi che mi interessano e li unisco
$testo =$titolo.' '.$descrizione.' '.$key.' '.$prodotti;
$buf = trim($testo);
//siccome uso tags html nei testi li rimuovo
$buf = strip_tags($buf);
$buf = ereg_replace('/&\w;/', '', $buf);
//estraggo le parole e per ogni parola controllo che non sia già inserita
preg_match_all("/(\b[\w+]+\b)/",$buf,$words);
for( $i = 0; $words[$i]; $i++ )
{
for( $j = 0; $words[$i][$j]; $j++ )
{
$cur_word = addslashes( strtolower($words[$i][$j]) );
$result = mysql_query("SELECT parole_id FROM parole
WHERE parole_parola = '$cur_word'");
$row = mysql_fetch_array($result);
if( $row['parole_id'] )
{
//se la parola è già inserita uso quella vecchia
$parola_id = $row['parole_id'];
}
else
{
//altrimenti inserisco la nuova e recupero pure il suo id perché devo inserirlo nella tb ‘occorrenze’
mysql_query("INSERT INTO parole (parole_parola) VALUES (\"$cur_word\")");
$parola_id = mysql_insert_id();
}
//nella tb ‘occorrenze’ quindi inserisco l’id del contenuto, che è ‘$ultimo_id’ e l’id della parola. Così so che sono tra loro in relazione!
mysql_query("INSERT INTO occorrenze (parola_id,pagina_id)
VALUES ($parola_id,$ultimo_id)");
}
}
Questa funzione mi sembra comoda e utile, soprattutto se gli articoli o le news ad esempio non sono lunghissimi. E sarebbe facile, pure, implementarla per avere delle pagine facilmente indicizzabili per i motori di ricerca (penso ai tag. pur sempre utili, description e keyword).
Ora vorrei chiedervi, proprio perché l’ho detto sono un dilettante, alcuni consigli:![]()
1. Lo scipt così fatto mi raddoppia le occorrenze, cioè per ogni parola mi inserisce due righe uguali, con gli id della parola e del contenuto, nella tb ‘occorrenze’. Ho provato a modificarlo ma non funziona più? Come mai?
2. Come posso fare per non indicizzare parole più corte di 3 caratteri (un po’ come il motore di ricerca di questo forum)? Oppure come potrei evitare di inserire una serie di parole magari presenti in una lista a parte?
Questo, invece, è lo script che, dopo aver ricevuto le parole da ricercare da un campo di nome ‘cerca’ di un form, procede alla ricerca e stampa i risultati.
$chiave = $_REQUEST["cerca"];
$array_chiave = explode(" ", $chiave);
//se le parole sono più di una mi serve un ciclo che faccia l’operazione per ogni parola
$i = 0;
foreach ($array_chiave as $key)
{
//allora qui mi cerca i contenuti che hanno la parola richiesta dall’utente e mi conta quante volte questa parola vi compare, così il risultato ottenuto è più che attendibile!!!
$sql = " SELECT pagina.pagina_id AS id, pagina.pagina_titolo AS titolo, parole.parole_parola as parola, COUNT(*) AS occorrenze
FROM pagina, parole, occorrenze
WHERE pagina.pagina_id = occorrenze.pagina_id AND
parole.parole_id = occorrenze.parola_id AND
parole.parole_parola = \"$key\"
GROUP BY pagina.pagina_id
ORDER BY occorrenze DESC
" ;
$result = mysql_query($sql);
// per ogni contenuto trovato vado ad inserire il risultato nella tb ‘finale’ che mi dice insomma quante volte la parola ‘tizio’ richiesta dall’utente compare nel contenuto ‘x’
while
($riga = mysql_fetch_array ($result))
{
$parola = $riga[parola];
$contenuto = $riga[id];
$punti = $riga[occorrenze];
$finale = "INSERT INTO `finale` ( `parola` ,`contenuto` , `punti`)
VALUES
('$parola','$contenuto', '$punti')";
$query = mysql_query($finale) or die (mysql_error());
}
}
//ora posso stampare i risultati
echo"
Risultati della ricerca per la frase: $chiave.
";
//qui incrocio la tabella ‘finale’ con quella ‘contenuti’ per recuperare il titolo dei contenuti per una migliore visualizzazione. Qui ho l’inserimento dei risultati per ogni parola ricercata dall’utente.
$fine = "SELECT finale.contenuto, Sum( finale.punti ) AS somma, contenuti.titolo, FROM finale INNER JOIN contenuti ON finale.contenuto = contenuti.id GROUP BY finale.contenuto ORDER BY somma DESC";
$ris = mysql_query($fine);
while ($riga = mysql_fetch_array ($ris))
{
echo"
<a href=\"contenuto_det.php?id=$riga[contenuto]\">$riga[titolo]</a>
($riga[somma] occorrenze)</p>
";
}
//e poi svuoto la tb ‘finale’ così da non avere vecchie ricerche che si sommino alle nuove.
$svuota = "TRUNCATE TABLE `finale`";
//$query = mysql_query($svuota) or die (mysql_error());
Lo script, per le mie esigenze è ottimo, credevo fosse lento e invece è velocissimo e lavora già su oltre 3.000 parole e quasi 100 articoli. Inoltre la visualizzazione dei risultati è corretta in quanto il numero delle occorrenze è l’unico indicatore valido, mi sembra, per confermare l’attinenza di un contenuto alla ricerca dell’utente.
![]()
![]()
![]()
Ma c’è un problema che proprio non sono riuscito a risolvere: questo script nella sua logica funziona solo per ricerche del tipo “OR” cioè non riesco ad evitare che compaiano anche i contenuti che contengono una sola delle parole richieste e non tutte e due come sarebbe nel caso di una ricerca ‘AND’. Certo, il conteggio delle occorrenze, che funziona bene, di solito fa visualizzare in alto i contenuti con entrambe le parole ma sarebbe opportuno, se l’utente richiede una ricerca ‘AND’ o se questa come in Google è di default, escludere del tutto gli articoli che non presentano tutte le occorrenze.
Dove intervenire? Dove e come bloccare lo script o selezionare solo i contenuti giusti?
Mi scuso se il messaggio è troppo lungo o se ho violato inavvertitamente qualche regola....