Solr : importation depuis la base de données

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
Dans l'article précédent était consacré à l'introduction au Solr. Maintenant, quand on sait de quoi on va parler, on peut aborder l'aspect plus concret de ces technologies, l'import des données.

A travers l'article ci-dessus on présentera une méthode simple d'importation des données comprises dans la base de données au Solr. Il montrera également quelques commandes qu'on peut utiliser dans la transmission des informations au Solr.

Importer une base de données au Solr
L'un des outils de Solr permet d'importer les données directement depuis la base de données au serveur. En plus, cette procédure se fait uniquement à travers les fichiers de configuration, sans aucune intervention supplémentaire du développeur. Cet outil puissant s'appelle DataImportHandler. On peut le retrouver dans la balise suivante de notre fichier de configuration :

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">./data-config.xml</str>
</lst>
</requestHandler>


Par rapport à d'autres gestionnaires de la requête, celui-ci se différencie par la présence d'un fichier de configuration, caché dans la balise <str name="config" />. Il pointe vers le fichier qui décrit la relation entre les champs stockés dans le schéma et ceux sauvegardés dans la base de données.

Fichier de configuration pour Data Import Handler
Ce fameux fichier de configuration est composé de parties suivantes :


  • <dataSource />
    Cette balise indique le type de la source de données, avec toutes ses données d'accès.


  • <document />
    Cet élément se rapporte au document dans les indexes Lucene. Il est décrit par balise <entity /> qui contient une suite des champs. Ces champs sont marqués par la balise <field />. Chaque champ doit posséder un attribut column qui le lie à la colonne dans la base de données. Un autre attribut obligatoire, name, lie le champ de la base au champ spécifié dans le fichier avec le schéma. Cette valeur sera donc utilisée dans toutes les requêtes vers Solr.
    Comment Solr connaît la façon de récupérer ces informations relatives à la balise <field /> ? Grâce à l'attribut query de la balise <entity />, il sait comment interpréter le résultat de la requête vers la base de données.



Grâce à cette description, Solr fait la relation entre ce qui se trouve dans la base de données et ce qui devrait se trouver dans les indexes Lucene.

Commandes d'importation
Une fois définie, le fichier de configuration ne sert à rien si l'on ne connaît pas des commandes d'importation. Le gestionnaire de requête indique que le DIH sera appelé à chaque accès à l'URL /dataimport. Cependant, un appel vierge ne servira à rien car Solr ne saura pas quel type d'import exécuter. C'est pourquoi dans l'URL d'appel il faut rajouter le paramètre command. Il peut prendre comme valeur une des commandes suivantes :
- full-import : l'opération concerne l'importation complète qui commence dans un nouveau thread. Si un index existe déjà, il est recrée. On peut cependant surcharger ce comportement par défaut, en spécifiant dans les paramètres URLs clean=false. Cela provoquera que l'ancien index sera préservé.

- delta-import : il s'agit d'une opération qui permet de mettre à jour les documents Solr. Cette opération est très utile dans la situation où notre index est énorme et une réimportation complète prendrait beaucoup de temps. On décrit cette méthode d'indexation en tant que l'indéxation incrémentale. Cependant, le fichier de configuration (notre data-config.xml) n'est pas le même car il doit comporter une information qui permet de déterminer si une entrée doit être actualisée. La requête d'importation incrémentale est alors spécifiée par 2 attributs deltaQuery et deltaImportQuery. La première récupère de nouvelles entrées en se basant sur la date de la dernière importation exprimée par la variable ${dih.last_index_time}. Ensuite elle retourne un identifiant sous la variable ${dih.delta.id} qui est utilisé dans la requête deltaImportQuery pour retrouver une nouvelle entrée.

- reload-config : grâce à cette commande on peut recharger la configuration sans forcément redémarrer Solr.

- abort : cette commande permet d'arrêter l'opération en cours.

On peut également connaître l'état de l'importation, en appelant tout simplement /dataimport (sans paramètre command). Il retourne, entre autres, le nombre de documents créés et supprimés ou le nombre de requêtes en cours.

