Solr : l'introduction

Implémenter Solr dans une application web Java

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
A l'époque où les milisecondes comptent pour tous, pour Google aussi bien que pour les sites plus anonymes, l'optimisation des moteurs de recherche est devenue un enjeu majeur. L'un des projets de la fondation Apache, Lucene, répond parfaitement aux attentes des développeurs. Et son serveur écrit en Java, Solr, est synonyme d'un vrai gain des performances - aussi bien qu'au niveau de la mise en place qu'au niveau des résultats.

Ce premier article sera l'ouverture d'une série consacrée au déploiement et à l'utilisation du Solr. Dans un premier temps, on présentera le fonctionnement de Lucene. Ensuite on abordera le projet Solr, en expliquant ses principes de fonctionnement. A la fin on présentera le projet dont on s'occupera au cours des articles suivants.

Qu'est-ce que c'est Apache Lucene ?
Il s'agit d'un projet de la fondation Apache qui fournit un moteur de recherche rapide et scalable, basée sur l'indexation clé-valeur. Développé depuis 2000, le projet a atteint sa maturité en plein sens du terme. Il est composé de plusieurs concepts qu'on retrouvera après dans Solr :
- documents : endroits qui contiennent les termers recherchés.
- score : signifie pertinence des résultats.
- champ : (field) correspond à une portion de données qui compose chaque document. Le champ peut ensuite être pris en compte lors de la recherche.
- terme : (term) similaire au champ, il définit une chaîne de caractères recherchée dans les documents pendnat la recherche.

Comment se déroule le processus d'indexation sous Lucene ? Pour commencer, définissons d'abord la notion d'indexation. Si l'on prend l'analogie avec un livre, on s'aperçoit que l'index placé à la fin, facilite beaucoup la recherche. Il regroupe les termes et indique leur localisation dans le livre. L'indexation est exactement la même chose. La seule différence repose dans le fait que les données ne sont pas stockées en leur état brut. Elles sont converties au format qui facilite la recherche performante.

Lucene base son moteur d'indexation sur la structure de données appelée inverted index (indexe inversé). Dans cette structure, les données d'entrée sont sauvegardées dans la mémoire ou sur le disque, sous forme d'un groupe des fichiers d'index. Pour simplifier la définition, on peut dire que les données sont alors stockées sous forme d'une relation entre le terme et son emplacement dans le document. L'exemple de Wikipedia illustre parfaitement cette définition simplifiée :

// Data
T[0] = "it is what it is"
T[1] = "what is it"
T[2] = "it is a banana"

// Inverted index where the first number matches the document (T[0], T[1] or T[2]) and the second one matches term position at this document.
// The term's position starts from 0
"a": {(2, 2)}
"banana": {(2, 3)}
"is": {(0, 1), (0, 4), (1, 1), (2, 1)}
"it": {(0, 0), (0, 3), (1, 2), (2, 0)}
"what": {(0, 2), (1, 0)}


Un autre concept intéressant de Lucene est scoring (performance des résultats). Il est le résultat d'un calcul effectué par deux méthodes :
- Vector Space Model (VSM) of Information Retrieval : plus le terme apparaît dans un document analysé, plus son résultat de performance est élévé.
- modèles probabiliste de pertinence : permet de déterminer la probabilité de présence d'un terme recherché dans un ou plusieurs documents analysés. Ce modèle introduit la notion du poids (probabilité de présence d'un terme dans les documents) que l'on peut retrouver dans la classe abstraite org.apache.lucene.search.Weight de Lucene. La méthode scorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, Bits acceptDocs) retourne l'instance de la classe org.apache.lucene.search.Scorer, utilisée pour représenter la performance des résultats.
- modèles de language : permet de déterminer la probabilité de présence d'un mot dans le document. En analysant les mots, il effectue un calcul dont la somme devrait être égale à 1. Plus le résutlat est proche de 1, plus le document est considéré comme correspondant à la recherche initiale. On retrouve cette notation dans l'attribut quand on effectuera des recherches dans Solr.

