Introduction à Lucene
Lucene est une librairie open source en Java (mais il existe de nombreux portages) permettant d’ajouter des fonctionnalités de recherche plein-texte à vos applications. Le projet Lucene est chapeauté par “The Apache Software Foundation”. D’autres projets très connus et de grande qualité de la fondation sont : Apache HTTP server, Tomcat, Cocoon, Ant, …
Il s’agit bien d’une librairie avec laquelle il n’est pas fourni d’outils permettant l’indexation de données en quelques clics de souris et quelques paramétrages. Il faut donc en passer par du code Java afin de mettre en place une solution sur mesure de recherche plein-texte.
Principe
Lucene indexe et retrouve des “documents”. Par document, on ne parle pas de fichiers Excel, Word, PDF ou HTML, mais d’une structure de données constituée de champs. Un champ est une donnée possédant un nom (titre, auteur, date de publication, contenu, ..) et à laquelle est associé du texte. C’est ce texte qui est indexé, recherchable et affichable. Les documents indexés sont regroupés au sein d’une collection de documents appelée “index”. Un index peut contenir plusieurs centaines, milliers ou millions de documents et il est possible de créer autant d’index différents que le nécessite votre ou vos applications. Physiquement, un index est un répertoire (que vous spécifiez) hébergeant un nombre variable de fichiers (ça c’est l’affaire de Lucene).
Si le texte qui est à indexé est contenu dans des fichiers Excel, Word, PDF ou HTML, c’est de votre ressort d’en extraire de contenu textuel qui sera indexé. Il est possible d’utiliser par exemple pdftotext pour les fichiers PDF et Antiword pour les fichiers Microsoft Word.
Obtenir et utiliser Lucene
La version actuelle de Lucene est la 2.3.2 et est disponible ici. La fichier lucene-x.x.x.zip est suffisant, mais le fichier lucene-x.x.x-src.zip avec les sources devient vite intéressant lorsque l’on veut étendre les possibilités de lucene et disposer d’exemples de code.
Dans la suite de cet article nous allons voir un exemple minimaliste illustrant comment indexer et rechercher des données. Cet exemple nous permet d’introduire les concepts de base de Lucene : document, field, analyzer, query, hits, …
La première chose à faire afin de pouvoir développer des classes Java utilisant Lucene, c’est de créer un projet dans votre environnement de développement et d’y inclure la librairie principale de Lucene : lucene-core-x.x.x.jar. J’utilise pour ma part “Eclipse Europa”. A noter qu’un bug dans Sun Java 1.6 pose problème à Lucene (détails ici), je vous conseille donc pour l’instant d’utiliser Sun Java 1.5.
Un peu de pratique
L’exemple qui suit est constitué d’une unique classe LuceneIntroduction.java dont voici le projet Eclipse complet dans un fichier zip.
Squelette de la classe
Le code suivant constitue le squelette de la classe. Il déclare les packages nécessaires et la méthode main qui exécute successivement une méthode pour l’indexation et une méthode pour la recherche.
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.*; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.search.Query; import org.apache.lucene.search.Hits; import org.apache.lucene.search.Hit; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import java.io.File; import java.util.*; public class LuceneIntroduction { static final String INDEX_DIR = "c:\temp\index_test"; public static void main(String[] args) { if (!index()) System.exit(1); if (!searchAndDisplay("titi")) System.exit(1); if (!searchAndDisplay("bla")) System.exit(1); } // ... }
Indexer des données
L’indexation de données met en oeuvre 4 classes Lucene.
| IndexWriter | c’est la classe qui donne accès aux index en écriture (création, ajout de document, optimisation, …) | |||||||||
| Analyzer | il s’agit d’un ensemble de classes qui ont pour but le découpage du texte en “token” (mot) et la normalisation du texte à indexer. Les principaux analyzer fournis sont :
|
|||||||||
| Document | Un Document représente une unité élémentaire d’information. Par exemple, indexer tous les fichiers Word d’un répertoire va ajouter dans l’index un Document Lucene par fichier. Ce sont des Documents qui sont retournés dans la liste de résultats d’une recherche. Comme cela a déjà été dit, un document est constitué de champs “Field” (nom / valeurs). | |||||||||
| Field | Il s’agit d’un sous élément d’un document. Les champs les plus fréquents sont : titre, auteur, date de publication, url et bien sur le texte du fichier Word, PDF ou HTML. | |||||||||
Le code suivant créer un index et ajoute 3 documents dans cet index. La méthode createDocument est plus particulièrement dédiée à la création d’un objet Document Lucene constitué de 3 champs : id, titre et texte.
public static boolean index () { File dir = new File (INDEX_DIR); if (dir.exists()) { System.out.println("Impossible de créer l'index dans le répertoire '" + INDEX_DIR + "', veuillez le supprimer d'abord."); return false; } try { // Création de l'index IndexWriter writer = new IndexWriter(INDEX_DIR, new StandardAnalyzer(), true); // Création et indexation d'un premier document Document doc = createDocument ("1", "Titre 1", "bla bla"); writer.addDocument(doc); // Création et indexation d'un second document doc = createDocument ("2", "Titre 2", "titi tutu"); writer.addDocument(doc); // Création et indexation d'un troisième document doc = createDocument ("3", "Titre 3", "bla bla titi tutu"); writer.addDocument(doc); // Fermeture de l'index writer.close(); } catch (Exception e) { e.printStackTrace(); return false; } return true; } private static Document createDocument (String id, String titre, String texte) { // Créer un document vide Document doc = new Document(); // Créer le champ id doc.add(new Field ("id", id, Field.Store.YES, Field.Index.UN_TOKENIZED)); // Créer le champ titre doc.add(new Field ("titre", titre, Field.Store.YES, Field.Index.TOKENIZED)); // Créer le champ texte doc.add(new Field ("texte", texte, Field.Store.NO, Field.Index.TOKENIZED)); return doc; }
On remarque que l’analyzer utilisé est spécifié au constructeur de l’objet IndexWriter. Et qu’un ensemble d’attributs importants sont spécifiés au constructeur de l’objet Field.
Le premier attribut est le mode de stockage de la donnée associée au champ : Field.Store.YES (stocké) ou Field.Store.NO (non stocké). Pour être indexé, une donnée ne doit pas forcément être stockée. On stockera un titre et un auteur par exemple car il doivent pouvoir être récupérés afin d’être affichés dans une liste de résultats. La totalité du texte d’un document PDF de 100 pages ne sera pas stocké mais juste indexé.
Le second attribut est le mode d’indexation de la donnée associée au champ : Field.Index.NO (non indexé), Field.Index.TOKENIZED (indexé avec découpage en mots), Field.Index.UN_TOKENIZED (indexé sans découpage en mots).
Rechercher
La recherche met en oeuvre 6 classes Lucene.
| IndexSearcher | c’est la classe qui donne accès aux index en recherche |
| Analyzer | Tout comme pour l’indexation les analyzer font partie du processus de recherche fin de normaliser les critères de recherche : |
| QueryParser | un parser de requête |
| Query | un objet qui représente la requête de l’utilisateur et utilisé par un IndexSearcher. |
| Hits | Une collection d’éléments résultats de la recherche |
| Hit | Un élément de la collection des résultats |
| Document | Un document retrouvé et tel qu’il était lors de son ajout dans l’index (constitué des mêmes champs) |
Le code suivant recherche les documents correspondant au critère et les affiche.
public static boolean searchAndDisplay (String criteria) { try { IndexSearcher searcher = new IndexSearcher(INDEX_DIR); QueryParser parser = new QueryParser("texte", new StandardAnalyzer()); Query query = parser.parse(criteria); Hits hits = searcher.search(query); System.out.println("Résultats pour '" + criteria + "': " + hits.length()); Iterator iter = hits.iterator(); while(iter.hasNext()) { Hit hit = iter.next(); Document doc = hit.getDocument(); System.out.println(doc.get("titre")); } } catch (Exception e) { e.printStackTrace(); return false; } return true; }
Résultat de l’exécution
Résultats pour 'titi': 2 Titre 2 Titre 3 Résultats pour 'bla': 2 Titre 1 Titre 3
Et si mes applications ne sont pas écrites en Java ?
Si vous acceptez de ne pas utiliser la toute dernière version de Lucene, vous pouvez utiliser une version portée dans votre langage de programmation préféré. Il existe de nombreux portages : Perl, Python, Ruby, C, C++, .NET, Common Lisp et PHP (recherche uniquement).
Si comme moi vous préférez utiliser la version originale Java en permanente évolution grâce à sa grande communauté de développeurs, voici ma suggestion : les Services Web.
Vous avez écrit une application Web dont l’interface est en PHP et les données sont dans une base SQL. Vous n’avez pas le choix, il faut développer votre moteur de recherche avec Java. Par contre, il faut également interfacer ce moteur de recherche avec votre application PHP pour lancer les recherches et afficher les résultats.
Pour la partie indexation des données il n’est pas nécessaire de s’interfacer avec le PHP, une application Java autonome peut être développée. Par contre, pour la recherche il faut interfacer le code PHP avec le code Java. La solution consiste en la mise en place de Services Web écrits en Java et fonctionnants sous Tomcat. Il n’est pas nécessaire de se lancer dans des Services Web au standard SOAP, le standard REST est largement suffisant. En gros une requête HTTP est envoyée à une servlet qui elle même retourne des résultats au format XML. Cette méthode permet d’interfacer la recherche Java/Lucene à tous types d’applications (WEB ou non WEB) et écrites dans n’ importe quel langage.
Extensions de Lucene
L’exemple présenté est comme je l’ai déjà dit “minimaliste”. Les possibilités offertes par Lucene sont très larges et se rapprochent des moteurs de recherche les plus puissants. En effet, il existe de nombreuses extensions fournies dans la distribution : analyzers avancées, corrections orthographiques, mise en évidence des termes recherchés dans les résultats, …
En voici la présentation dans la Sandbox Lucene
Utilitaires
Voici deux utilitaires intéressants en phase de développement.
Luke – outils de monitoring et de consultation des index
Limo – outils de monitoring des index
Documentation
La documentation est disponible ici. En plus de la javadoc, on y trouve une FAQ, un Wiki et différents articles intéressants.
jGuru fournit une FAQ Lucene intéressante.
Et enfin, il existe un livre en anglais basé sur une déjà ancienne version 1.4 : Lucene in Action (2004).
Support et assistance
Lucene est un projet open source, il n’existe pas de support à proprement parlé, mais il existe une mailing-list et un forum très actifs qui permettent d’obtenir de l’aide et des suggestions pour les problèmes les plus pointus.
Projets utilisant Lucene
Tags: java, Lucene / Solr, moteur de recherche, tomcat, tutoriel
août 21, 2008
Bonjour,
Belle présentation de Lucène. Pour information, le portail open source Lutece utilise Lucène depuis l’origine du projet. On pourra y trouver plusieurs exemples de mise en oeuvre.
Bonne continuation.
Pierre
août 27, 2008
Bonjour et merci pour cette présentation !
Je pense qu’il y a une erreur ici :
Field.Store.YES (non stocké) ou Field.Store.NO (stocké)
C’est le contraire non ?
août 27, 2008
@Marco
Exact. Merci !
sept 18, 2008
Bonjour !
merci pour cet excellent article.
une demande cependant : je veux réaliser un client lourd en java permettant la recherche de chaine de caractères dans de gros fichiers de logs (texte) et afficher les résultat à l’aide d’un interface graphique (SWING?).
Lucene est t il adapté à mon projet ? je veux principalement rechercher des chaines dans de gros fichiers textes et filtrer l’affichage.
merci beaucoup
sept 18, 2008
Ce que vous voulez faire c’est chercher des lignes dans le fichier de log qui contiennent certains mots ou expressions.
Il faut considérer chaque ligne du fichier log comme un document lucene. D’une ligne peut être extraite la date et peut être d’autres informations. Le document Lucene contiendra un Field pour le texte de la ligne de log et d’autres Fields pour les autres informations interessantes (par exemple pour un log apache : la date, le code d’erreur, l’ip du client, …)
Oui, je pense que Lucene peut répondre au besoin.
sept 18, 2008
merci beaucoup pour ta réponse rapide !
> Il faut considérer chaque ligne du fichier log comme un document lucene.
ok mais, mes fichiers risquent d’être énormes -> plusieurs millions de lignes au total !
je peux quand même indéxer mon fichier comme une table de base de donnée alors ?
merci beaucoup
déc 04, 2008
[...] Il s’agit bien d’une librairie avec laquelle il n’est pas fourni d’outils permettant l’indexation de données en quelques clics de souris et quelques paramétrages. Il faut donc en passer par du code Java afin de mettre en place une solution sur mesure de recherche plein-texte. (Lire la suite) [...]
fév 10, 2009
A ce que il y a une possibilité de indexer une base de donne ave lucene
fév 10, 2009
Lucene n’offre pas de module d’indexation de base données qui ne nécessiterait que du paramétrage. Il faut développer sa propre solution (interroger la base en jdbc et indexer les données dans lucene).
Le projet Solr qui est basé sur Lucene offre un Database Import Handler très puissant.
J’ai également trouvé ce produit commercial :
http://www.sematext.com/product-db-indexer.html
fév 15, 2009
comment modifier la base d’indexation de lucene par exemple modifier le champ name qui contient le nom de document ,ou bien a c que il y a une possibilité d’ajouter d’autre champs a la basse d’indexation
mar 08, 2009
Salut,
je débute sous Lucene, et j’ai une question concenant le résultat d’une recherche. Je m’explique:
j’ai plusieurs fichiers PDF que j’indexe, et j’index toujours la date de création. Alors je sais pas est ce que il y a un moyen de dire au moteur de recherche de trier les résultats de plus récents en plus anciens.
Merci d’avance
mar 10, 2010
Bonjour
Je vous remercie pour cette petit introduction et j’ai question sur l’indexation du mot accentués .Car j’ai un problème lors de la recherche d’un mot sans mettre leur accent.
Exemple si je cherche le mot “problème” le résultat est OK ce qui n’est pas le cas si je lance la recherche du mot “probleme” sans mettre leur accent .
Merci d’avance
mar 10, 2010
@Walibda
Il faut utiliser lors de l’indexation et de la recherche une analyzer qui inclura le filtre ISOLatin1AccentFilter.
Le but est de normaliser tous les caractères accentués dans leur forme non accentuée (éèê -> e, î->i, …). Ainsi, “problème” et “probleme” seront considérer comme identiques.
mar 10, 2010
Bonjour
j’utilise le filter StandardAnalyzer.
Est ce que ne marche pas avec ce filter .
Cordialement
mar 10, 2010
StandardAnalyzer est bien pour l’anglais mais pas pour les langues européennes accentuées. Cet analyser fait :
* découpage en token
* normalisation en minuscule
* suppression des mots vides (paramétrable)
Il faudrait le dériver en un StandardISOLatinAnalyzer.
et surcharger les méthodes :
public TokenStream tokenStream(String fieldName, Reader reader)
public TokenStream reusableTokenStream(String fieldName, Reader reader)
mar 10, 2010
Salut
Quand j’utilise le filtre ISOLatin1AccentFilter,mon débogueur d’ezpublish3.9.2 affiche ça
Warning: PHP Mar 10 2010 17:15:30
java.lang.Exception: CreateInstance failed: new org.apache.lucene.queryParser.MultiFieldQueryParser((String;)o(String;), (Analyzer)c(ISOLatin1AccentFilter)). Cause: java.lang.IllegalArgumentException:
Est ce que l’instanciation n’est pas correct ?
Merci
mar 10, 2010
C’est un peu juste comme information.
mar 10, 2010
Merci pour tous
nov 12, 2010
Bonjour à toutes et tous,
J'utilse la recherche lucene sur mon applicatif (JAVA JEE, Lucene-analysers-2.1.0.jar) depuis en certain temps. Hors actuellement j'ai un léger soucis (qui doit dater !!!). En effet le parser semble me tronquer les mots clés. Par contre aucun soucis avec les accents.
——————————————————————————-
Exemple :
theKeyword = "voie"
parser.parse(theKeyword ) = "voi"
parser.parse(theKeyword.toUpperCase()) = "voie"
parser.parse(theKeyword.toLowerCase()) = "voi"
Idem pour "Partie" qui devient "part", "Cordialement" qui devient "Cordial". Par contre un mot comme "épervirà" reste "épervirà"… Bref je nage un peu…
Utiliser le toUpperCase() n'est pas vraiment bon en soit car il ne recherchera que le mot clé en majuscule dans les fichiers d'index.

———————————————————————————–
Voici mon code :
public static List findByKeyWords(String theKeyWords) throws ParseException, IOException {
log.info("Entrée dans la méthode –> findByKeyWords"); //$NON-NLS-1$
List candidatsList = new ArrayList();
FrenchAnalyzer analyser = new FrenchAnalyzer();
QueryParser parser = new QueryParser(KEYWORDS_FIELD, analyser);
Query query = parser.parse(theKeyWords);
IndexSearcher searcher = new IndexSearcher(REP_INDEX);
Hits hits = searcher.search(query);
for (int i = 0; i < hits.length(); i++) {
Document doc = hits.doc(i);
candidatsList.add(new Integer(doc.get(ID_CANDIDAT)));
}
log.info("Sortie de la méthode –> findByKeyWords"); //$NON-NLS-1$
return candidatsList;
}
Si vous avez une idée, ou pas
Merci.
mar 17, 2011
Bonjour,
je cherche à modifier la methode de recherche vectorielle de lucene. Quelle(s) classe(s) dois-je modifier? Comment?
merci d'avance
avr 18, 2011
tres bonne introduction pour lucene , merci pour le code
juin 25, 2011
Pour tester l’installation de Lucene en recherchant un terme dans le contenu indexé de l’ensemble de ses fichiers sources. comment en tapant la commande :
java org.apache.lucene.demo.SearchFiles
merci
mar 11, 2013
merci d’avance ,s’il y a qu’un personne peut m’assister vraiment il me fait un grand plaisir.
le modèle sac de mot c’est un modèle de recherche d’information