Voici ce que donnent ces commandes :


  • http://localhost:8080/world/dataimport?command=full-import

    <response>
    <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">0</int>
    </lst>
    <lst name="initArgs">
    <lst name="defaults">
    <str name="config">./data-config.xml</str>
    </lst>
    </lst>
    <str name="command">full-import</str>
    <str name="status">idle</str>
    <str name="importResponse"/>
    <lst name="statusMessages">
    <str name="Total Requests made to DataSource">1</str>
    <str name="Total Rows Fetched">252</str>
    <str name="Total Documents Skipped">0</str>
    <str name="Full Dump Started">2013-07-17 19:47:36</str>
    <str name="">
    Indexing completed. Added/Updated: 252 documents. Deleted 0 documents.
    </str>
    <str name="Committed">2013-07-17 19:47:41</str>
    <str name="Total Documents Processed">252</str>
    <str name="Time taken">0:0:5.537</str>
    </lst>
    <str name="WARNING">
    This response format is experimental. It is likely to change in the future.
    </str>
    </response>

    D'après les informations comprises dans ce XML, on s'aperçoit que 252 documents ont été ajoutés et aucun supprimé. Le status à 0 signifie que l'importation s'est déroulée sans aucun problème.


  • http://localhost:8080/world/dataimport?command=delta-import
    Entre temps, juste pour les besoins de notre test, on a rajouté une nouvelle colonne dans notre table countries. Cette colonne, nommée last_change, stocke un TIMESTAMP avec la dernière opération effectuée sur un pays. On y rajoute également un nouveau pays "test", dont la date de la dernière modification est supérieure à la date du dernier import. En plus, on crée des requêtes delta dans le fichier de configuration : deltaQuery="SELECT id FROM countries WHERE last_change > '${dih.last_index_time}'" et deltaImportQuery="SELECT * FROM countries WHERE id='${dih.delta.id}'". La réponse de Solr est alors :

    <response>
    <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">1</int>
    </lst>
    <lst name="initArgs">
    <lst name="defaults">
    <str name="config">./data-config.xml</str>
    </lst>
    </lst>
    <str name="command">delta-import</str>
    <str name="status">idle</str>
    <str name="importResponse"/>
    <lst name="statusMessages"/>
    <str name="WARNING">
    This response format is experimental. It is likely to change in the future.
    </str>
    </response>

    Si l'on analyse la réponse pour la demande d'état du dernier import, on s'apercevra qu'un document a été mis à jour :

    <response>
    <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">0</int>
    </lst>
    <lst name="initArgs">
    <lst name="defaults">
    <str name="config">./data-config.xml</str>
    </lst>
    </lst>
    <str name="status">idle</str>
    <str name="importResponse"/>
    <lst name="statusMessages">
    <str name="Total Requests made to DataSource">2</str>
    <str name="Total Rows Fetched">2</str>
    <str name="Total Documents Skipped">0</str>
    <str name="Delta Dump started">2013-07-18 07:14:47</str>
    <str name="Identifying Delta">2013-07-18 07:14:47</str>
    <str name="Deltas Obtained">2013-07-18 07:14:48</str>
    <str name="Building documents">2013-07-18 07:14:48</str>
    <str name="Total Changed Documents">1</str>
    <str name="">
    Indexing completed. Added/Updated: 1 documents. Deleted 0 documents.
    </str>
    <str name="Committed">2013-07-18 07:14:49</str>
    <str name="Total Documents Processed">1</str>
    <str name="Time taken">0:0:1.59</str>
    </lst>
    <str name="WARNING">
    This response format is experimental. It is likely to change in the future.
    </str>
    </response>




Dans cet article on a pu voir que le transfert des données depuis une base de données au Solr est presqu'un jeu d'enfant. On s'est aperçu que l'importation est une question de quelques commandes et un fichier de configuration. Cependant, il ne s'agit pas d'un seul moyen d'envoyer les informations au Solr. Dans l'article suivant on verra comment importer les données via HTTP.
Bartosz KONIECZNY 08-09-2013 17:16 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

Un problème avec la définition des valeurs par défaut pour input type checkbox ?

Si vous rencontrez un problème avec la définition des valeurs par défaut pour un champ du type checkbox sous Symfony2, assurez-vous de la conformité des types de ces valeurs. Par exemple, le code suivant ne va pas fonctionner (le checkboxes ne seront pas sélectionnés pour les valeurs indiquées) :

  private $gifts = array(1 => 'apple', 2 => 'orange');
  public function setPreferedGifts($value = array())
  {
    $vals = array();
    foreach($value as $v => $val)
    {
      $vals[] = $val;
    }
    $this->preferedGifts = $vals;
  }
Par contre, le code suivant fonctionnera correctement (les checkboxes seront sélectionnés pour des valeurs passées dans la boucle foreach) :
  private $gifts = array(1 => 'apple', 2 => 'orange');
  public function setPreferedGifts($value = array())
  {
    $vals = array();
    foreach($value as $v => $val)
    {
      $vals[] = (int)$val;
    }
    $this->preferedGifts = $vals;
  }
Pour résumer, si le tableau avec les choix ($gifts dans l'exemple) contient les clés qui sont des integers, les valeurs par défaut doivent aussi être des integers.