Les moteurs de recherche fourmillent d'options toujours plus intéressantes que les autres. Bien que la plupart semblent être un acquis depuis plusieurs années, force est de constater que même les fonctionnalités les plus simples demandent parfois de nombreuses lignes de code.
Nous allons nous intéresser ici à la correction automatique des requêtes de recherche. Cette fonctionnalité implique de nombreux paramètres mais permet souvent de se sortir d'un bien mauvais pas quand notre niveau d'orthographe décide de s'approcher du néant (et comme cela arrive à tout le monde, l'option de correction devient fort intéressante).
L'exemple ci-dessous digne des perles trouvées sur le site Bescherelle ta mère active la fonctionnalité de correction des requêtes de Google. Elle illustre ce qu'il est possible de réaliser en matière de correction en PHP objet, comme nous le verrons plus bas dans cet article...
Pour les impatients, vous pouvez télécharger le pack complet en PHP 5.5 ici (les versions antérieures du moteur de recherche ont été abandonnées) :
Comment corriger automatiquement une requête de recherche en PHP ?
La correction automatique des requêtes est présente depuis si longtemps sur Google que l'on en oublierait presque qu'il est très difficile d'atteindre un tel niveau de performance en matière de correction orthographique. Bing utilise également ce type de procédé et a même expliqué comment le moteur de recherche faisait pour corriger les requêtes via SearchEngineWatch.
En fait, de nombreuses méthodes permettent de corriger l'orthographe des mots tapés dans les requêtes de recherche. En voici quelques-unes :
- Utiliser un tableau de correspondance avec le mot bien orthographié et les variantes possibles. Cette méthode est lourde et ne permet pas avec certitude de trouver un maximum de correspondances.
- Utiliser la distance de Levenshtein pour mesurer la "distance" entre les mots (en réalité, le nombre de modifications de lettres à effectuer pour obtenir un mot exact). Par exemple, l'indice de Levenshtein entre "Google" et "Googli" est 1, car il suffit de modifier une seule lettre pour retomber sur le bon mot. La distance de Levenshtein est une technique intéressante mais ne doit pas être utilisée seule car elle ne vérifie pas vraiment l'orthographe, mais fait plutôt des correspondances entre des suites de lettres. Une variante consiste à utiliser une fonction comme similar_text() en PHP, mais le rendu n'est pas bien meilleur...
- Utiliser l'algorithme phonétique Soundex pour comparer la phonétique des mots sans faute avec celle des mots erronés.
- Utiliser l'algorithme phonétique Metaphone pour comparer les mots. Cet indice agit comme Soundex mais est plus précis en termes de phonétique. Son créateur a même inventé l'algorithme double metaphone non disponible en natif en PHP (mais qui apporte plus de précisions dans la phonétique de certaines langues, contrairement à Soundex et Metaphone qui utilisent l'anglais).
Certaines méthodes présentées ici peuvent être couplées avec d'autres pour augmenter le nombre de possibilités. Nous pouvons déjà imaginer que Google et Bing ne se contentent pas de ces techniques "simples", ils utilisent sûrement des analyses sémantiques et thématiques avant les corrections (pour restreindre les champs de correction, etc.).
Limites de la correction orthographique des requêtes en PHP
Chaque méthode impose un index de mots et expressions "sans faute d'orthographe" pour être utilisé comme base de comparaison. C'est la qualité et la taille de cet index inversé qui permet une plus grande précision du correcteur orthographique.
Qu'on se le dise, même avec un index inversé de la puissance de celui de Google ou Bing, des fautes passeront toujours entre les mailles du filet. La phonétique a ses limites et même un tableau de correspondance idéal ne pourra jamais répondre à tous les cas de figures. Ce type de fonctionnalités tend donc à une meilleure interprétation des requêtes sans pour autant garantir 100% d'exactitude.
Protocole de test de la correction automatique des requêtes
Les méthodes PHP objet (POO) de la correction orthographique automatique ont été ajoutées au sein du moteur de recherche PHP objet que j'ai créé il y a plusieurs mois. Ainsi, nous bénéficierons de tous les atouts du moteur existant avec les nouvelles fonctionnalités.
Plusieurs méthodes permettent de corriger l'écriture des requêtes de recherche (la méthode principale teste les algorithmes soundex et metaphone), de créer son propre index inversé, de vérifier si l'index existe, etc. Pour être totalement honnête, il est prévu que je réécrive le moteur complet avec plus de méthodes qu'auparavant afin de le découper encore davantage et le rendre plus modulaire qu'il ne l'est déjà, j'ai donc déjà entrepris ce travail avec ces nouveaux objets PHP.
Il est possible de personnaliser l'affichage qui précède ou encadre la requête corrigée. Dans la capture suivante (sans CSS donc à l'état brut), il est inscrit "Tentez avec une autre orthographe : " suivi de la requête corrigée (correspondant à l'ancre du lien réécrit en HTML).
Les expressions exactes recherchées (entre guillemets) sont récupérées ainsi que les mots indépendants (comme "amet" et "dolor" ici) afin de correspondre au maximum à la recherche originelle.
Il est possible également d'utiliser une méthode particulière (à placer obligatoirement après la méthode de correction !) pour afficher directement les résultats de la requête corrigée, comme c'est le cas sur Google notamment. Si cette méthode n'est pas appliquée, on risque d'obtenir le message "Pas de résultats pour cette recherche." si les fautes d'orthographes sont trop bloquantes pour la recherche.
Voici l'exemple du code du fichier moteur-normal-php5.5.php présent dans le pack (voir en haut de l'article). Les lignes surlignées correspondent aux ajouts spécifiques concernant la correction orthographique automatique.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Moteur de recherche - Mode normal</title> </head> <body> <form id="searchForm" name="moteurSubmit" method="GET" action=""> <label for="q">Rechercher :</label> <input type="text" value="<?php if(isset($_GET['q'])) { echo htmlspecialchars($_GET['q']); } ?>" name="q" id="moteur" /> <input type="submit" value="Envoyer" /> </form> <?php include_once("class.inc/BDD-PHP5.5.class-inc.php"); // Class PHP 5.5 (avec mysqli) include_once("class.inc/stopwords.php"); include_once("class.inc/moteur-php5.5.class-inc.php"); // Class PHP 5.5 // // $link se trouve dans BDD.class-inc.php, il s'agit de la variable de connexion // Attention, elle doit absolument être utilisée (sous ce nom ou un autre) en PHP 5.5 ou supérieur // N.B. : elle s'ajoute en début d'appel des class moteurRecherche($link...), autoCompletion($link...) et alterTableFullText($link...) // if(isset($_GET) && !empty($_GET['q'])) { $moteur = new moteurRecherche($link, stripslashes($_GET['q']), 'search', 'regexp', $stopwords, $exclusion = '', $encoding = 'utf-8', $exact = true, $accent = true); $colonnesWhere = array('title', 'description'); $alterTable = new alterTableFullText($link, 'table_search', 'search', $colonnesWhere); $moteur->moteurRequetes($colonnesWhere); } if(isset($moteur)) { // Affichage de la requête avec $moteur->requete echo '<h3>Résultats de la recherche : <em>'.$moteur->requete.'</em></h3>'; // Création de la table des mots corrects if($moteur->isIndex("correctindex", "table_search") == false) { // Créé l'index correct $moteur->createIndex(); } // Tableau des mots puis ajout dans la table $motsCorrects = array("lorem", "dolor", "amet", "sit", "ipsum", "dolor amet", "lorem ipsum"); $moteur->setIndex($motsCorrects); // Affichage de la correction des résultats $corrections = $moteur->getCorrection(); $moteur->getCorrectedResults(); if(!empty($corrections)) { echo "<p>Tentez avec une autre orthographe : ".$corrections."</p>\n"; } // Fonction d'affichage des résultats (fonction callback appelée ensuite) function display($requete, $nbResults, $mots) { if($nbResults == 0) { // Si aucun résultat n'est retourné echo "<p>Aucun résultat, veuillez effectuer une autre recherche !</p>"; } else { // Sinon on affiche les résultats en boucle // Affichage du nombre de résultats (optionnel) $affichageResultats = new affichageResultats(); echo $affichageResultats->nbResultats(true); // Instanciation des ID (et du numéro de résultat si besoin) $nb = 0; // Comptage dynamique du nombre de résultats if(isset($_GET['p'])) { // $nb = 0 + (limite * (numéro de page - 1)) $nb = $nb + (10 * ($_GET['p'] - 1)); } while(($key = mysqli_fetch_assoc($requete))) { $nb++; // Incrémentation de l'ID // On encode chaque clé/valeur de résultats en UTF-8 foreach($key as $k => $v) { $key[$k] = utf8_encode($v); } // Résultats de recherche à afficher (à personnaliser) $texte = "<div class='results' id='".$nb."'>\n"; $texte .= "\t<h3>".$nb." - ".$key['title']."</h3>\n"; $texte .= "\t<p>".$key['description']."</p>\n"; $texte .= "</div>\n"; // Affichage du contenu après surlignage des mots recherchés // N.B. : optionnel --> possibilité de remplacer par echo $texte; $surlignage = new surlignageMot($mots, $texte); echo $surlignage->contenu; } // Fin de la boucle while } } // Fin de la fonction display (callback) // Nombre de résultats par "tranche d'affichage" $limit = 10; // Numéro de page récupéré dynamiquement if(isset($_GET['p'])) { $page = htmlspecialchars($_GET['p']); } else { $page = 0; } // Lancement de la fonction d'affichage avec paramètres $moteur->moteurAffichage('display', '', array(true, $page, $limit, true)); // Ajout de la fonction de pagination // N.B. : le second paramètre correspond au "name" de $_GET['p'] $moteur->moteurPagination($page, 'p'); } ?> </body> </html>
Liste des méthodes de correction en PHP Objet du moteur de recherche
Le moteur de recherche contient 9 nouvelles méthodes dont trois objets privés, nous allons les décrire rapidement ici, bien que tout soit noté dans les commentaires de la class PHP à télécharger :
- getCorrection($tableIndex = "", $parametre = "q", $select = true) : méthode principale de la correction automatique qui retourne un lien réécrit avec la bonne requête corrigée. Il est important de faire correspondre le paramètre $paramètre utilisé pour le moteur ("q" par défaut", "s" sur WordPress, etc.). Enfin, $select (true ou false) permet de récupérer les valeurs de comparaison directement dans l'index (true) ou en les calculant à la volée (false - plus gourmant).
- getCorrectedResults() : objet à placer après getCorrection() (car il utilise sa valeur retournée) pour afficher directement les résultats issus de la requête corrigée (si des résultats correspondent bien entendu). Il suffit de supprimer ou commenter la ligne de cette méthode pour retirer la fonctionnalité.
- isIndex($tableIndex, $databaseName) : vérifie si la table de l'index ($tableIndex) existe dans la base de données ($databaseName). La méthode permet d'enregistrer le nom de la table pour ne pas avoir à la répéter ultérieurement (comme dans l'exemple présent dans cet article).
- getIndex($tableIndex = "") : retourne un tableau contenant toutes les informations relatives à la table de l'index inversé ($tableIndex), comme les mots présents, les valeurs métaphones et soundex, etc.
- createIndex($tableIndex = "") : crée la table de l'index. En réalité, l'idéal est de l'utiliser une seule fois pour améliorer les performances globales du moteur, car une fois l'index inversé créé, la méthode devient obsolète.
- setIndex($arrayWords = array(), $tableIndex = "") : ajoute un tableau de mots ou expressions ($arrayWords) dans l'index. Comme pour createIndex(), il convient d'utiliser cette méthode uniquement quand on veut remplir l'index inversé.
- queryStringToLink($string, $queryArg = "q") - méthode privée : fonction de modification automatique du lien présent derrière la requête corrigée (afin de correspondre à cette nouvelle requête).
- setQuery($query) - méthode privée : mutateur qui permet de modifier la requête de recherche à la volée (utile pour prendre en compte la correction automatiquement).
- getCleanQuery($query = "") - méthode privée : objet qui permet de nettoyer la requête afin de la rendre utilisable en cas de pagination automatique (scroll infini) ou par trigger (clic sur un bouton "Afficher plus de résultats").