Comment mettre en place un moteur de recherche sans base de données ?
Je vais essayer de vous montrer comment j'ai mis en place un moteur de recherche sur un de mes sites, sans bases de données,
en faisant une recherche sur les pages du site en question. Les mots à rechercher sont séparer par un espace. Le module de recherche
prend en compte les accents et la casse.
Les résultats sont affichés comme suit :
- Titre de la page et nombre d'occurences trouvées sur cette page
- Morceau de la ligne contenant l'occurence recherchée et affichage de celle-ci en surbrillance
Les résultats sont finalement affichés triés par ordre décroissant d'occurences trouvées.
Je ne rentrerai pas dans le détail des feuilles de style utilisées ici.
Voici les différentes étapes :
Le formulaire de recherche
Un formulaire tout simple avec un méthod en POST :
<form method="POST" action="search.php">
<input type="text" size="15" name="search" id="search" />
<input type="button" title="rechercher" value="searchOK" />
</form>
Récupération des données dans le fichier search.php
Voici le début du code source, que je vais détailler :
if (isset($_POST["searchOK"])) {
$search = (isset($_POST["search"])) ? trim($_POST["search"]) : "" ;
# Test sur la longueur des mots saisis
if (strlen($search)>0) {
$tempTab = explode(" ",$search) ;
$tropCourt = false ;
foreach ($tempTab as $v) {
if (strlen($v)<=3) {
$tropCourt = true ;
}
}
if ($tropCourt) {
echo "Recherche sur : ".str_replace("|"," ",$search) ;
if (sizeof($tempTab)>1) {
echo "Merci de saisir plus de 3 caractères dans tous vos termes de recherche." ;
} else {
echo "Merci de saisir plus de 3 caractères dans votre critère de recherche." ;
}
}
}
}
Dans cette partie, je teste tout d'abord si l'utilisateur a bien cliqué sur le bouton de recherche et n'est pas tombé sur la page
par hasard...
Ensuite, je teste la longueur de chaque mots saisis (séparés par un espace) car je ne souhaite pas
que l'on puisse faire une recherche sur un mot de mois de 3 caractères... trop de bruit dans les réponses !
Les messages d'information ne sont pas ici formaté, je vous laisse le soin de créer une classe CSS à votre goût.
Recherche dans les pages du site
C'est maintenant que cela se complique :
Je vais détailler le code au fur et à mesure :
# Remplacement des espaces par des "pipe" afin de chercher sur tous les mots
# à l'aide d'une expression régulière
$search = str_replace(" ","|",$search);
Je commence tout d'abord par remplacer tous les espaces pouvant être saisis dans la
chaîne à rechercher par des "pipe", qui me permettra de faire ma recherche sur
tous les mots saisis, à l'aide d'une expression régulière.
J'ai mis dans un tableau toutes les pages dans lesquelles je souhaite faire une recherche. Il y a beaucoup d"autres méthodes, comme parcourir
l"arborescence, mais le tableau était celle qui me convenait le mieux. Ce tableau $URL_REDIRECTcontient
comme clé le titre que je souhaite faire afficher, et comme
valeur l'url du fichier.
Définissons d'abord les variables nécessaires :
# Nombre de caractères à afficher dans le résultat
$nbeCar = 50 ;
# Nombre d'occurences trouvées au total
$nbeRechercheTotal = 0 ;
# Tableau récupérant le nombre d'occurences trouvées
$tabNbeOccurence = array();
# Tableau permettant l'affichage du résultat
$tabAff = array() ;
# Mise en place de la chaîne de remplacement
$remplacement = "<span class='motTrouve'>$0</span>" ;
# On retire tous les accents à la chaîne de recherche
$patternSearch = regexAccents($search);
- Dans la chaîne de remplacement, on utilise la variable $0 (c'est un zéro) qui permet, suite à l'utilisation de la méthode
preg_replace d'être remplacée par la chaîne que l'on souhaite.
- La méthode regexAccents permet, par le biais d'un expression régulière, de traiter tous les
accents de la chaîne de recherche. En fait, cette méthode va remplacer toutes les lettres assujéties à avoir un accent par un ensemble
de ces mêmes lettres accentuées et séparées par un pipe afin de pouvoir l'utiliser dans une expression
régulière.
function regexAccents($chaine) {
$accent = array('a','à','á','â','ã','ä','å','c','ç','e','è','é','ê','ë',
'i','ì','í','î','ï',
'o','ð','ò','ó','ô','õ','ö','u','ù','ú','û','ü','y','ý','ý','ÿ');
$inter = array('%01','%02','%03','%04','%05','%06','%07','%08','%09','%10',
'%11','%12','%13','%14','%15','%16','%17','%18','%19','%20','%21','%22',
'%23','%24','%25','%26','%27','%28','%29','%30','%31','%32','%33','%34','%35');
$regex = array('(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)',
'(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(a|à|á|â|ã|ä|å)','(c|ç)','(c|ç)',
'(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)','(è|e|é|ê|ë)',
'(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)','(i|ì|í|î|ï)',
'(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)',
'(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)','(o|ð|ò|ó|ô|õ|ö)',
'(u|ù|ú|û|ü)','(u|ù|ú|û|ü)','(u|ù|ú|û|ü)','(u|ù|ú|û|ü)',
'(y|ý|ý|ÿ)','(y|ý|ý|ÿ)','(y|ý|ý|ÿ)','(y|ý|ý|ÿ)');
$chaine = str_ireplace($accent, $inter, $chaine);
$chaine = str_replace($inter, $regex, $chaine);
return $chaine;
# Parcours du tableau contenant les pages du site sur lesquelles on souhaite effectuer la recherche
foreach ($TAB_SEARCH as $rewrite => $titre) {
$fichier = $URL_REDIRECT[$rewrite] ;
if (file_exists($fichier)) {
...
Nous allons maintenant formater le contenu du fichier afin qu'il soit exploitable pour une recherche. Le gros du travail est donc
de supprimer toutes les balises contenues dans le document. Une méthode PHP, strip_tags, semble
bien pratique, mais possède néanmoins un bug dans le cas de balises imbriquées.
Exemple :
<!-- Considérons la balise 'img' suivante : -->
<img src='<?php echo $URL_IMAGE ?>image.jpg' />
<!-- un strip_tags sur cette ligne ne donne pas le résultat escompté, c'est à dire :
<img src='http://.../image.jpg' />
mais le texte sera tronqué à cet endroit, et la recherche ne continuera pas plus loin sur cette page... -->
Seule solution : supprimer d'abord les balises PHP, puis utiliser la méthode strip_tags pour supprimer
toutes les autres balises.
# Récupération du contenu du fichier
$page = file_get_contents($fichier) ;
$pagePreg = $page ;
# Suppression des balises PHP
$supprTagPhp ="<\?(php)?[\n\s\r]*.*[\n\s\r]*\?>";
$pagePreg = preg_replace($supprTagPhp,"",$pagePreg) ;
# Suppression des autres balises
$pagePreg = strip_tags($pagePreg);
# Suppression des espaces inutiles
$pagePreg = preg_replace('@([\r\n])[\s]+@', ' ', $pagePreg);
# Au final, récupération du résultat avec remplacement des termes cherchés
$pageSearch = preg_replace("(".$patternSearch.")i",$remplacement,$pagePreg,-1,$count);
La dernière ligne de ce code permet de compter le nombre d'occurrences trouvées. Ce nombre est mis dans
la variable $count. Le paramètre -1 permet de faire une recherche
illimitée dans la chaîne.
On prépare maintenant l'affichage du résultat :
if ($pageSearch != $pagePreg) {
# Si la recherche a été fructueuse, $pageSearch et $pagePreg sont différents
$tabNbeOccurence[] = $count ;
$nbeRechercheTotal += $count ;
$pos = strpos($pageSearch, "<span class='motTrouve'>");
$aff = "<div class='searchResultat'>" ;
$aff .= "<a href='".$rewrite."' class='nomFichier'>".$titre."</a> - ";
$aff .= "<span class='nbeOccurence'>(".$count." occurence" ;
$aff .= ($count>1) ? "s" : "" ;
$aff .= ")</span>" ;
$aff .= "<br />";
$debut = ($pos-$nbe_car<0) ? 0 : $pos-$nbe_car ;
$pagePregAff = preg_replace("(".$patternSearch.")i",$remplacement,$pagePreg,1);
$length = (strlen($pagePregAff)<$nbe_car) ? strlen($pagePregAff) : $nbe_car*3 ;
if ($debut>0) { $aff.= "..."; }
$aff .= mb_substr($pagePregAff,$debut,$length,"UTF-8")."..." ;
$aff .= "</div>";
$tabAff[] = $aff ;
}
Dans ce code, je vais expliquer plusieurs points :
-
J'utilise des tableaux parce que je souhaite au final trier mes réponses par ordre décroissant de réponses trouvées.
-
Je recherche tout d'abord la position de "motTrouve" qui fait partie de ma chaîne de remplacement.
Bien évidemment, il ne faut pas que cette chaîne de caractères fasse partie du contenu d'une de vos pages... si c'est le cas,
remplacez "motTrouve" par "Sbradaravdje", comme ça, peu de chance que
vous tombiez dessus, à part pour un hommage à Perceval !
-
Je relance la commande de remplacement avec comme valeur 1 pour le paramètre gérant le nombre maximal
de remplacements. La raison est que je ne souhaite afficher en surbrillance que la première occurence trouvée. Cela est d'abord un
choix graphique, et puis cela évite que la chaîne soit coupée un peu plus loin au milieu d'une autre balise de remplacement.
$pagePregAff = preg_replace("(".$patternSearch.")i",$remplacement,$pagePreg,1);
-
J'utilise également la méthode mb_substr qui permet de couper une chaîne de caractères
mais qui se débrouille comme une grande pour ne pas la couper sur un caractère accentué
(donc sur 2 octets), ce que ne sait pas faire substr et qui vous retourne un joli caractère
sorti d'on ne sait où !
$aff .= mb_substr($pagePregAff,$debut,$length,"UTF-8")."..." ;
Affichage du résultat
if ($nbeRechercheTotal==0) {
echo "Aucun résultat pour votre recherche." ;
} else {
echo "<div class='critereSearch'>Recherche sur : <span class='motRecherche'>" ;
echo str_replace("|"," ",$search)."</span>\n" ;
echo "<span class='nbeReponse'>\n" ;
if ($nbeRechercheTotal==1) {
echo "(1 réponse)";
} else {
echo "(".$nbeRechercheTotal." réponses)" ;
}
echo "</span></div>\n" ;
arsort($tabNbeOccurence) ;
foreach ($tabNbeOccurence as $k=>$v) {
echo $tabAff[$k] ;
}
}
Pas grand chose à dire ici : on remplace de nouveau les pipe
par des espaces pour afficher la chaîne recherchée, et on trie le tableau contenant le nombre d'occurences
trouvées dans l'ordre décroissant, avant d'afficher les lignes de celui qui contient les phrases avec
les mots clés en surbrillance.
Résultat :
J'ai mis en place cette fonctionnalité sur le site de Sorgeat.