Mise en oeuvre de l'indexation et de la recherche avec le projet Apache Lucene dans Cocoon |
Dates de modification | ||
---|---|---|
Lucene est une projet Apache pour l'indexation de documents en vue de faire de la recherche et des statistiques sur ces derniers. Cocoon est un framework de transformations XSLT basé sur des pipelines, chargé de traiter un ensemble de contenus décrits sous forme XML afin de les convertir vers un panel de formats de sorties varié et extensible : PDF, RTF, WORD, HTML, WML pour ne citer que les formats les plus utilisés. Il est loisible aux développeurs d'implémenter des composants pour étendre les capacités de traitement du framework vers d'autres sources d'entrée ou d'autres formats de sortie.
Pipeline Cocoon : Generator ► Transformer ► Serializer
Cocoon intègre l'API Lucene à travers plusieurs composants, apportant ainsi les fonctionnalités d'indexation et de recherche à ce dernier :
LuceneIndexTransformer pour l'indexation par transformation au sein d'un pipeline( voir wiki)
SearchGenerator pour la recherche
LuceneCocoonIndexer pour l'indexation à partir de code Java.
L'indexation peut se faire de deux manières différentes:
par crawling en utilisant LuceneCocoonIndexer
ou bien par transformation, à l'aide de LuceneIndexTransformer.
La première méthode consiste à fournir une URL de départ à partir de laquelle tous les liens vont être parcourus afin de collecter les paires URL/contenu qui servent de base de départ à l'indexation. Ceci se fait à l'aide d'une page XSP, sorte de document XML mêlant du contenu et de la logique de programmation(ici langage Java). Pour résumer, la technologie XSP est au XML ce que JSP est au HTML. Voici un exemple de code d'une page XSP permettant la création d'un index par crawling
LuceneCocoonIndexer lcii; Analyzer analyzer = LuceneCocoonHelper.getAnalyzer( "org.apache.lucene.analysis.standard.StandardAnalyzer" ); void createIndex(String baseURL, boolean create) throws ProcessingException { try { lcii = (LuceneCocoonIndexer)this.manager.lookup( LuceneCocoonIndexer.ROLE ); Directory directory = LuceneCocoonHelper.getDirectory( new File( workDir, "index" ), create ); lcii.setAnalyzer( analyzer ); URL base_url = new URL( baseURL ); lcii.index( directory, create, base_url ); } catch (...) ...
Cette méthode implique de disposer d'un document index et d'un contenu que l'on peut parcourir exhaustivement de lien en lien: elle est donc plus adaptée à l'indexation du contenu d'un site qu'à celui d'un corpus de documents indépendant.
La seconde façon se fait au niveau même du sitemap à l'aide d'un transformer spécifique, LuceneIndexTransformer, composant qui prends en entrée un document XML contenant les URL et les contenus des documents à indexer. En sortie, il génère un rapport sur les documents et leur temps de traitement. Le travail d'implémentation consiste donc à mettre en place dans un pipeline une série de transformations dédiées à l'élaboration du document entrée, et éventuellement à l'affichage du rapport de l'indexation en sortie. Voici un exemple de ce que l'on peut trouver dans le sitemap :
déclaration du transformer :
<map:transformers default="xslt" > <map:transformer name="index" logger="sitemap.transformer.luceneindextransformer" src="org.apache.cocoon.transformation.LuceneIndexTransformer"/> </map:transformers>
définition d'un pipeline:
<map:match pattern="create-index"> <map:generate type="webdav" src="{iconf:/iconf/webdav}{request-param:path}" label="webdav"> <map:parameter name="depth" value="infinity" /> </map:generate> <map:transform src="stylesheets/MDGen2content.xsl" label="content"> <map:parameter name="webdavpath" value="{iconf:/iconf/webdav}{request-param:path}"/> </map:transform> <map:transform type="cinclude" label="cinclude"/> <map:transform src="stylesheets/content2lucene.xsl" label="lucene"> <map:parameter name="use-request-parameters" value="true"/> </map:transform> <map:transform type="index"/> <map:transform src="stylesheets/lucene-index2html.xsl"/> <map:serialize type="html"/> </map:match>
Tout ce qui se situe en amont de la tranformation nommée « index » représente la préparation du document d'entrée, lequel donne quelque chose de ce genre :
<lucene:index xmlns:lucene="http://apache.org/cocoon/lucene/1.0" analyzer="org.apache.lucene.analysis.standard.StandardAnalyzer" directory="index" create="false" merge-factor="20"> <lucene:document url="http://localhost/sample.html"> <!-- here is some sample content --> <html> <head> <title lucene:store="true">Sample</title> </head> <body> <h1>Blah</h1> <a href="blah.jpg" title="download blah image" lucene:text-attr="title"> <img src="blah-small.jpg" alt="Blah" lucene:text-attr="alt"/> </a> </body> </html> </lucene:document> <lucene:document url="http://localhost/sample-2.html"> <!-- Another sample doc --> <html> <head> <title lucene:store="true">Second Sample</title> </head> <body> <h1>Foo</h1> <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. </p> </body> </html> </lucene:document> </lucene:index>LuceneIndexTransformer indexe le contenu des éléments XML incluent dans l'élément <lucene:document>.
Pour indexer également certains attributs, il faut intégrer parmi les attributs l'attribut "lucene-text-attr", pour lequel on définie une valeur correspondant aux attributs que l'on veut indexer. L'attribut "lucene:store" permet de définir des éléments qui seront stockés en même temps que l'URL du document afin de pouvoir être affichés parmi les résultats de la recherche.
Les attributs de l'élément racine <lucene:index> sont les suivants :
directory : désigne le nom du répertoire où va être stocké l'index
merge-factor : détermine la fréquence à laquelle les indices de segment sont fusionnés
create : dans tous les cas l'index est créé, mais quand cet attribut est positionné à false, le processus n'écrase pas l'index s'il existe déjà, mais le met à jour. A true, l'ancien index est détruit.
Le composant SearchGenerator s'intègre en entrée de pipeline dans le sitemap, et fait référence au nom de l'index qu'il doit utiliser pour la recherche. L'emplacement physique du répertoire utilisé pour stocker l'index est le work directory de Cocoon, à savoir :
${TOMCAT_HOME)/work\Catalina\localhost\cocoon\cocoon-files
Pipeline Cocoon pour la recherche :
<map:match pattern="findIt"> <map:generate type="search" label="content"> <map:parameter name="index" value="injac-index"/> </map:generate> <map:transform type="log"/> <map:transform src="stylesheets/search-index2html.xsl"> <map:parameter name="use-request-parameters" value="true"/> </map:transform> <map:serialize type="html"/> </map:match>
Les paramètres reçus par SearchGenerator permettent de spécifier la requête, mais également le nombre de résultat par page, l'index de début et de fin de la page courante ainsi que ceux de la page précédente et suivante le cas échéant. Voici un tableau récapitulant ces paramètres :
Paramètres du SearchGenerator
Nom | valeur par défaut | Description |
queryString | pas de valeur par défaut | Spécifie la chaîne de requête exécutable par le moteur de recherche |
pageLength | 10 | Nombre de résultats affichés par page |
startIndex | 0 | Index de départ de l'affichage des résultats |
startNextIndex | 0 | Index de départ de l'affichage pour la page suivante |
startPreviousIndex | 0 | Index de départ de l'affichage pour la page précédente |
Cela signifie que le SearchGenerator ne renvoie pas que des résultats bruts, mais également des éléments permettant de construire une page de navigation sur les résultats. Le document de sortie de ce generator a la forme suivante :
?xml version="1.0" encoding="UTF-8"?> <search:results date="1101730810578" query-string="ldap" start-index="0" page-length="10"> <search:hits total-count="2" count-of-pages="1"> <search:hit rank="0" score="0.32969838" uri="/slide/files/UT1/Cri/exploitation-LDAPGLOBAL-20040923.xml?title=&file=exploitation-LDAPGLOBAL-20040923.xml"/> <search:hit rank="1" score="0.2851561" uri="/slide/files/UT1/Cri/ldap_samba_howto.xml?title=Ldap Samba&file=ldap_samba_howto.xml"/> </search:hits> <search:navigation total-count="2" count-of-pages="1" has-next="false" has-previous="false" next-index="2" previous-index="0"> <search:navigation-page start-index="0"/> </search:navigation> </search:results>
Lucene implémente une syntaxe pour les expressions de requêtes permettant une série de fonctionnalités de recherche :
recherche dans les champs
Un nom de champ suivi de deux points induit une recherche dans un champs. ex.:
body:ldap renvoie les documents ayant un champ body contenant "ldap"
title:samba renvoie les documents dont un champ "title" contient "samba"
caractères de substitution
"*" remplace une série de caractère "?" remplace un caractère ex.: ?dsl retourne des documents contenant aussi bien "adsl" que "xdsl" ou "sdsl".
Note : On ne peut pas utiliser le caractère "?" ou "*" en début de requête (provoque une erreur lexicale).
recherche floue
Faire suivre un terme d'un tilde accompagné optionnellement d'une valeur entre 0 et 1 induit une recherche par approximation du terme. "1" signifie une grande ressemblance alors que zéro ouvre à une tolérance beaucoup plus grande. La valeur par défaut est 0.5. ex.: ldab~0.8
recherche par proximité
Il est possible de spécifier une "distance" maximale entre deux termes recherchés, en faisant suivre une expression par un tilde et une valeur numérique entière qui indique le nombre de mots comme tolérance d'écart. ex.: "jakarta apache"~10 désigne les documents contenant les mots "jakarta" et "apache" espacés d'au maximum 10 mots.
recherche par rang
On peut également utiliser des plages de valeur pour un champ donné, dont les limites sont incluses ou excluses selon que l'on utilise respectivement les crochets ou les accolades. ex.: mod_date:[20020101 TO 20030101] désigne l'ensemble des documents dont le champ "mod_date" est compris entre 20020101 et 20030101 inclus. title:{Aida TO Carmen}ensemble de documents dont le champs "title" est compris entre "Aida" et "Carmen" exclus.
pondération des termes
Avec l'accent circonflexe suivi d'un entier, on peut affecter un terme d'un poids plus élevé que d'autres. ex.: jakarta^4 apache
opérateurs et groupes
Enfin, il est possible d'utiliser des opérateurs pour cumuler, choisir ou retrancher des termes ou expressions, ainsi que les parenthèses pour regrouper des éléments. ex.: "jakarta apache" OR jakarta opérateur par défaut, union des ensembles de documents contenant l'expression ou le terme désigné "jakarta apache" AND "jakarta lucene" intersection des ensembles de documents contenant l'expression ou le terme désigné "jakarta apache" NOT "jakarta lucene" exclusion des documents contenant l'expression après NOT "jakarta apache" -"jakarta lucene" idem que NOT +jakarta apache indique l'obligation de contenir le terme "jakarta"
Regroupements:
(jakarta OR apache) AND website
title:(+return +"pink panther")