Nicolas Penin
Lucene Persistence Engine pour Evaluant Universal Storage Services
Suite à l'article de Laurent Kempé, voici un moteur de stockage pour EUSS permettant l'indexation d'entités métier avec Lucene.
Par Nicolas Penin publié le 01/06/2008 à 23:38, lu 1672 fois, 4 pages
 2 | EUSS
Euss, comme je l'ai déjà précisé est un outil persistance capable de faire du mapping objet-relationnel. Ainsi, on peut se baser sur une source de données SQL Server et changer pour Oracle ou Db2 en une ligne de code. Ce changement se fait au niveau du fichier de configuration. Euss utilise des moteurs de persistance fournissant les implémentations des logiques de stockage. On retrouve donc plusieurs implémentations:
 
/content/897cdedc-11d9-4ae9-87fd-5e50db853b79/Persistence Engines.png
 
Le Lucene Persistence Engine vient s'ajouter à la liste des moteurs de persistance déjà présents en utilisant Lucene comme indexeur ou bien comme simple moteur de sauvegarde.
Avant d'entrer dans les détails, je vous propose un petit exemple d'illustration de Euss, ainsi que la description de son fonctionnement.
Il y a deux objets principaux que l'on manipule avec euss : l'ObjectService et l'ObjectContext. Le premier est celui qui va servir de Factory au second. Ainsi, on a besoin d'un seul ObjectService dans une application. Typiquement, l'ObjectContext est relatif à une transaction. Voici l'exemple d'une application Windows Forms:

public partial class Form1 : Form

{

    private static ObjectService os = new ObjectService();

    private ObjectContext oc;

 

    public Form1()

    {

        InitializeComponent();

        oc = os.CurrentObjectContext;

        oc.InitializeRepository();

        CreateArticles();

        oc = null;

    }

 

    private void CreateArticles()

    {

        #region Creating authors

 

        Person nicolas = new Person();

 

        nicolas.FirstName = "Nicolas";

        nicolas.LastName = "Penin";

 

        nicolas.Company = new Company();

        nicolas.Company.Name = "Evaluant";

 

        Person noham = new Person();

        noham.FirstName = "Noham";

        noham.LastName = "Choulant";

 

        noham.Company = nicolas.Company;

 

        Person laurent = new Person();

        laurent.FirstName = "Laurent";

        laurent.LastName = "Kempé";

        laurent.Company = new Company();

        laurent.Company.Name = "Innoveo";

 

        #endregion

 

        Article article = new Article();

        article.Content = ".NET Source Code is here";

        article.Title = ".NET Source code";

        article.Author = laurent;

 

        oc.BeginTransaction();

        oc.Serialize(article);

        oc.CommitTransaction();

 

        article = new Article();

        article.Content = @"La communication entre les hommes et femmes évolue, celle des

            applications aussi grâce au .Net Framework 3.0.";

        article.Title = "WCF";

        article.Author = noham;

 

        oc.BeginTransaction();

        oc.Serialize(article);

        oc.CommitTransaction();

 

        article = new Article();

        article.Content = "Euss and Lucene have met and gave birth to me";

        article.Title = "When Euss meet Lucene";

        article.Author = nicolas;

 

        oc.BeginTransaction();

        oc.Serialize(article);

        oc.CommitTransaction();

    }

 

    private void button1_Click(object sender, EventArgs e)

    {

        oc = os.CreateObjectContext();

        // Recherche des Articles

        var articles = from Article article in oc

                       select article;

        // Chargement anticipé de l’entité Author liée

        articles.Infer(a => a.Author);

        // Si un texte est entré, on l’utilise pour filtrer les Articles

        // Dans cet exemple, la recherche est automatiquement effectuée par Lucene

        // plutôt que par le moteur de base de données

        if (!string.IsNullOrEmpty(textBox1.Text))

            articles.Where<Article>(a => a.Content.Contains(textBox1.Text));

        dataGridView1.DataSource = articles.ToList<Article>();

        dataGridView1.AutoResizeColumns();

    }

}

La méthode button1_Click permet de rechercher avec une requête LINQ des articles contenant un texte spécifique. Si par exemple, je ne cherche aucun contenu spécifique, voici le résultat :
 
/content/897cdedc-11d9-4ae9-87fd-5e50db853b79/LPE_result.png
 
L'indexer persistence engine est un moteur qui va encapsuler deux autres moteurs au choix, un pour la recherche, et un autre pour la reconstruction des données. Ainsi on obtient le modèle suivant
  1. Un permettant l'indexation
  2. Un permettant la sauvegarde/reconstruction des objets
 
/content/897cdedc-11d9-4ae9-87fd-5e50db853b79/IndexerArchitecture.png
 
Avant de pouvoir l'utiliser comme simple indexeur, il a fallu développer le Persistence Engine comme n'importe quel autre moteur. C'est pendant cette phase que je me suis aperçu qu'il manquait certaines fonctionnalités dans Lucene auxquelles il a fallu palier. Par exemple, les fonctions d'agrégation et les requêtes imbriquées, n'existent pas. Ces fonctionnalités ont donc été implémentées dans le Persistence Engine. La principale restriction sur ce moteur est l'impossibilité d'utiliser des fonctions d'agrégation dans des contraintes. Par exemple, il n'est pas possible de récupérer les Personnes dont le nombre de Partenaires est égal à 2. De même il n'est pas possible de comparer deux attributs. Par exemple, on ne peut pas récupérer les Personnes dont le Prénom est égal au Nom. A l'heure actuelle, deux options de paramétrage sont possibles :
  1. Le choix du mode synchrone ou asynchrone.
    Avec ce mode, il est possible de choisir si le thread courant doit attendre la fin de l'indexation pour continuer son exécution.
  2. Le choix de stocker tous les attributs, ou uniquement ceux nécessaires à l'indexation.
    Si les attributs ne sont pas mémorisés, seuls les identifiants des références seront sauvegardés ainsi que les identifiants et le type des entités elles-mêmes. Ce choix est utile uniquement si vous souhaitez utiliser le LucenePersistenceEngine comme indexeur et non comme moteur de persistance.
Pour l'instant, il n'est pas possible de choisir quels attributs seront indexés ou non, ou leur importance (boost dans Lucene). Dans un futur proche, il existera un fichier semblable à un fichier de mapping, permettant de combler ce manque. Il sera également possible de choisir l'architecture que l'on veut pour les Directory. En effet, Lucene fonctionne sur le principe de Directory, il en existe 2 :
  1. RAMDirectory
  2. FSDirectory
Comme leur nom l'indique, l'un est basé en RAM, et l'autre sur un système de fichiers. L'avantage est que l'on peut baser le RAMDirectory sur le FSDirectory, on a ainsi un cache en mémoire de l'index. On pourra donc avoir le choix d'utiliser ou non le cache en RAM, et surtout quand et comment basculer le tout sur le FileSystem (temporisation, ou nombre de documents, ou encore, une fois toutes les commandes effectuées). Après toutes ces descriptions, le mieux est de laisser la place à un exemple d'utilisation simple, mais permettant de voir une utilisation possible d'un tel moteur.
 
» Démarrer une discussion
 
Discussion démarée par stiiifff le 02/06/2008 à 09:38, 1 commentaire(s).