Qu'est-ce que c'est Solr ?
Solr est un autre projet de la fondation Apache, fortement lié au Lucene. Il s'agit d'un serveur développé en Java qui peut être déployé dans n'importe quel conteneur des servlets (Tomcat, Jetty...). Il utilise Lucene pour le travail d'indexation et de recherche, et expose ces fonctionnalités grâce à une web service très ressemblant à l'architecture REST. Une fois déployé, il peut donc être utilisé par les applications écrites dans tous les langages du monde informatique. L'échange des résultats se fait via des formats standards : XML, JSON, CSV ou les fichiers binaires.

Les fonctionnalités principales de Solr, à part l'indexation et la recherche, sont :
- la recherche "filtrée" : (faceted search) qui permet d'effectuer une recherche pour une catégorie donnée
- la recherche géo-localisée qui permet de retrouver les documents par rapport à leur distance géographique
- la prise en charge des documents binaires, comme .pdf ou .doc
- l'intégration facile aux bases de données, avec un simple fichier de mapping XML
- le blacklistage des termes
- l'utilisation d'un dictionnaire des synonymes
- l'implémentation d'un vérificateur des mots (spellchecker) qui peut être utile pour, par exemple, mettre en place un mécanisme du type "Did you mean beurre ?" pour le mot tapé beurr sur Google

Présentation du projet
Le projet qui nous servira à apprendre Solr sera une simple base de données contenant tous les pays du monde avec leurs villes. En plus, chaque pays aura une liste des voisins. Tout sera compris dans une base de données MySQL et importé directement au Solr. Ensuite, on développera un client qui permettra de requêter le serveur pour retrouver les données, mais aussi pour les mettre à jour. A travers ces opérations on verra quelques façons de le faire.

On présentera également des fonctionnalités supplémentaires de Solr, comme déjà mentionnées : faceted search, blacklistage des mots, la gestion des synonymes ou le spellchecker. Toutes les informations sur les villes ont été trouvées sur Geonames.
Bartosz KONIECZNY 08-09-2013 16:29 Solr
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil Symfony2

Comment personnaliser l'affichage des champs du formulaire ?

Pour personnaliser l'affichage des champs du formulaire sous Symfony2 on doit surcharger le paramètre nommé templating.helper.form.class. Par défaut il s'agit de la classe Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper. Le code se présente ainsi :

<?xml version="1.0" encoding="UTF-8" ?>
  <container xmlns="http://symfony.com/schema/dic/services"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <parameters>
      <parameter key="templating.helper.form.class">Frontend\FrontBundle\Helper\FormHelper
    </parameters>
  </container>
La classe surchargée va, avant d'afficher le champ, faire appel à la méthode setTheme(). Tout cela pour déterminer quel thème doit utiliser Symfony2 pour le rendu du formulaire (champ text, boutons radio etc.). Le code de cette FormHelper se présente ainsi :
namespace Frontend\FrontBundle\Helper;

use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Util\FormUtil;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper as ParentHelper;

class FormHelper extends ParentHelper
{
    protected $engine;

    protected $varStack;

    protected $context;

    protected $resources;

    protected $themes;

    protected $templates;

    protected function renderSection(FormView $view, $section, array $variables = array())
    {
        // if new theme is set (for exemple for override form templates)
        if(isset($variables['attr']['theme'])) $this->setTheme($view,  $variables['attr']['theme']);
        return parent::renderSection($view, $section, $variables);
    }

}
Afin de personnaliser un champ du formulaire sous Symfony2 il faut passer un paramètre theme qui appelera la fonction setTheme. Voici un exemple :
// it will show the template located ad /app/Resources/views directory
echo $view['form']->widget($form['replyType'], array('attr' => array('theme' => array(0 => ':'))));
Pour voir comment on peut personnaliser ces champs, veuillez vous référer aux fichiers placés dans /vendor/Symfony/Bundle/FrameworkBundle/Resources/views/Form.