mardi 23 avril 2013

Solr : Améliorer l'indexation depuis un SGBD (Part 1)


Dans cette série d'articles, je vais vous montrer comment optimiser un maximum l'indexation de documents Solr depuis une base de données.

Objectif annoncé : passer de 15 docs/s à + de 1000.

Dans cette première partie, je vais aborder l'approche classique d'indexation Solr, le DataImport, ce qui préparera le terrain pour la suite.

L'idée étant de faire un moteur de recherche de clients à partir d'une GRC. Nous devrons pouvoir rechercher un client par un certain nombre de critères tels que la raison sociale, le téléphone, le siren, l'email et la/les adresse(s).


Après avoir récupéré la dernière version stable connu à ce jour (la 4.2.1 en l’occurrence), jetons-nous sur la configuration du schéma fourni en exemple par Solr afin de couvrir nos besoins.

Ouvrez le fichier "$SOLR_HOME/example/solr/collection1/conf/schema.xml" avec votre éditeur de texte préféré et ajouter les lignes ci-dessous en dessous de field name="id" par exemple:
<field name="raison_sociale" type="text_general" indexed="false" stored="true"/>
<field name="siren" type="text_general" indexed="false" stored="true"/>
<field name="tel" type="text_general" indexed="false" stored="true"/>
<field name="email" type="text_general" indexed="false" stored="true"/>
<field name="adresse" type="text_general" indexed="false" stored="true" multiValued="true"/>

Nous avons mis multiValued="true" au champ adresse car nous pouvons avoir plusieurs adresses par client.
Nous avons mis pour chaque nouveau champs l'attribut stored="true" de manière à pouvoir exploiter ces champs lors de la restitution d'une recherche.
Par contre pourquoi indexed="false" ??
Car nous voulons pas faire de recherche sur un champ spécifique (le SGBD le fait très bien) mais une recherche globale sur tous les critères ci-dessus.
Pour cela, nous allons copier chaque champ susnommé dans un champ prénommé "text" (nom donné dans le fichier de conf par défaut) qui lui sera "indexable". En plus c'est le champ de recherche par défaut.

Allons-y, toujours dans le même fichier repérer les tags copyField et ajoutez les vôtres  :
<copyField source="raison_sociale" dest="text"/>
<copyField source="siren" dest="text"/>
<copyField source="tel" dest="text"/>
<copyField source="email" dest="text"/>
<copyField source="adresse" dest="text"/>

Ça y est notre schéma est fin prêt lançons le serveur Solr.
java -jar $SOLR_HOME/example/start.jar

Vérifions le bon fonctionnement du serveur : http://localhost:8983/solr/#/collection1

OK nous avons un bel index complètement vide ! Wouhou ! C'est déjà ça !!

Passons à l'indexation des données telle que décrite sur le wiki Solr : via un DataImportHandler.
Pour cela il faut déclarer un requestHandler dans le fichier "$SOLR_HOME/example/solr/collection1/conf/solrconfig.xml" :

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
    <lst name="defaults">
      <str name="config">$SOLR_HOME/example/solr/collection1/conf/data-config.xml</str>
    </lst>
</requestHandler>
Il faut remplacer $SOLR_HOME par votre répertoire Solr évidemment ! ;p

Ensuite il faut créer au même endroit un fichier data-config.xml qui contiendra les lignes suivantes :
<dataConfig>
    <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/lucene" user="lucene" password="lucene" />
    <document>
        <entity name="item" query="select   reference as id, 
                                            trim(concat(IFNULL(nom, ''), ' ',IFNULL(prenom, ''))) as raison_sociale,
                                            cl_tel as tel,
                                            cl_email as email,
                                            cl_info_comp24 as siren
                                    from clients">
            <field column="id" name="id" />
            <field column="raison_sociale" name="raison_sociale" />
            <field column="tel" name="tel" />
            <field column="email" name="email" />
            <field column="siren" name="siren" />
         </entity>
    </document>
</dataConfig>
NB : Je ne vais pas vous donner ma base de données pour faire vos tests, mais je suis sûr que vous en avez une adaptable sous la main pour essayer.
Éteignez le serveur Solr, copier les jars suivants dans "$SOLR_HOME/example/solr-webapp/webapp/WEB-INF/lib" :

  • solr-dataimporthandler-4.2.1.jar
  • solr-dataimporthandler-extras-4.2.1.jar
  • mysql-connector-java-5.1.0-bin.jar (le driver jdbc mysql)
Démarrons le serveur et testons le module d'import : http://localhost:8983/solr/#/collection1/dataimport
Suivez les étapes numérotées.


OK pas mal 15s pour 134000 clients.

Ajoutons les adresses maintenant...
"$SOLR_HOME/example/solr/collection1/conf/data-config.xml"
<dataConfig>
    <dataSource driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/lucene" user="lucene" password="lucene" />
    <document>
        <entity name="item" query="select   reference as id, 
                                            trim(concat(IFNULL(nom, ''), ' ',IFNULL(prenom, ''))) as raison_sociale,
                                            cl_tel as tel,
                                            cl_email as email,
                                            cl_info_comp24 as siren
                                    from clients">
            <field column="id" name="id" />
            <field column="raison_sociale" name="raison_sociale" />
            <field column="tel" name="tel" />
            <field column="email" name="email" />
            <field column="siren" name="siren" />
            <entity name="adresses"
                    query="select reference as id,
                            trim(concat(
                              ifnull(cll2_str3,''), ' ',
                              ifnull(cll2_str4,''), ' ',
                              ifnull(cll2_str5,''), ' ',
                              ifnull(cll2_str6,''), ' ',
                              ifnull(cll2_str7,'')
                            )) as adresse
                            from cl_link2 
                            where reference=${item.id}">
                    <field name="adresse" column="adresse" />
            </entity>

         </entity>
    </document>
</dataConfig>
Redémarrez le serveur et relancer l'import ...

Ouch ! 15 docs/s .... soit environ 2 heures et demi pour faire un full-import de mes 134000 clients.
J'ai arrêté l'import, je ne suis pas très patient ;p

En fait pour chaque occurrence de la première requête, il va exécuter la sous-requête des adresses.
De plus pour l'article j'ai restreint le nombre de champs multivalués que je voulais insérer dans mon index... je vous laisse imaginer le temps que cela prendrait avec 2 ou 3 autres sous-requête.

C'est là où le bas blesse pour moi avec le module standard d'intégration. Dans les faits, on peut faire un full-import très long une bonne fois pour toute, et intégrer le delta à fréquence régulière. Il faut créer des "deltaQuery" dans le fichier data-config.xml et jouer avec les dates de modification etc.

Pour ma part, je préfère savoir que je peux reconstruire l'index à tout moment en quelques minutes.

C'est ce que je m'apprête à vous montrer dans un prochain billet. L'indexation Solr via Talend et SolrJ.

@ bientôt !

Aucun commentaire:

Enregistrer un commentaire