Moteurs de recherche Jean-Jacques LE COZ Introduction La vogue des annuaires d'entreprise et le succès des moteurs de recherche sur internet ont donné l'idée aux éditeurs de logiciels de créer une nouvelle couche applicative de type moteur de recherche au dessus des couches logicielles de type middleware et au dessus des systèmes de gestion de bases de données. Moteur de recherche Un moteur de recherche est un logiciel permettant de retrouver des ressources associées à des mots quelconques Exemples Voila, Yahoo Google Desktop, Copernic Mamma, Kartoo Lucene Types de moteur Moteur de recherche Web Outil de recherche sur le web constitué de robots qui parcourent les sites à intervalles réguliers et de façon automatique sans intervention humaine, ce qui les distingue des annuaires pour découvrir de nouvelles adresses (URL) Moteur Desktop Outil qui combine la recherche parmi les fichiers stockés sur le PC et la recherche parmi les sites web Principes des moteurs du Web Le fonctionnement se décompose en trois étapes : Exploration Indexation Recherche Exploration Réalisée grâce à un robot d'indexation Les hyperliens sont parcourus récursivement Les ressources intéressantes sont récupérées L'exploration est lancée à partir d'une ressource pivot Indexation Réalisée par extraction des mots considérés comme significatifs à partir des ressource récupérées Les mots extraits sont stockés dans une base de données organisée comme un dictionnaire inverse Recherche Correspond à la partie requête du moteur Un algorithme se charge de pondérer les correspondances afin de présenter le résultat par ordre de pertinence Surcouche logiciel De plus en plus de logiciels de type middleware offrent une interface de recherche d'information calquée sur les moteurs de recherche Ils reposent sur une indexation et un cache de données Ils peuvent offrir en plus un accès distant de type SOA Architecture HTTP SOA API Moteur de recherche Cache Middleware de persistance Source de données Intérêts du moteur Faciliter l'expression des requêtes Réduire la complexité des requêtes Méta-caractères : * , ? Opérateurs logiques : AND, OR, NOT Plus besoin de connaître un langage de requêtes spécifique à chaque source de données : SQL pour les SGBDR OQL pour les SGBDOO Xquery pour les SGBDXML etc ... Intérêts de la couche SOA Universalise l'interface d'utilisation Permet tous les types de client Permet d'avoir un moteur centralisé Sécurité Maintenance Journalisation Hibernate Search Présentation Search est une surcouche au logiciel Hibernate Il s'appuie sur le logiciel libre Lucene Lucene est un projet du groupe Apache C' est un moteur de recherche Il permet l'indexation Il permet la recherche par mots clefs Lucene n'est pas orienté objet Search adapte Hibernate à Lucene Principes Search indexe le modèle persistant Avec un jeu d'annotations spécifiques Il gère la synchronisation de la base de données avec les indexes Il restaure les graphes d'objets persistants à partir de requêtes faites de mots clefs Architecture Lucene Lucene Projet de la fondation Apache La licence est de type Apache Software licence Porté vers différents langages Delphi, Perl, C#, C++, Python, Ruby, PHP N'est pas un robot d'indexation sur internet Est un moteur d'indexation et de recherche Pour du texte Indépendant du format PDF, HTML, OpenOffice Writer, Microsoft Word, etc. Requêtes Lucene Termes Une requête est composée de termes et d'opérateurs Il y a deux types de terme Les mots et les phrases Un mot est une suite de caractères comme ''bonjour'' Une phrase est un groupe de mots protégés par des quottes Les termes peuvent être combinés à l'aide d'opérateurs booléens Requêtes Lucene Field Un champ est un mot qui identifie un type de terme Une requête peut préciser un champ de recherche et/ou un champ par défaut Exemples (text est ici le champ par défaut) title:"The Right Way" AND text:go title:"Do it right" AND right title:Do it right Requêtes Lucene Méta-caractères Symboles ''?'' et ''*'' Une requête ne peut pas commencer par un wildcard Exemples te?t test* te*t Requêtes Lucene Recherche approximative Symboles ''~''' Exemples roam~ Résulats : foam, roams, etc Requêtes Lucene Recherche d'un ensemble de valeurs Valeurs numériques mod_date:[20020101 TO 20030101] Valeurs alphanumériques title:{Aida TO Carmen} Requêtes Lucene Opérateurs booléens AND "jakarta apache" AND "Apache Lucene" Le résultat doit contenir le +mot +jakarta lucene + Doit contenir jakarta et peut ou non contenir lucene OR "jakarta apache" jakarta "jakarta apache" OR jakarta Requêtes Lucene Opérateurs booléens NOT Groupe d'expressions "jakarta apache" NOT "Apache Lucene" (jakarta OR apache) AND website title:(+return +"pink panther") Protection des caractères spéciaux + - && || ! ( ) { } [ ] ^ " ~ * ? : \ Avec le symbole ''\'' Requêtes Lucene Augmenter le poids d'un terme pour la recherche Symbole ''^'' Utilisation terme^valeur entière (valeur > 1) Par défaut un terme a un poids de 1 Exemple jakarta apache jakarta^4 apache "jakarta apache"^4 "Apache Lucene" Indexation avec Search Indexation avec Search Indexation automatique prise en charge par Search Pour chaque entité capable de persistance Lorsqu'elle devient persistante Lors d'une mise à jour Lorsqu'elle est supprimée Les données déjà présentes dans la base de données ne sont pas prises en charge Il faut donc une indexation « manuelle » Entité persistante indexée Avec les annotations Search Entité indexée Choix d'un critère d'unicité @Indexed @DocumentId Champ persistant indexé @Field Exemple de classe indexée @Entity @Table(name="TABLIVRE") @Indexed public class Livre { @DocumentId private int id; @Field(index=Index.TOKENIZED, store=Store.NO) private String titre; Exemple d'indexation manuelle EntityManager em = entityManagerFactory.createEntityManager(); FullTextEntityManager fullTextEntityManager = Search.createFullTextEntityManager(em); List books = em.createQuery("from Book as book").getResultList(); for (Book book : books) { fullTextEntityManager.index(book); } tx.commit(); //index are written at commit time Requêtes avec Search Gérant de requête FullTextEntityManager Paramètres de la requête Les indexes, l'analyseur Au constructeur d'un objet QueryParser Requêtes avec Search Construire la requête Lucene Méthode parse() sur un objet QueryParser La méthode parse() a pour arguments les mots clefs de la recherche Exécuter la requête Méthode createFullTextQuery(QueryParser) Invoquée sur l'objet FullTextEntityManager Exemple de recherche public static void main(String[] args) { try { EntityManagerFactory emf = Persistence.createEntityManagerFactory("manageur"); EntityManager em = emf.createEntityManager(); FullTextEntityManager fem = Search.createFullTextEntityManager(em); QueryParser parseur = new QueryParser("titre", new StandardAnalyzer()); Query lucenequery = parseur.parse("voyage*"); javax.persistence.Query ftq = fem.createFullTextQuery(lucenequery); List<Livre> livres = ftq.getResultList(); for(Livre instance : livres) System.out.println(instance.getTitre()+" - " +instance.getAuteur().getPrenom()+" "+instance.getAuteur().getNom()); } } catch (ParseException e) {e.printStackTrace(); Indexation relative Portée Pour les classes embarquées Pour les classes en relation Les objets sont indexés comme partie de l'indexe de l'entité racine. Exemple entité racine Livre entité associée * indexe principale 1 Auteur indexe associé Exemple (suite) @Entity @Table(name="TABLIVRE") @Indexed public class Livre { @DocumentId private int id; @Field(index=Index.TOKENIZED, store=Store.NO) private String titre; private Auteur auteur; @Id @GeneratedValue public int getId() {return id;} @ManyToOne(cascade=CascadeType.PERSIST) @IndexedEmbedded public Auteur getAuteur() {return auteur;} ... @Entity @Table(name="TABAUTEUR") public class Auteur { @Id @GeneratedValue private Integer id; private String nom; @Field(index = Index.TOKENIZED, store = Store.YES) public String getNom() {return nom; } ... L'indexe @DocumentId de Livre contient le champ titre et aussi le champ auteur.nom Ressources Documentation Hibernate Search