Moteur de recherche pour des fichiers textes

A plusieurs reprises, j’ai eu à chercher comment effectuer un moteur de recherche avec interface web pour un ensemble de documents, plusieurs possibilités sont disponibles qui vont de l’utilisation d’un fournisseur externe comme google à la conception manuelle.

L’utilisation de Google est tentante, elle évite toute prise de tête, mais nécessite un enregistrement auprès de ce dernier pour avoir la prise en compte de l’ensemble des documents sans parler du côté privé.

L’utilisation d’outils faits pour ça tel que Beagle ou Tracker génère des dépendances relativement lourdes et pour le type de document qui m’intéresse (des fichiers textes), c’est surdimensionné. Je peux utiliser des programmes tel que grep, mais pour une interface web, l’appel à des programmes externes n’est pas forcément recommandé.

La conception de A à Z supposent de connaître les rouages de l’indexation, on peut imaginer un archivage dans une base de données et l’attaquer à coup de LIKE, mais trop lourd, et pas assez rapide.

La solution qui m’a paru la plus simple à mettre en œuvre est celle de l’article « SQLite: index et recherche rapide de texte » d’Alexandre Courbot de la revue GNU Linux Magazine (au passage, c’est une très bonne revue).

Préparation

Le moteur se basera sur un module très pratique de SQLite: Full Text Search (FTS).

Il est inclus par défaut dans le paquet fourni par Archlinux, si ce n’est pas le cas pour votre distribution, vous pouvez voir la manière de compiler SQLite avec FTS.

Pour l’exemple, je vais utiliser les logs du canal #archlinux-fr sur IRC, on peut utiliser le langage qu’on souhaite pour créer et remplir la base, celui de l’article étant en Python, on va changer, créons le fichier builddb.php :

exec("create table docs (file text)");
$dbh->exec("create virtual table docs_text using fts3(contents)");
$sth_1 = $dbh->prepare ('insert into docs values (?)');
$sth_2 = $dbh->prepare ('insert into docs_text (rowid, contents)
 values (?, ?)');

$handle=opendir($rep);
while ($file = readdir($handle))
{
 if (is_file($rep . $file))
 {
  $sth_1->execute (array ($file));
  $sth_2->execute (array ($dbh->lastInsertId(),
   file_get_contents ($rep . $file)));
 }
}
?>

Puis depuis un serveur web ou tout simplement en le lançant:

php builddb.php

Utilisation

Un test tout simple:

sqlite3 log.db
select file from docs d1 join docs_text d2 on d1.rowid = d2.rowid where contents match 'sqlite';
#archlinux-fr.2008-12-13.log
#archlinux-fr.2009-03-24.log
#archlinux-fr.2009-02-28.log
#archlinux-fr.2009-01-03.log
#archlinux-fr.2009-04-15.log
#archlinux-fr.2009-02-01.log

Ou si on choisit de sortir un extrait des occurrences trouvées:

select snippet (docs_text) from docs_text where contents match 'sqlite';
... ce truc plante, parce qu'il utilise sqlite et cette base ne gère pas les thread ...
... j'imagine
[09:07:36]   sqlite te permet de gerer ta db sql dans un ...
... quoi comme bonne platforme blog qui gèrent sqlite ?
[16:23:01]   wordpress non? ...
... thread
[13:51:24]   mais j'utilise sqlite et le module python de sqlite ne permet pas ...
... Par contre, c'est quoi ces dépendances à SQlite ?
[20:42:37]   Elle servent à ...
... Forum Ubuntu-fr.org / F-spot SIGSEGV bug (sqlite, mono...): http://forum.ubuntu-fr.org/ ...

(hmmm, l’extrait n’est pas flatteur pour SQLite :))

Il ne reste plus qu’à mettre en forme pour une mise à jour régulière de la base ainsi qu’une interface web.

Interface

Un exemple d’interface:



Recherche log


prepare ("select file, snippet (docs_text) from docs d1 join docs_text d2 on d1.rowid = d2.rowid where contents match ? order by 1 desc"); $sth->execute (array ($_GET['q'])); while (($row = $sth->fetch ()) !== false) { echo '' . $row[0] . " \n"; echo str_replace ("\n", " ", $row[1]) . " \n"; } } ?>

Bon, ben il me reste plus qu’à mettre tout ça sur les Logs #Archlinux-fr

Commentaires (6)