SQLite et la recherche en texte intégral

| Aucun Commentaire | Aucun Trackback

Dans un des derniers numéro de GNU/Linux Magazine France, je suis tombé sur un article qui a suscité mon intérêt : SQLite : index et recherche rapide de texte.

Les SGBD(R) offrent quasiment tous leur fonctionnalité de recherche en texte intégral. Ainsi, les habituels clients du marché open source :

Et donc, SQLite offre également cette fonctionnalité, ce qui est très intéressant car SQLite étant conçu pour être embarqué dans d'autres applications, cela ouvre des perspectives non négligeables.

L'article illustre cette fonctionnalité en créant un moteur de recherche pour la documentation disponible dans les répertoires /usr/share/doc de votre distribution Linux. L'article utilise Python comme langage, et je me propose de voir ce que Perl nous offre en la matière. Dans un premier temps, je me contenterai de produire un équivalent de ce qui a été présenté dans l'article, mais j'avoue que j'ai déjà envie de modifier un peu l'outil de manière à le transformer en quelque chose d'utilisable dans ma vie quotidienne.

Compilation de DBD::SQLite

Une des premières choses à faire pour pouvoir utiliser SQLite dans vos scripts Perl est bien entendu d'installer les modules adéquats s'ils ne sont pas déjà présent sur votre système. Le module Perl concerné ici est DBD::SQLite. Evidemment, il s'agit de rajouter un pilote pour DBI afin de permettre à ce dernier de se connecter à des bases de données SQLite. La bonne nouvelle est qu'il n'est pas nécessaire d'avoir SQLite installé sur votre système puisque DBD::SQLite embarque SQLite dans le module. Il vous faut néanmoins un compilateur pour installer le module via CPAN. Néanmoins, dans mon cas, je veux modifier la compilation du module de manière à permettre à SQLite de supporter les opérateurs AND et NOT. Voici comment faire :

edgeoya:./~ $ sudo cpan

cpan shell -- CPAN exploration and modules installation (v1.9402)
Enter 'h' for help.

cpan[1]> get DBD::SQLite
[...]
cpan[2]> look DBD::SQLite

Ce qui vous amènera dans le répertoire d'installation du module. Il vous faudra alors modifier le fichier Makefile.PL, et ajouter une option de compilation. Voici la sortie d'un diff illustrant cette modification :

diff --git a/Makefile.PL b/Makefile.PL
index 8a5be1c..a14b958 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -172,6 +172,7 @@ if ( $sqlite_inc ) {
 my @CC_DEFINE = (
        '-DSQLITE_CORE',
        '-DSQLITE_ENABLE_FTS3',
+        '-DSQLITE_ENABLE_FTS3_PARENTHESIS',
        '-DSQLITE_ENABLE_COLUMN_METADATA',
        '-DNDEBUG=1',
        "-DSQLITE_PTR_SZ=$Config{ptrsize}"

L'option SQLITE_ENABLE_FTS3_PARENTHESIS permet d'utiliser les opérateurs AND et NOT dans les requêtes FTS3, ce qui est plutôt utile, non ? Il permet également d'utiliser les parenthèses pour regrouper les sous-requêtes.

Une fois que la modification est faite, vous pouvez lancer la séquence traditionnelle de l'installation manuelle d'un module :

$ perl Makefile.PL
$ make
$ make test
$ sudo make install

Le script d'indexation

Voici le script d'indexation de la documentation :

#!/usr/bin/env perl

use strict;
use warnings;
use DBI;
use File::Find::Rule;
use Compress::Zlib;
use File::Slurp;

my $db = './doc.db';
my $docDir = '/usr/share/doc';

unlink $db if -e $db;
my $dbh = DBI->connect("dbi:SQLite:dbname=$db", '', '');
$dbh->do('create table docs(package text, file text)');
$dbh->do('create virtual table docs_text using fts3(contents)');

my $sth_docs = $dbh->prepare('insert into docs values (?, ?)');
my $sth_contents = $dbh->prepare('insert into docs_text (rowid, contents) values (?, ?)');

foreach my $file (File::Find::Rule->file()->in($docDir)) {
    my $contents = '';
    if ($file =~ /\.gz$/) {
        my $gz = gzopen( $file, 'rb' );
        my $buffer = '';
        while ($gz->gzread( $buffer ) > 0) {
            $contents .= $buffer;
        }
        $gz->gzclose();
    } else {
        $contents = read_file( $file );
    }

    $file =~ m|/usr/share/doc/([^/]*)|g;
    my $package = $1;

    $sth_docs->execute( $package, $file );
    my $id = $dbh->last_insert_id(undef, undef, 'docs', 'rowid');
    $sth_contents->execute( $id, $contents );
    $dbh->commit;
}

Notez que j'ai suivi comme fil conducteur le script en Python de l'article, je ne me suis pas amusé à perlifier le code.

L'instruction qui nous intéresse est la suivante :

$dbh->do('create virtual table docs_text using fts3(contents)');

Cela permet de créer une table virtuelle qui indexera le contenu d'un attribut de la table.

Je renvois à l'article pour plus d'explication.

Conclusion

Bref, voilà, cela m'aura permis de découvrir une fonctionnalité de SQLite qui me paraît assez intéressante. Nul doute que je parviendrai à l'utiliser dans un futur proche.

Mais avant de me lancer dans mes propres développements, il faut encore que je programme le second outil de l'article, à savoir un script permettant d'interroger la base de données ainsi créée. Ben oui, sinon, ce n'est pas fort intéressant !

Mise à jour du 1er août 2009

Je me suis trompé dans ma retranscription du script de l'article. En effet, dans la table docs, l'auteur de l'article gère le nom du fichier et le nom du paquet contenant ce fichier. Ce que je faisais, de manière assez idiote, était de stocker le nom du fichier, et le contenu de ce fichier. Donc, je me retrouvais avec une base de données énorme ! 1.7Go ce n'est pas rien !

J'ai donc mis à jour le script pour récupérer le nom du package et pour le stocker dans la base de données. Je le fais au moyen d'une regex. Le code ci-dessus a été corrigé, et voici les lignes ajouées ou modifiées :

    $file =~ m|/usr/share/doc/([^/]*)|g;
    my $package = $1;

    $sth_docs->execute( $package, $file );
Je ferai plus attention la prochaine fois ;-)

Aucun Trackback

URL de Trackback : http://blog.bjornoya.be/mt-tb.cgi/3

Laisser un commentaire

À propos de cette note

Cette page contient une unique note de manu publiée le 23 juillet 2009 19h19.

Finally... est la note précédente de ce blog.

Mandater la récolte des piécettes est la note suivante de ce blog.

Retrouvez le contenu récent sur l'index principal ou allez dans les archives pour retrouver tout le contenu.

Pages

Powered by Movable Type 4.261