Chapter 1 Travaux Pratiques : Lucène - Gestion d un index plein texte Pour ce TP, nous allons étudier l utilisation d un index de recherche textuel OpenSource : Lucene 1. Nous allons créer un projet en Java qui va indexer un ensemble de documents et nous permettre de regarder le contenu des résultats retourner par des requêtes textuelles. 1.1 Indexation 1. Créer un projet Java sous eclipse 2. Télécharger et intégrer le package lucene : http://cedric.cnam.fr/ traversn/lucene/lucene-3.0.2.zip Contenu intéressant de l archive : lucene-core-3.0.2.jar : Package coeur de la plateforme Lucène documents contrib : main packages analyzers : Analysers de texte pour extraire les mots en fonction de la langue (luceneanalyzers-3.0.2.jar). Indispensable pour l indexation et l interrogation. collation: Change l analyser lucène pour optimiser les tris et les requêtes par intervale. db : Base de données berkeleydb instantiated : Index lucene en mémoire centrale queryparser : Permet de modifier le query parser snowball : Package de lemmatisation du texte en fonction de la langue. spellchecker : Verifie l orthographe des mots et propose des corrections spatial : Classement des résultats en fonction du fonction de score spatiale wordnet : Dictionnaire Wordnet permet de vérifier si un mot est contenu dans celui-ci et d en extraire des synonymes. importez les packages lucene-core-3.0.2.jar et lucene-analyzers-3.0.2.jar (contrib/analyzer) 3. Ci-dessous, quelques fonctions java pour la création et fermeture de l index (s il vous manque les noms des packages générez les via eclipse) : 1 Site officiel : http://lucene.apache.org/java/docs/ 1
String directory = "index"; //Writing object, linked to the repository org.apache.lucene.index.indexwriter writer = null; /* Open or create the index */ protected void openindex(boolean newindex) throws CorruptIndexException, IOException { //Link the directory on the FileSystem to the application Directory d = FSDirectory.open(new File(directory)); try { //Verifies if the index has already been locked (or not closed properly). if (IndexWriter.isLocked(d)) IndexWriter.unlock(d); if (writer == null) //Link the repository to the IndexWriter writer = new IndexWriter(d, new StandardAnalyzer(Version.\lucene{_30), newindex, IndexWriter.MaxFieldLength.LIMITED); catch (FileNotFoundException e) { writer = new IndexWriter(d, new StandardAnalyzer(Version.\lucene{_30), true, IndexWriter.MaxFieldLength.LIMITED); Pour fermer l index, il faut d abord l optimiser et ensuite le fermer. // Compact and flush the index on the repository public void writeindex() throws CorruptIndexException, IOException { writer.optimize(); // Close the index public void closeindex() throws CorruptIndexException, IOException { writeindex (); writer.close(); 4. Créer un objet de gestion de l index et une fonction d initialisation 5. Créer un objet qui permet de créer, ouvrir et fermer un index Lucene 6. Maintenant que nous avons un index, il faut le peupler. Pour cela, il faut créer un Document avec différents champs (Field) et l ajouter à l index (writer). Ci-dessous un exemple de bout de code pour l ajout d un titre, de son contenu et le chemin vers le fichier : org.apache.lucene.document.document doc = new Document(); // Add the title Field, which will be indexed and Stored doc.add(new Field("title", new StringReader (title), TermVector.YES)); // Add the content Field, which will be indexed and Stored in a TermVector doc.add(new Field("content", content, Field.Store.YES, Field.Index.ANALYZED)); // Add the path Field which will be displayed each time the document is returned doc.add(new Field("path", file, Field.Store.YES, Field.Index.NOT_ANALYZED)); // Add the Document into the Index writer.adddocument(doc); Pour plus de détails, l API 2 de lucene permet d avoir plus de précision sur l objet Document (pour les différentes versions de la fonction add). 7. Créer une fonction qui permet d ajouter un fichier à l index (avec titre, contenu, chemin). 1.2 Index de fichiers Nous allons créer un programme qui index un ensemble de fichiers textes, apres analyse du titre et du contenu. 2 API Lucene : http://lucene.apache.org/java/3 0 2/api/ 2
1. Récuperer le fichier texte suivant : http://cedric.cnam.fr/ traversn/lucene/files/methode.txt 2. Copier le fichier dans un répertoire files 3. Dans le fichier, on peut remarquerun champ titre et un champ contenu. Créerune fonction qui extrait chaque champ à partir d un fichier. On n utilisera alors FileReader sur le chemin du fichier passé en paramètre pour parser le document pour en récupérer le titre et le contenu. 4. Une fois les champs récupérés, faire appel à la fonction d indexation avec les champs titre, contenu et le chemin vers le fichier. 5. Créer une fonction qui va analyser le contenu du répertoire files et analyser chaque fichier. 1.3 Recherche dans l index Maintenant que nous avons indexé un document, nous allons pouvoir rechercher celui-ci à l aide de requêtes. 1. Pour préparer les requêtes, il faut un analyseur et un parseur lié à l index. Ci-dessous, se trouve le créateur d index avec un analyseur de texte en Français. IndexReader reader = IndexReader.open(FSDirectory.open(new File(directory)), true); IndexSearcher searcher = new IndexSearcher(reader); Analyzer analyzer = new StandardAnalyzer (Version.lucene_30); String defaultfield = "content"; queryparser = new QueryParser(Version.lucene_30, defaultfield, analyzer); 2. Créer un objet de gestion de vos requêtes et une fonction qui permet d initialiser le moteur de requête. 3. Pour faire la requête q, et récupérer les 20 premiers résultats, il suffit d utiliser : //Parse the given String query Query query = queryparser.parse(q); //Prepare the result format TopScoreDocCollector collector = TopScoreDocCollector.create(20, false); //Search the corresponding documents searcher.search(query, collector); //Show founded results ScoreDoc[] hits = collector.topdocs().scoredocs; ScoreDoc scoredoc; Document document; for (int i = 0; i < hits.totalhits; i++) { try { scoredoc = hits[i]; document = searcher.doc(scoredoc.doc); System.out.print (scoredoc.score); System.out.print("\t"); System.out.print (document.get("title")); System.out.print("\t"); System.out.println (document.get("path")); catch (Exception e) { 4. Créer une fonction qui permet d interroger votre index et d afficher le résultat à l aide d une requête (champ String) 5. le langage d interrogation, il suffit d utiliser un ensemble de mots-clés. La syntaxe permet de faire des recherches un peu plus élaborées avec des recherches exactes, des pondérations, des wildcards (complétion)... Vous trouverez la syntaxe exacte ici :. 3
1.4 Test de l index Maintenant que nous pouvons ajouter des documents à l index et interroger l index, nous pouvons manipuler intégralement cet index. 1. Créer un objet qui intéragi avec l indexeur et le requêteur 2. Créer un menu qui permet de scanner le contenu du répertoire et d interroger l index 3. Télecharger les fichiers sur le répertoire suivant : http://cedric.cnam.fr/ traversn/lucene/files. Cette vingtaine de fichiers respectent le même format (title + content). Placer ces fichiers dans votre projet dans le répertoire f iles. 4. Indexer tous les fichiers, via le scanne du répertoire files 5. Faire les requêtes suivantes et comparer les résultats : (a) Recherche Information (b) Recherche NOT Lucene (c) Recherche Information (d) Rechercheˆ 3 Information (e) Recherche Info* (f) title: Recherche (g) Recherche Information 2 (h) title:recherche TO Information (i) +Recherche Information La syntaxe complete utilisée par le moteur lucene est disponible sur ce site : http://lucene.apache.org/java/2 3 2/queryparsersyntax.html 1.5 Indexation d un site web Pour aller plus loin, nous vous proposons de créer un index pour un site web. Pour cela, vous pouvez créer un objet qui va récupéer la page principale d un site web déterminé par son domaine. Ensuite pour chaque ancre (lien href) présent sur la page, vous parcourez le lien s il est sur le même site web et le parcourir récursivement 3. Pour chaque page, il faut extraire le texte et indexer la page avec son adresse de maniere récurisive. Il faut vérifierque vous cherchez une page de ce site web (même domaine), et que celui-ci contient bien du texte (il faut enlever toutes les balises). Afin de détecter le titre ou les liens, il est fortement conseillé d utiliser la classe Matcher avec un Pattern approprié. Attention, la recherche récursive de pages sur un site web peut être longue et compliquée. Il faut faire attention à ne pas indexer deux fois la même page, et éviter d indexer les images, les vidéos ou les musiques. Il vaut mieux aussi n indexer que les pages du site web, sinon, vous aller indexer le Web... 3 L utilisation de thread est fortement conseillée 4
Chapter 2 Travaux Pratiques : Lucène - la fonction de similarité Pour cette partie du TP, nous allons poursuivre l utilisation du moteur d indexation Lucène. Nous allons cette fois-ci modifier la fonction de calcul de similarité afin de voir les conséquences de chaque calcul sur l ordonnancement des résultats. 2.1 Scoring Afin d ordonner les résultats trouvés dans l index, une méthode dite de Scoring est utilisée, basée sur le principe du tf/idf : score(q,d) = (tf(t d ) idf(t) getboost(t.field d ) lengthnorm(t.field d )) coord(q, d) queryn orm(q) La fonction de score utilise les paramètres suivants : 1. q : requête (query) 2. d : document 3. t : terme 4. tf : Fréquence des termes dans le document. Un document qui contient plus souvent un terme est généralement plus relevant. Par défaut : freq 5. idf : Fréquence inverse de la présence du terme dans l index. Les termes les plus communs de l index sont discrimants (contrairement au nom moins communs). Par défaut : log( numdocs docfreq+1 )+1 6. getboost : facteur de boost pour le champ du terme (provient de la requête, en utilisant ˆ ) 7. lengthnorm: La valeur de normalisation pour un champ, à partir du nombre total de termes contenus dans ce champ. Cette valeur est stockée dans l index. Ces valeurs, avec fieldboost, sont stockées dans un index et multipliés dans les scores de hits, sur chaque champ, par le code de recherche. 1 par défaut : numterms 8. coord : Nombre de termes de la requête couvert dans le document. Plus un document répond, plus il est important. par défaut : overlap maxoverlap 5
9. querynorm : la valeur de normalisation pour une requête, à partir de la somme des carrés des poids de chacun des termes de la requête. Cette valeur est ensuite multipliée par le poid de chaque terme de requête. Seuls les fonctions tf,idf,lengthnom et coord (celles soulignées) sont modifiable dans Lucène. Les fonctions par défaut sont données au dessous. Ainsi, l algorithme de scoring peut-être personnalisé en définissant votre propre classe Similarity. 2.2 Class Similarity La classe Similarity se trouve dans le package Lucène : org.apache.lucene.search. On peut le modifier via l objet de recherche IndexSearcher que vous utilisez pour vos requêtes. La fonction de similarité par défaut est : org.apache.lucene.search.def aultsimilarity. Voici les différentes étapes pour modifier votre fonction de similarité : 1. Créer un nouvel objet qui hérite de Def aultsimilarity ; 2. Y insérer les fonctions suivantes : public float tf(float freq) ; public float idf(int docfreq, int numdocs) ; public float lengthnorm(string fieldname, int numterms) ; public float coord(int overlap, int maxoverlap) ; 3. Implémenter les fonctions par défaut ; 4. Modifier votre objet similarité pour paramètrer les fonctions choisies. Celles-ci peuvent prendre les calculs suivants : tf : freq, 1, freq, 1 freq,... ; idf : log( numdocs docfreq+1 )+1, 1, numdocs numdocs docfreq+1, 1 log( docfreq+1 ) ; lengthnorm : 1 numterms, 1, numterms, 1 1 numterms ; coord : overlap overlap maxoverlap, 1, 1 maxoverlap, 1 maxoverlap ; 5. Comparer les résultats obtenus à la séance précédente avec différents paramétrages/combinaisons de fonctions. 6
Chapter 3 Travaux Pratique : Lucène - Extension Dans cette partie du TP, nous souhaitons intégrer de nouvelles fonctionnalités à notre plateforme d indexation. 1. Développez une interface graphique qui va intégrer l indexation avec les fonctionnalités suivantes : adresse du site web à indexer, stopper l indexation en cours, nombre de documents indexés, vider l index Pour l interrogation : un champ pour la requête une visualisation du document un lien vers la page indexée un panneau de configuration de la fonction de similarité, prenant en paramètre les différentes fonctions de similarité que vous avez intégré dans la partie précédente. 2. Le package snowball permet de gérer la lemmatisation des mots. Nous souhaitons pouvoir paramétrer la plateforme d index pour être capable de le tenir en compte. Lorsque celui-ci est activé, il faut que chacun des termes des documents soient indexés en utilisant snowball. Ainsi, ne seront stockés que les racines des mots. De même, lors de l interrogation, il faudra faire la lemmatisation de chacun des mots de la requête. 3. De même, ajoutez un correcteur orthographique(spellchecker) au module de requête pour pouvoir proposer une requête plus appropriée. 4. Afin d améliorer les requêtes posez à votre index, ajouter un module Wordnet qui vous permettra de trouver des synonymes pour chaque mot de la requête. Proposez ainsi une liste des nouvelles requêtes possibles auxquelles vous associerez le nombre de documents répondant à chaque nouvelle requête(nous n afficherons pas les résultats, seulement le nombre de résultats). 7