Première rédaction de cet article le 16 mars 2009
Le tout nouveau moteur de recherche de ce blog repose sur plusieurs techniques que j'apprécie, notamment WSGI, TAL et la recherche texte de PostgreSQL.
Les données sont à l'origine dans des fichiers
XML (voir l'article « Mise en œuvre de ce
blog »). Ces données sont, chaque nuit, lues par un programme
Python, blog2db.py
, qui
traduit le XML en texte et met ledit texte dans une base de données
PostgreSQL. Cette base est configurée (script
de création en create.sql
) pour
faire de la recherche plein
texte. Je peux y accéder depuis la ligne de commande
Unix ainsi :
% search-blog médiane filename | title -----------------------------------+--------------------------------------------------- mediane-et-moyenne.entry_xml | Médiane et moyenne 2681.rfc_xml | A Round-trip Delay Metric for IPPM le-plus-rapide-dns.entry_xml | Quel est le plus rapide serveur DNS d'un groupe ? echoping-6-fini.entry_xml | Version 6 d'echoping ...
où le script search-blog
fait simplement une
requête SQL
psql -c "SELECT filename,trim(replace(substr(title,1,63),E'\n','')) \ AS title \ FROM Blog.search('$query') LIMIT 13;" \ blog
Et pour le Web ?
WSGI est utilisé comme plate-forme de
développement et d'exécution du script Python qui anime l'interface Web du moteur de recherche. Le langage
de gabarit utilisé est TAL (alors que le reste du blog
est en Cheetah, je sais, ce n'est pas très cohérent). Le gabarit est nommé search.xhtml
et le programme WSGI est search.py
.
Le programme se connecte à la base de données, la connexion reste ouverte tant que le démon WSGI tourne. On ne se connecte donc pas à chaque requête. Mais la base de donnés posait un autre problème. Une fois qu'un a configuré PostgreSQL pour faire de la recherche plein texte, on a le choix entre deux syntaxes pour exprimer les requêtes :
to_tsquery()
.plainto_tsquery()
.
Aucune des deux ne me convenait tout à fait. Je voulais une syntaxe sans
opérateurs mais où l'opérateur implicite soit OU et pas ET comme avec
plainto_tsquery()
.
Le code du programme essaie donc
to_tsquery()
:
try: cursor.execute("SELECT to_tsquery('french', '%(query)s')" % \ {'query' : query}) except psycopg2.ProgrammingError: # Not PostgreSQL FTS syntax, convert it query = to_postgresql(query, default_is_or) cursor.execute("ROLLBACK;")
et, si cela échoue, il traduit la requête en PostgreSQL avec l'opérateur par défaut sélectionné (via un bouton radio) :
def to_postgresql(q, default_or = True): """ Converts query q (typically an human-entered string with funny characters to PostgreSQL FTS engine syntax. By default, the connector is OR. """ q = separators.sub(" ", q) q = q.strip() if default_or: connector = "|" else: connector = "&" # AND return re.sub(" +", connector, q)
Le système n'est pas parfait : PostgreSQL accepte des requêtes non documentées (par exemple « foo;bar » est accepté et équivalent à « foo&bar ») mais il permet de satisfaire l'utilisateur avancé (qui veut contrôler précisément la recherche grâce au langage de requête de PostgreSQL) et les autres, qui veulent simplement taper une liste de mots.
Tous les fichiers nécessaires sont distribués dans l'archive des programmes qui animent ce blog.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)