Depuis plusieurs mois, je voulais prendre du temps pour créer mon propre moteur de recherche interne en PHP Objet (POO pour les intimes) afin de gagner du temps en cas de besoin dans des projets à venir. En effet, toute la force de la programmation orientée objet est de créer des « fonctions » prêtes à l’emploi qui demandent peu de lignes de code pour être utilisées (même si le développement procédural est tout aussi efficace en réalité, contrairement aux mythes faussement répandus). Dans le cas de mon moteur de recherche, je tenais absolument à avoir un résultat complet sur bien des points (qui seront cités par la suite) afin de répondre à un maximum d’éventualité. Après quelques heures de travail acharné, j’ai décidé de vous faire partager le code, en espérant qu’il fera le bonheur de pas mal d’entre vous (sachant que tout ce code peut encore être amélioré bien entendu)…
Edit (17/12/2018)
La page a été revue et corrigée pour correspondre par défaut à un usage classique du moteur de recherche pour PHP 5.5 et supérieurs (notamment PHP 7 et ses successeurs, bien plus performants). Le moteur fonctionne très bien jusqu'à PHP 7.3 actuellement, même si nul n'est parfait bien entendu.
Pour ceux qui veulent aller plus loin que les fonctionnalités présentes dans cet article, sachez que j'ai publié un autre article sur ce moteur pour proposer plusieurs types de pagination en Ajax (en infinite scroll ou par trigger). La dernière version publiée le 15 juillet 2015 fait également état d'une nouvelle fonctionnalité intéressante : la correction automatique des résultats.
N'hésitez pas à télécharger les packs du moteur de recherche car ils contiennent plusieurs cas d'usage détaillés, avec différentes options.
N.B. : il se peut que je supprime à terme la version pour PHP 4 et jusqu'à PHP 5.4 car ces versions de PHP sont obsolètes depuis quelques temps déjà. Je veux prendre le temps de réécrire l'ensemble du moteur de recherche mais cela dépend clairement de mon activité personnelle, qui ne me permet pas jusqu'à présent de pouvoir tout refaire comme je le souhaiterais. J'ai pas mal d'idées en tête donc je pense pouvoir faire bien mieux pour ce moteur dans les faits... ;-)
Les possibilités du moteur de recherche PHP
L’usage du moteur de recherche interne fonctionne parfaitement à partir du moment où vous disposez d’une base de données (bien construite si possible pour faciliter le traitement final), il ne s’agit pas ici d’un moteur comme Google ou Bing avec un robot d’indexation…
Voici une liste des possibilités offertes par les objets PHP (ou méthodes si vous préférez) qui concernent le traitement des requêtes, l’utilisation du code sera détaillée par la suite :
- 3 méthodes de recherche : LIKE (approximatif), REGEXP (plus précis -> par défaut), FULLTEXT (l’idéal)
- 2 types d’application de la recherche : exacte (tous les mots -> par défaut) ou non (un ou plusieurs mots justes)
- Recherche précise (expression entre guillemets) ou non (mot à mot), quel que soit la méthode de recherche utilisée (normalement, cela ne fonctionne qu’avec FULLTEXT...)
- Exclusion de mots (avec un tableau PHP de « stop words » -> un exemple fourni en téléchargement avec le code du moteur) et/ou de mots en dessous d’une certaine taille
- Choix de l’encodage pour le traitement (UTF-8 par défaut et très fortement conseillé…)
- Possibilité de rechercher sans accent après nettoyage de la requête (si la base de données n’a que des contenus sans accent bien entendu, sinon c’est inutile -> désactivé par défaut)
N.B. : si vous souhaitez utiliser la méthode FULLTEXT, il faut absolument ajouter des index FULLTEXT dans les colonnes de la base de données à partir desquelles vous souhaitez effectuer une recherche. J’ai pensé à vous en créant la class PHP « alterTableFullText » pour générer les index sans passer par PhpMyAdmin ou autre SGBD… C’est pour cette raison que je n’ai pas mis FULLTEXT par défaut…
Le moteur de recherche PHP dispose aussi d’un objet spécifique à l’affichage des résultats qui offre également plein de possibilités :
- Appel d’une fonction d’affichage (« callback ») obligatoire
- Choix des colonnes de sélections en SQL plutôt que de tout prendre (* -> par défaut)
- Ajout ou non d’une clause LIMIT en SQL (tableau avec trois arguments)
- Ajout ou non d’une clause ORDER BY en SQL (tableau avec trois arguments)
- Utilisation ou non de mon algorithme de pertinence (simple) personnalisé (tableau à 4 arguments), sachant qu’il peut être couplé avec l’ORDER BY précédent si besoin
- Personnalisation ou non de la fin de requête (ajout de vos propres clauses après le WHERE telles que AND…, AS…, ORDER BY…, LIMIT…)
Comme tout ne serait pas assez complet ainsi, j’ai également créé une class PHP pour afficher une pagination. Tout est à paramétrer pour que le résultat soit parfait mais cela fonctionne bien, la fonction a été reprise sur le site seebz.net mais je l’ai totalement réadaptée au moteur de recherche et je lui ai ajouté des options pour que chacun puisse faire ce qu’il souhaite…
Enfin, j’ai créé une class PHP à part pour permettre de surligner les mots et expressions recherchées si désiré, de manière exacte (juste la chaîne précise) ou complète (la chaîne + les lettres environnantes si elles existent). Dans tous mes tests, cela fonctionne parfaitement mais je reste prudent car la méthode est basée sur des expressions régulières (REGEX) et nous ne sommes jamais sûr de rien lorsqu’elles sont complexes… La class doit être utilisée au sein de la fonction « callback » d’affichage des résultats !
N.B. : Toutes les class et méthodes importantes sont détaillées à la fin et entièrement commentées dans les codes sources à télécharger
Télécharger “BDD PHP 5.5 et PHP 7”BDD-PHP5.5.class-inc.zip – Téléchargé 6612 fois – 426,00 o
Télécharger “Liste et tableau des “stop words””stopwords.zip – Téléchargé 8207 fois – 2,32 Ko
Usage complet du moteur de recherche (codes à l’appui)
Tout est commenté dans le code source, mais pour plus de simplicité, je vous fournis quelques explications afin que vous puissiez tirer profit du moteur de recherche sans trop de complication. Je vais me baser sur un exemple totalement fictif pour vous montrer quoi faire...
1/ Inclusion des fichiers (Connexion BDD + Class du moteur de recherche + liste des « stop words »)
<?php include_once("CHEMIN-URL/BDD.class-inc.php"); // Fichier de connexion à la base de données (optionnel) include_once("CHEMIN-URL/stopwords.php"); // Fichier contenant un tableau $stopwords pour les "stop words" (optionnel) include_once("CHEMIN-URL/moteur-php5.5.class-inc.php"); // Class du moteur de recherche PHP et autres ?>
2/ Connexion BDD (avec ma méthode personnalisée, cela fonctionne avec PDO, etc.)
<?php // Gestion de la connexion SQL (avec ma méthode personnalisée) --> Pour PHP 5.5 et PHP 7 $base = "mabase"; // Nom de la base de données (BDD) $serveur = "localhost"; // Nom du serveur de BDD $login = "root"; // Login de la BDD ("root" pour Wamp et Xampp) $passe = ""; // Mot de passe de la BDD (vide pour Wamp et Xamp, "root" pour Mamp) $link = new mysqli($serveur, $login, $passe, $base); // Connexion // En cas d'erreur if (mysqli_connect_error()) { die('Connexion impossible à Mysql ('.mysqli_connect_errno().') : '.mysqli_connect_error()); } ?>
3/ Exemple de formulaire de recherche complet (avec "hidden" pour la pagination et le LIMIT)
<form id="searchForm" name="moteurSubmit" method="GET" action=""> <label for="moteur">Rechercher :</label> <input type="text" value="<?php if(isset($_GET['moteur'])) { echo htmlspecialchars($_GET['moteur']); } ?>" name="moteur" id="moteur" /> <input type="submit" value="Envoyer" /> </form>
4/ En cas de soumission du formulaire de recherche (if...)
<?php // Placez de préférence cette partie au-dessus du DOCTYPE sous les inclusions et la connexion BDD if(isset($_GET) && !empty($_GET['moteur'])) { /* // Appel de la class moteurRecherche (9 pour PHP 5.5 et PHP 7, 8 pour les versions inférieures) 0. VERSION PHP inférieur à 5.5 : premier paramètre en moins ($link dans l'exemple commenté ci-dessous) 1. requête de recherche ($_GET['moteur'] avec stripslashes() par sécurité) -> OBLIGATOIRE 2. table de la base de données dans laquelle chercher -> OBLIGATOIRE 3. mode de recherche (like, regexp ou fulltext) -> REGEXP par défaut 4. tableau contenant des stop words ($stopwords ici) 5. nombre entier pour exclure les mots plus petits ou égaux que cette taille (si vide, aucune exclusion -> par défaut) 6. encodage souhaité (utf8, utf-8, iso-8859-1, latin1...) -> UTF-8 par défaut 7. booléen (true/false) pour choisir entre la recherche exacte (tous les mots) ou non (un mot juste ou plusieurs) -> TRUE par défaut 8. booléen (true/false) pour faire des recherches sans accent (si les contenus sont entrés sans accent dans la BDD) -> FALSE par défaut */ $moteur = new moteurRecherche($link, stripslashes($_GET['moteur']), 'TABLE_SQL', 'regexp', $stopwords); // PHP 4 et PHP 5.5- : $moteur = new moteurRecherche(stripslashes($_GET['moteur']), 'TABLE_SQL', 'regexp', $stopwords); // tableau des colonnes dans lesquelles effectuer une recherche $colonnesWhere = array('titre', 'description'); // Lancement de la class moteurRequetes() pour générer les requêtes de recherche $moteur->moteurRequetes($colonnesWhere); } ?>
5/ Affichage des résultats (dans la même page ici !)
- Fonction Callback d’affichage des résultats de recherche (appelée affichage() ici)
- Lancement de l’objet faisant appel à la fonction Callback (avec plusieurs paramètres possibles)
- Appel à l’objet de pagination (entièrement paramétrable)
Ce bloc doit être intégré à l'endroit où vous souhaitez afficher les résultats dans la page...
<?php if(isset($moteur)) { // Affichage de la requête avec $moteur->requete echo '<h3>Résultats de la recherche : <em>'.$moteur->requete.'</em></h3>'; /* Fonction "callback" pour l'affichage des résultats (3 arguments obligatoires) 1. Requête de recherche (variable au choix...) 2. Nombre total de résultats (variable au choix...) 3. Liste des mots et expressions de la requête (variable au choix...) */ function affichage($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 // Afficher le nombre de résultats $affichageResultats = new affichageResultats(); echo $affichageResultats->nbResultats(true); // Comptage dynamique du nombre de résultats if(isset($_GET['p'])) { $nb = $nb + (10 * ($_GET['p'] - 1)); } while($key = mysqli_fetch_assoc($link, $requete)) { // 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 = "<br/>".$key['titre']."<br/>"; $texte .= $key['contenu']."<br/>"; /* Objet pour surligner les mots recherchés si nécessaire => 3 arguments (les deux premiers sont obligatoires) 1. variable contenant les mots ($mots ici) 2. variable contenant le texte ($texte ici) 3. "exact" (la chaîne précise mise en gras) ou "total" (la chaîne précise + environnante en gras) -> "exact" par défaut */ $surlignage = new surlignageMot($mots, $texte); echo $surlignage->contenu; // Affichage du contenu après surlignage // N.B. : echo $texte; si vous ne voulez pas de surlignage... } // Fin de la boucle while } } // 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 class moteurAffichage 1. appel à la fonction callback d'affichage entre guillemets (obligatoire) 2. colonnes à sélectionner dans la base (toutes s'il est laissé "vide") 3. LIMIT en SQL -> tableau avec 4 valeurs : true/false, numDépart ($_GET['page'] ici pour la pagination dynamique), nbResultatsParPage (4 ici), true/false (true pour pagination classique, false pour autre) -> Par défaut array(false, 0, 10, false) 4. ORDER BY : tableau avec 3 valeurs : true/false, colonne d'ordre, ASC/DESC -> par défaut array(false, 'id', 'DESC') 5. ORDER BY avec algorithme de pertinence -> tableau avec 4 valeurs : true/false, colonne de classement (inédite !), ASC/DESC, colonne de l'ID -> Par défaut array(false,'algo','DESC','id') N.B. : la fonction ajoute la colonne de classement si elle n'existe pas ! 6. Fin de requête personnalisable (après le WHERE) : avec ORDER BY, LIMIT, AND */ $moteur->moteurAffichage('affichage', '', array(true, $page, $limit, true)); /* Lancement de la méthode de pagination 1. OBLIGATOIRE : variable de récupération de la page ($page pour $_GET['p'] ici) 2. OBLIGATOIRE : nom du paramètre GET pour la page (situé dans l'input "hidden") -> 'page' ('p' dans l'exemple) 3. nombre de pages affichées autour de la page courante -> 2 par défaut 4. nombre de liens (premières et dernières pages) à afficher (0 pour annuler l'option) -> 0 par défaut 5. booléen (true/false) pour afficher ou non "page suivante" et "page précédente" -> true par défaut 6. booléen (true/false) pour afficher ou non "première page" et "dernière page" -> true par défaut 7. tableau qui contient les éléments de mise en forme (8 args) => textePrécédent, texteSuivant, textePremierePage, texteDernierePage, classPrecSuiv, classPage, classBloc, classInactif -> défaut : array('« Précédent', 'Suivant »', 'Première page', 'Dernière page', 'precsuiv', 'current', 'pagination', 'inactif') 8. tableau qui contient les séparateur (5 args) => pointSuspension, sepPremiereDernierePage, $sepNumPage, sepSuivPrec, sepDebutFin -> Défaut : array('…', ' ', ' ', ' ', ' ') */ $moteur->moteurPagination($page, 'p'); } ?>
Vous devriez avoir un moteur de recherche fonctionnel avec tous ces codes, je vais tout de même terminer cet article en détaillant un peu plus quelques fonctionnalités du moteur et en proposant des pistes d'évolutions. Pour ceux qui sont déjà satisfaits, merci pour votre lecture.
N.B. : depuis la version 1.5 du moteur, il est possible d'afficher le nombre de résultats total et le nombre par page à l'aide de la class fille en PHP Objet appelée affichageResultats(). Il suffit d'appeler la méthode nbResultats() de la class au sein de la fonction Callback d'affichage (comme dans l'exemple ci-dessus).
Algorithme de pertinence (simple)
L'algorithme de pertinence que j'ai créé est tout simple, mais il donne des idées d'évolution pour la suite. Pour être tout à fait franc, c'est l'algorithmie qui m'a donné envie de développer ce moteur de recherche en PHP objet, je voulais trouver un moyen de classer les résultats différemment d'un ORDER BY classique en SQL.
L'algorithme est activé lorsque le 5e argument de la class moteurAffichage() est présent. Il s'agit d'un tableau contenant 4 paramètres obligatoires :
- booléen (true/false) pour activer ou non l'algorithme
- nom d'une colonne SQL pour accueillir les valeurs de classement ('algo' par défaut). Cette colonne est créée dans la table si elle n'existe pas...
- type de classement : ASC (ascendant) ou DESC (descendant)
- intitulé de la colonne d'identifiant ('id' en général, mais attention à la casse ou à un nom différent...)
La pertinence se fait ici de manière simple. Le moteur analyse la requête de l'utilisateur puis compte dans les contenus le nombre d'occurrences des mots et expressions. Une fois les comptes réalisés, l'option met à jour la colonne de la base de données ('algo' par défaut) puis classe les résultats en fonction de la valeur attribuée. En d'autres termes, il s'agit d'une partie du tri par pertinence très connu des référenceurs.
J'ai ajouté deux possibilités en cas d'activation de l'algorithme :
- le classement se fait uniquement par pertinence
- le classement se fait par pertinence mais aussi avec un autre ORDER BY si le 4e argument de la class (tableau) est activé (true). Dans ce cas, cela permet par exemple de classer par pertinence puis par ID ou autre...
Détail de la class « alterTableFullText »
Si vous voulez utiliser le moteur FULLTEXT, il est impératif que les colonnes dans lesquelles nous voulons chercher ait des index FULLTEXT dans la base de données. Comme tout le monde n'est pas "expert" en SQL, j'ai créé une class spécifique pour ajouter un index FULLTEXT aux colonnes. Il faut aussi que la table utilise "l'engine" MyISAM et non INNOdb comme cela est parfois le cas dans le monde du web, la class convertit en MyISAM automatiquement...
En réalité, l'index FULLTEXT est ajouté uniquement s'il n'existe pas déjà pour la (ou les) colonne(s) sélectionnées. La fonction peut donc être laissée dans le pire des cas, bien que l'idéal soit de la lancer la première fois pour ajouter les index FULLTEXT, puis ensuite de la retirer car elle deviendrait dès lors inutile (cela évite une surcharge de traitement pour rien).
L'idéal est de la lancer juste avant la class moteurRequetes() et juste après le tableau des colonnes de recherche (appelées $colonneWhere dans l'exemple précédent).
$colonnesWhere = array('titre', 'contenu'); $alterTable = new alterTableFullText($link, 'mabase', 'matable', $colonnesWhere); // PHP 4 et 5.5- : $alterTable = new alterTableFullText('mabase', 'matable', $colonnesWhere); $moteur->moteurRequetes($colonnesWhere);
Dans notre cas, nous avons utiliser un tableau contenu les colonnes mais nous pouvons aussi utiliser une chaîne de caractères avec les noms de colonne séparées par des virgules. Ainsi, nous aurions très bien pu avoir $colonneWhere = "titre, contenu"; mais malheureusement la variable ne fonctionnerait plus pour la class moteurRequetes ici (car elle attend un tableau).
Détail de la class « surlignageMot »
La class surlignageMot permet de mettre en gras les mots rechercher. Elle prend cinq paramètres (seuls les deux premiers sont obligatoires). Pour ceux qui avaient déjà téléchargé le moteur de recherche, sachez que l'ancienne version de la class ne prenait que trois paramètres et manquait de précision... Voici donc les cinq arguments de la class surlignageMot :
- Les chaînes de caractère (mots et/ou expressions) à mettre en gras
- Le texte dans lequel mettre en gras les chaînes de caractères
- la méthode de mise en gras (voir en dessous pour les précisions)
- le degré d'exactitude de la recherche (comme pour le moteur de recherche) : "true" pour la recherche des mots complets correspondant à la requête, "false" pour trouver les mots contenant les chaînes de caractères de la requête (exemple : les mots comme "mode", "demain" ou "céder" contiennent tous la chaîne "de")
- le type de recherche (le même que pour la class moteurRecherche) : "FULLTEXT", "REGEXP" ou "LIKE"
N.B. : les deux derniers paramètres doivent reprendre ceux fixés dans la class moteurRecherche, ils ont été ajoutés pour obtenir de bien meilleurs résultats en matière de mise en gras des mots clés recherchés. Ainsi, la mise en gras est idéale selon le type de recherche, l'exactitude souhaité et la méthode choisie (voir ci-dessous).
Volontairement, j'ai créé deux méthodes de mise en gras :
- "exact" (par défaut) permet de mettre en gras la chaîne de caractère précise sans tenir compte de ce qui l'entoure (par exemple, si nous cherchons la chaîne "mot" avec la méthode LIKE, le moteur afficherait "moteur de recherche", "motoculteur"... selon les mots trouvés)
- "total" (ou "complet") permet de mettre en gras la chaîne précise et son environnement proche (pour reprendre notre exemple, la recherche de la chaîne "mot" avec la méthode LIKE afficherait "moteur de recherche" et "motoculteur"). La différence ici est de mettre en gras les mots qui contiennent la chaîne recherchée (utile pour la méthode LIKE notamment).
// Surlignage exact (par défaut) $surlignage = new surlignageMot($mots, $texte, "exact", true, "REGEXP"); // Surlignage total ("total" ou "complet") --> surtout utile pour la recherche LIKE (ou avec false et "REGEXP") ! $surlignage = new surlignageMot($mots, $texte, "total", true, "LIKE"); // N.B. : attention à la casse pour le type de recherche choisie, cela peut causer des erreurs !!
Détail de l’objet « moteurPagination »
L'objet moteurPagination() permet d'utiliser un système de pagination sans avoir à tout coder à la main et à adapter. La méthode a totalement été mise en place pour fonctionner avec le moteur de recherche PHP sans problème. Elle ne fonctionne qu'avec la méthode GET et utilise 7 arguments :
- Le nom du paramètre GET de la page ('page' par défaut) qui a été intégré dans l'input type "hidden" du formulaire.
- Le nombre de pages affichées autour de la page courante en surbrillance (2 par défaut). Par exemple, si nous voulons afficher "... 1 2 3 ...", il faut mettre 1 en valeur, tandis que 2 permet de faire quelque chose comme "... 3 4 5 6 7 ...".
- L'affichage de liens pour les premières et dernières pages (0 pour aucun lien, par défaut). Par exemple, une valeur de 1 permet de faire quelque chose du type "1 ... 4 5 6 7 8 ... 17" et une valeur de 2 "1 2 ... 4 5 6 7 8 ... 16 17".
- Un booléen (true/false) pour afficher ou non "page suivante" et "page précédente"
- Un booléen (true/false) pour afficher ou non "première page" et "dernière page"
- Un tableau qui contient les éléments de mise en forme (8 args) dans l'ordre : texte "Précédent", texte "Suivant", texte "Première page", texte "Dernière page", class CSS des liens, class CSS du lien actif (page courante), class CSS du bloc de pagination, class CSS quand le lien est inactif.
- Un tableau qui contient les séparateur (5 args) dans l'ordre : points de suspension, après "première page" et avant "dernière page", entre les numéros de page, après "précédent" et avant "suivant", après lien(s) de début et avant lien(s) de fin. Hormis les points de suspension qui ont un intérêt, les autres séparateurs ont peu de rôle si vous utilisez du style CSS, c'est pourquoi ils sont laissés vides par défaut.
Voici quelques exemples d'usage de l'objet moteurPagination() selon des réglages différents :
N.B. : la dernière page n'affiche pas uniquement les dernières résultats, elle "remplie" l'espace avec des résultats déjà cités dans la page précédente si nécessaire (en d'autres termes, si on paramètre à 10 résultats par page, la dernière page affichera 10 résultats, quitte à en reprendre quelques-uns de la page précédente !).
Voici le style CSS utilisé dans les exemples :
.pagination {margin:1.5em 0 0 0; line-height:2em;} .pagination a, .pagination span {padding:0.2em 0.5em 0.3em;} .pagination a {border:1px solid #108c98; border-radius:5px; color:#108c98; text-decoration:none;} .pagination a:hover {border:1px solid #108c98; background:#108c98; color:#fff;} .pagination .precsuiv {font-weight:bold;} .pagination span.inactif {border:1px solid #ccc; border-radius:5px; color:#ccc;} .pagination span.current {border:1px solid #108c98; background:#108c98; border-radius:5px; color:#ffffff; font-weight:bold;}
Détail de la class « autoCompletion »
La version 2.0 de la class du moteur de recherche PHP apporte une nouvelle fonctionnalité intéressante. Outre les quelques nettoyages du code, cette version a mis au jour la possibilité d'ajouter un système d’auto-complétion (comme sur Google). Voici les possibilités autorisées :
- Possibilité de créer une table d'index inversé pour le moteur de recherche (l'index inversé contient les mots clés et les expressions qui serviront à l'auto-complétion).
- Possibilité d'ajouter automatiquement les mots recherchés dans l'index inversé au fur et à mesure des recherches des internautes. En effet, sauf si vous avez déjà créé une table rempli de mots et expressions, il faudra opter pour cette option afin que la table se génère de manière autonome et permette d'afficher des résultats lors de l'auto-complétion.
- Possibilité de suggérer des mots seulement pour le premier mot tapé ($multiple = false) ou pour tous les mots ($multiple = true).
- Possibilité de modifier le nombre de résultats affichés dans la zone d'autocomplétion.
- Possibilité de choisir le type d'autocomplétion dans le moteur de recherche ($type = 0 pour démarrer en début de mot ou $type = 1 pour suggérer des mots contenant les lettres tapées).
- Possibilité d'utiliser un système d'autofocus qui surligne directement la première proposition (déconseillé toutefois...).
- Possibilité de choisir à partir de combien de caractères les mots seront ajoutés dans l'index inversé (si l'index est généré automatiquement !)
Voici mon conseil pour ceux qui n'auraient pas créé une table d'index inversé :
- Créez dans un premier temps l'index inversé du moteur de recherche PHP avec l'option $create = true; puis passez l'option sur "false" ensuite.
- Optez pour la génération automatique de l'index (même si vous avez déjà rempli des mots dans une table à vous) en utilisant la méthode PHP autoComplete().
- Mettez le paramètre $minLength à 2 au moins pour éviter que des mots courts soient ajoutés dans l'index inversé (peu de pertinence dans ce cas !).
N.B. : la méthode d'ajout automatique des mots dans le système d'auto-complétion ne rajoute pas tous les mots, elle vérifie d'abord s'il existe déjà dans la table de l'index inversé et si ce n'est pas le cas, le terme est rajouté (de même pour les expressions entre guillemets).
1/ Ajout des scripts jquery.js et jquery.autocomplete.js dans le HEAD
<script src="CHEMIN/jquery.js"></script> <script src="CHEMIN/jquery.autocomplete.js"></script>
2/ Ajout du style CSS (avec le fichier jquery.autocomplete.css ou non)
<link rel="stylesheet" href="CHEMIN/jquery.autocomplete.css" />
3/ Mise en place de l'auto-complétion pour le moteur de recherche PHP
Sachez qu'il est nécessaire d'ajouter les identifiants de connexion également dans le fichier autocompletion.php (ou de refaire une connexion à la base avec votre propre système si vous préférez).
// PLACER CE MORCEAU DE CODE APRES $connect = new ConnexionBDD($base, $serveur, $login, $passe); ABSOLUMENT ! // 4 paramètres obligatoires sur 10 disponibles // N.B. : tous les paramètres sont décrits dans les fichiers de la class $autocompletion = new autoCompletion($link, "CHEMIN/autocompletion.php", "#moteur", "NOM_DE_LA_TABLE", "NOM_DE_LA_COLONNE"); // Sans "$link" pour PHP 4 et PHP 5.5-
4/ Utilisation (optionnelle) de l'outil d'ajout automatique des nouveaux mots clés et des expressions
if(isset($moteur)) { /* ... CODE ... */ // Juste au dessus : $moteur->moteurAffichage('affichage', '', array(true, $page, 10, true)); // Juste au dessus : $moteur->moteurPagination($page, 'p'); $autocompletion->autoComplete(stripslashes($_GET['moteur'])); }
Débogages et évolutions possibles
Il ne faut pas se le cacher, un tel moteur de recherche fourmille de petites options qui paraissent invisibles pour ceux qui ne plongeraient pas dans le code. En réalité, il faut penser à plein de points importants pour que l’ensemble de la machine soit fonctionnel et ne soit pas (trop) bogué.
Globalement, j’ai corrigé tous les principaux bugs que j’ai pu rencontrer :
- Problème d’encodage, que ce soit en UTF-8 ou ISO-8859-1, car nous devons bien construire nos base de données (en MySQL notamment) et parfois traiter les informations dans la fonction d’affichage avec la fonction utf8_encode().
- Problème avec la clause LIMIT pour la pagination car cette dernière part à la page 1 alors que le point de départ normal est 0. Etant donné que mon système permet d’utiliser ou non la pagination, je devais trouver un remède miracle et c’est en place.
- Problème des index FULLTEXT pour les colonnes sélectionnées pour la recherche résolu grâce à la class « alterTableFullText ».
- Gestion des échappements de caractères spéciaux (guillemets, apostrophes, slashs…) et de quelques signes (« + », « * »).
- Problème des mots composés. En effet, un mot comme « faux-plafond » correspond à deux mots pour un moteur classique, soit « faux » et « plafond ». J’ai fait en sorte que les mots composés soient traités comme une expression exacte.
- Possibilité d’ajouter les colonnes SELECT de son choix plutôt que de faire systématiquement un « * » qui abaisse parfois les performances.
- Débogage des problèmes d’attributs HTML lorsqu’un mot cherché est présent dans la chaîne et que le surlignage est utilisé. Par exemple, si je cherche le mot « google » et que ce dernier est contenu dans une URL (href ou src), il se met en gras (<strong>…</strong>) et bloque donc le fonctionnement. Désormais, la mise en gras est supprimée dans plusieurs attributs HTML pour éviter tout risque…
- Quelques bugs avec la mise en gras lorsqu'un mot était accolé à une virgule, à un point ou à une parenthèse par exemple.
- Résolution de problèmes avec la recherche PHP en REGEXP (certains signes bloquaient les expressions régulières).
- Débogage des recherches en lettres capitales accentuées.
Toutefois, quelques points peuvent encore être plus poussés, je vous laisse coder un peu même si je mettrais peut-être (sûrement) à jour les fichiers présents dans cet article lorsque j’aurais des idées :
- Amélioration possible de la recherche FULLTEXT à cause du paramètre ft_min_word_len de my.ini (MySQL) qui ignore les mots de 1 à 3 caractères automatiquement (paramétrable sur un serveur dédié mais pas sur un hébergement mutualisé...).
- Algorithme de pertinence à faire évoluer pour plus de précision dans les résultats et dans l’ORDER BY associé…
- Gestion des requêtes qui peut être améliorée. Actuellement, cela prend en compte les expressions entre guillemets et les mots seuls, je n’ai pas envisagé de gérer les « + » et les « - » ni les « AND » et « OR » par exemple, mais cela pourrait se faire (le problème étant que ce système ne fonctionne qu’avec la recherche FULLTEXT et pas les autres…).
- Gestion de la sécurité, bien que les données doivent être traitées en amont pour plus de confort. Le minimum a été fait pour protéger l'ensemble, mais ce point peut être amélioré…
La capture ci-dessous montre un problème (désormais résolu) qui avait lieu avec l'ancienne version de la class du moteur de recherche lorsque nous cherchions des chaînes courtes telles que "le", "de" (...). En fait, il s'agissait essentiellement d'un problème de surbrillance, ce dernier est résolu grâce aux nouvelles expressions régulières en PHP et à l'amélioration de la fonction de surlignage.
Dans le fond, le moteur de recherche PHP fonctionnait très bien mais l'affichage n'était pas transcendant... Voilà un premier problème résolu... :D