Travaux Pratiques : Luc`ene - Gestion d`un index plein texte

Transcription

Travaux Pratiques : Luc`ene - Gestion d`un index plein texte
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 : Lucene1 . 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’API2 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 remarquer un champ titre et un champ contenu. Créer une fonction qui extrait
chaque champ à partir d’un fichier. On n’utilisera alors F ileReader 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écursivement3.
Pour chaque page, il faut extraire le texte et indexer la page avec son adresse de maniere récurisive. Il
faut vérifier que 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) =
P
(tf (td ) × idf (t) × getBoost(t.f ieldd) × lengthN orm(t.f ieldd))
×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 : f req
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).
numDocs
Par défaut : log( docF
req+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 : √numT
erms
8. coord : Nombre de termes de la requête couvert dans le document. Plus un document répond, plus il
est important.
overlap
par défaut : 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, lengthN om 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 : f req, 1, f req, 1 − f req, ... ;
numDocs
• idf : log( docF
req+1 ) + 1, 1,
• lengthNorm :
• coord :
√
1
,
numT erms
overlap
maxOverlap ,
1, 1 −
numDocs
docF req+1 ,
1,
√
numDocs
1 − log( docF
req+1 ) ;
numT erms, 1 −
1
√
numT erms
overlap
1
maxOverlap , 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