Jean-Luc NGUYEN : Nouvelle extension eZ Publish : PM Compress

Nouvelle contribution : http://projects.ez.no/pm_compress

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jean-Luc NGUYEN le 23/07/2010 14:59

Jean-Luc NGUYEN : eZ Publish VS. bug : la checklist

Lorsqu’on a un bug sur eZ Publish, de quelconque nature, j’ai noté au fur et à mesure les raisons possibles et les outils pouvant aider un développeur à le corriger (en mode développement bien sûr).

  • Utiliser le plugin Firebug pour parcourir le code source et vérifier qu’il affiche bien ce qu’il doit afficher.
  • Vérifier qu’une ressource n’est pas dans le cache navigateur en vidant ce dernier ou le désactiver.
  • Vider / purger le cache eZ Publish, voire même supprimer les répertoires cache physiquement.
  • Regarder les logs Apache.
  • Regarder les logs access.
  • Regarder les logs eZ Publish dans le répertoire var/log/*.
  • Vérifier que les répertoires et fichiers ont les bons droits.
  • Vérifier que l’affichage css / javascript / html est bien identique entre les templates HTML et l’intégration dans les templates eZ Publish.
  • Vérifier que les fichiers *.ini personnalisés ont les bonnes valeurs en dév (http://projects.ez.no/noveniniupdate).
  • Activer le mode debug eZ Publish.
  • Activer le mode debug eZ Publish et afficher les requêtes SQL exécutées.
  • Vérifier qu’il n’y a pas d’erreur javascript.
Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jean-Luc NGUYEN le 07/07/2010 08:45

Jérôme Vieilledent : Slides de ma présentation eZConference 2010

Voici comme promis mes slides ainsi que le topic sur le forum de Share pour continuer à en discuter

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jérôme Vieilledent le 28/06/2010 07:57

Gilles Guirand : eZ Conference 2010 slides about eZ Find 2.2 customization & advanced development

eZ Conference - eZ Find cover slides

On June 24th, at the 2010 eZ Conference developer track, I proposed a talk about eZ Find 2.2 customization & advanced development. This talk illustrate and summarize my eZ Find articles / tutorials you'll find below, with new schemas and real project exemples. Thanks to all the eZ Community for their interest and questions during the conference. I hope to have time to write one of these requested tutorials in coming month.

I'd like to thanks Nicolas Pastorino for the english translation of my articles on share.ez.no.

Download the PDF version, or browse the slideshare version.

Slideshare version

Articles / tutorials about eZ Find

French articles about eZ Find on gandbox.fr :

English articles about eZ Find (translation from French), on share.ez.no :

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Gilles Guirand le 27/06/2010 17:34

Jean-Luc NGUYEN : Share.ez.no, concours de re-design

Le gagnant du concours de re-design du site http://share.ez.no est connu. Toutes les infos ici :
http://share.ez.no/blogs/share.ez.no-team/ez-design-contest-and-the-winner-is

Il ne « reste » plus qu’à l’implémenter.

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jean-Luc NGUYEN le 25/06/2010 10:51

Jean-Luc NGUYEN : Live eZ Conference

Toutes les infos du direct sur la eZ Conference : http://share.ez.no/live

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jean-Luc NGUYEN le 22/06/2010 23:24

Jean-Luc NGUYEN : eZ Publish : où placer les siteaccess ?

En voulant corriger un bug sur la prévisualisation des contenus en back-office, j’ai également appris une chose sur la configuration des siteaccess : il est nécessaire de les placer en dehors de l’extension, dans /settings/.

J’ai toujours pensé que c’était une question de « best practice », mais si le répertoire siteaccess se trouve dans /extension/mon_extension/settings/, la prévisualisation ne fonctionne pas.

Il y a d’ailleurs un bug datant de 2006, patché récemment : http://issues.ez.no/IssueView.php?Id=9903

Ce patch ne fonctionne qu’à moitié, on va bien chercher le siteaccess du front-office pour la prévisualisation, mais l’affichage ne prend en compte que le template générique full.tpl, sans passer par les règles du fichier override.ini.append.php.

A savoir donc.

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Jean-Luc NGUYEN le 14/06/2010 12:26

Gilles Guirand : Développement avancé avec eZ Find (partie 3 : Tirer profit de la syntaxe Solr)

eZ Find - Chapter 3

Le billet précédent décrit comment ajouter des champs supplémentaires dans Solr, afin de pouvoir les exploiter avec la syntaxe native d'eZ Find sous la forme 'mycontentclass/mycontentattribute/mycontentsubattribute'.

Cette syntaxe spécifique à eZ Find est certes confortable mais non exclusive, à savoir qu'il est possible de mixer des éléments de syntaxes eZ Find et des éléments de syntaxes Solr, comme par exemple les noms des champs ('attr_myfield_type') ou encore des opérateurs logiques (AND, NOT, etc.).

- OUI c'est une mauvaise pratique. Une syntaxe 'interface' n'est pas faites pour être outrepasser, au risque de compromettre l'évolutivité de la couche basse, à savoir Solr
- OUI cela peut faciliter les développements, voir même sauver la vie du développeur sur certaines situations complexes

Ce billet montre quelques exemples d'exploitation de la syntaxe Solr, volontairement simplifiés pour en faciliter la compréhension.

Faire un tri sur un attribut commun à plusieurs classes

Il s'agit d'un grand classique des problématiques d'eZ Publish, à savoir :

  • On crée 2 classes différentes pour X raisons "Post" et "Article"
  • On ajoute dans chacune des classes des attributs communs, parceque utile dans les 2 cas, par exemple un attribut "Date"

Résultat : Impossible de mélanger les "Post" et "Article" en les triant par dates décroissante (sauf à développer un terrifiant opérateur de template). Généralement, les développeurs font en sorte d'utiliser une seule classe plus générique afin de contourner ou plutôt déplacer le problème (la mutualisation d'une classe peut avoir d'autres inconvénients).

La solution des attributs communs entre les classes avec eZ Find :

Le premier billet de cette série consacré à eZ Find décrit la logique de nommage des champs dans Solr. Une effet secondaire positif de cette convention (lié au concept de dynamicfields de Solr) est l'absence bienheureuse de l'identifiant de la classe dans le nom de chaque champs. Il est ainsi possible d'exploiter les champs homonymes comme bon nous semble, au travers de recherche, filtres ou tris en fonction du besoin.

Exemple de code de template eZ Publish sur la seule classe "Post" :

{def $search_result = fetch( 'content', 'list', hash( 'parent_node_id', 2,
    'class_filter_type',  'include',
    'class_filter_array', array(24),
    'sort_by', array( array( 'attribute', false(), 'post/date' ) ),
    'limit', 10,
    'depth', 3
))}
 

Exemple équivalent de code de template eZ Find pour notre problématique de tri inter-classes "Post" et "Article" :

{def $search=fetch( ezfind, search,
     hash( query , '',
           'class_id', array('post', 'article'),
           'limit', 10,
           'sort_by', hash('attr_date_dt', 'desc')
))}
 

A noter : une évolution souhaitable d'eZ Find serait de pouvoir exploiter une syntaxe du type '//date', afin de rendre facultatif l'ajout de la classe dans le filtre Solr généré.

Travailler avec les keywords

Contrairement à l'exemple précédent sur les dates, les keywords disposent dans eZ Publish d'une table externe au contenu (ezkeyword_attribute_link), permettant de lier chaque keyword à divers contenus de diverses classes. Cependant le fetch par keyword ne dispose pas de tous les filtres possibles d'un fetch par list par exemple (class_filter_type, class_filter_array, extended_attribute_filter, etc.). Cette limitation se comprend puisque permettre un filtre inter-classes engendre forcement une limitation sur les fonctionnalités liées aux attributs spécifiques à chaque classes.

Sur le même logique que le traitement par date, on peut donc exploiter eZ Find pour effectuer toutes les opérations nécessaires autour des keywords. Voici un exemple de code :

'filter', array('attr_tags_lk:"ez publish"', 'NOT attr_title_t:"RSS"')
 

Résultat : Retourne uniquement les résultats associés au keyword "eZ Publish" ou "ez publish" (notez l'utilisation du _lk pour lowercase), ne contenant pas "RSS" dans le titre

'filter', array('attr_tags_lk:"ez publish"', 'attr_tags_lk:"mootools"')
 

Résultat : Retourne uniquement les résultats associés à la fois au keyword "eZ Publish" ou "ez publish", et au keyword "Mootools" ou "mootools"

Faire des filtres complexes

Voici quelques exemples de filtres, qui n'ont rien d'exhaustif puisqu'il est possible d'exploiter l'ensemble des opérateurs Lucene en fonction de la version de Solr déployée (version de Solr 1.4, disponible dans eZ Find 2.2 lors de la rédaction de ce billet).

'filter', array('NOT ( attr_title_t:(ez+find) OR attr_intro_t:(ez+find) )')
 

Résultat : Retourne uniquement les résultats qui possèdent l'expression 'ez find' ou 'eZ Find', dans l'attribut 'title' ou l'attribut 'Intro'. Il faut noter l'utilisation de la version 'text' (_t) sur l'attribut 'title' permettant de profiter du non respect de la casse (contrairement au type 'string').

'filter', array('attr_title_s:[A TO G] AND ezf_df_text:google~0.7')
 

Résultat : Retourne uniquement les résultats dont le 'title' commence par A,B,C,D, E ou F (G exclu), et dont le contenu possède approximativement l'expression 'google' (Google, iGoogle, etc.).

  • A noter : Le ratio '0.7' peut être ajuster au besoin
  • A noter : le champs 'ezf_df_text' est un champs constitué dynamiquement et par recopie de tous les autres champs de types 'string', 'text' ou 'keyword'. On peut aussi utiliser le champs 'ezf_sp_words' si la fonctionnalité de spellcheck est exploitable. Voir le fichier schema.xml et la définition de ces champs par copyField pour plus de détail.
Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Gilles Guirand le 23/05/2010 19:17

Gilles Guirand : Développement avancé avec eZ Find (partie 2 : Indexer des champs supplémentaires dans Solr)

eZ Find - Chapter 2

Le billet précédent décrit les mécanismes bas niveaux d'eZ Find, et la façon dont les correspondances entre les attributs eZ Publish (noms, types de champs) et les champs Solr sont gérés. Ce billet décrit comment eZ Find peut considérablement faciliter le développement de certaines fonctionnalités (en évitant de complexes opérateurs de templates aux multiples requêtes SQL...), en ajoutant automatiquement des champs dans Solr lors de l'indexation d'un contenu, ré-exploitables par la suite pour la construction d'une facette par exemple ou pour profiter d'un filtre supplémentaire.

Etude de cas : un filtre par années, et par mois-années

eZ Find - Facettes par dates

L'exemple étudié a surtout une valeur pédagogique, puisqu'il s'agit d'un besoin générique et relativement simple à implémenter. Les listes d'actualités, ou les listes de billets sur les Blogs proposent généralement des filtres par années (2010) ou par mois / année (Janvier 2010) (comme sur ce blog par exemple dans la colonne de droite). Habituellement, pour ce genre de filtre on développe un opérateur de template permettant d'effectuer la requête SQL nécessaire, ce qui peut rapidement devenir très complexe. Il suffit de lire le code de l'opérateur de template eZArchive pour en comprendre les limites.

Il est relativement fréquent que ces manipulations SQL souffrent de carences fonctionnelles, comme la propagation des droits, la gestion des langues, la gestion de certaines subtilités entre MySql ou PostGreSql. Ce sont des problématiques à la charge du développeur, puisque l'on contourne les API pour exploiter directement du SQL. Par ailleurs, l'opérateur eZArchive montre une autre limite importante, puisqu'il se contente de travailler sur la date de publication 'publication_date' (par facilité), et ne permet donc pas d'exploiter un attribut de date spécifique à la classe.

Développer le filtre par années, et par mois-années avec eZ Find

Etape 1 : Indexer les années, mois / année vers Solr

Les settings d'eZ Find (ezfind.ini, à surcharger dans le ezfind.ini.append.php de votre extension) permettent de déléguer le traitement de l'indexation d'un datatype eZ Publish vers une classe PHP :

[SolrFieldMapSettings]
CustomMap[ezdate]=ezfSolrDocumentFieldDate
 

Notre classe PHP, nommée arbitrairement ezfSolrDocumentFieldDate hérite de la classe ezfSolrDocumentFieldBase et doit être ajoutée dans le dossier /extension/myextension/classes/ezfsolrdocumentfielddate.php avec le squelette suivante :

<?php
class ezfSolrDocumentFieldDate extends ezfSolrDocumentFieldBase
{
 public static function getFieldName( eZContentClassAttribute $classAttribute, $subAttribute = null, $context = 'search' )
 {
 // return the fieldname like : attr_mydate_d
 }
 
 public function getData()
 {
 // return the array keys (fieldname => value), like : array('attr_mydate_dt' => '2010-04-30T00:00:00Z')
 }
}
?>
 

Le rôle de la méthode getFieldName

Cette méthode est invoquée pour transformer les noms des attributs eZ Find vers les noms de champs Solr. Ainsi lorsqu'on construit une facette avec la syntaxe 'mycontentclass/mydateattribute', cette méthode reçoit 'mydateattribute' et doit retourner 'attr_mydateattribute_dt'. Nous allons donc implémenter cette fonction de la façon suivante :

  • Si un sous attribut est spécifié (par exemple 'mycontentclass/mydateattribute/year'), alors retourne le nom composé du sous attribut
  • Si aucun sous attribut n'est spécifié, alors exécute le code de la classe parent
  • Important : Pour profiter d'une certaine généricité, et éviter de nommer 'en dur' les champs Solr, nous utilisons les méthodes parents generateSubattributeFieldName et generateAttributeFieldName
const DEFAULT_SUBATTRIBUTE_TYPE = 'date';
 
public static function getFieldName( eZContentClassAttribute $classAttribute, $subAttribute = null, $context = 'search' )
{ 
  switch ( $classAttribute->attribute( 'data_type_string' ) )
  {
    case 'ezdate' :
    {
     if ( $subAttribute and $subAttribute !== '' )
     {
      // A subattribute was passed
      return parent::generateSubattributeFieldName( $classAttribute,
       $subAttribute,
       self::DEFAULT_SUBATTRIBUTE_TYPE );
     }
     else
     {
      // return the default field name here.
      return parent::generateAttributeFieldName( $classAttribute, self::getClassAttributeType( $classAttribute, null, $context ) );
     }
  } break;
    
    default:
    {} break;
  }
}
 

Le rôle de la méthode getData

Cette méthode est invoquée pour extraire les données d'eZ Publish, et les préparer pour leur indexation vers Solr. C'est donc dans cette méthode que l'on peut ajouter nos champs supplémentaires 'year' et 'yearmonth'. Pour faciliter la future exploitation de ces champs avec eZ Find, je souhaite pouvoir construire des facettes ou des filtres selon la syntaxe classique :

  • 'mycontentclass/mydateattribute/year', dont la transposition Solr serait 'subattr_date-year_dt'
  • 'mycontentclass/mydateattribute/yearmonth', dont la transposition Solr serait 'subattr_date-yearmonth_dt'
public function getData()
 {
  $contentClassAttribute = $this->ContentObjectAttribute->attribute( 'contentclass_attribute' );
 
  switch ( $contentClassAttribute->attribute( 'data_type_string' ) )
  {   
  case 'ezdate' :
  {
  $returnArray = array();
   
  // Get timestamp attribute value
  $value = $this->ContentObjectAttribute->metaData();
   
  // Generate the main filedName attr_XXX_dt 
  $fieldName = parent::generateAttributeFieldName( $contentClassAttribute,
  self::DEFAULT_ATTRIBUTE_TYPE );
 
  $returnArray[$fieldName] = parent::convertTimestampToDate( $value );
 
  // Generate the yearmonth subattribute filedName subattr_year_dt
  $fieldName = parent::generateSubattributeFieldName( $contentClassAttribute,
  'year',
  self::DEFAULT_SUBATTRIBUTE_TYPE );
 
  $year = date("Y", $value); // Get Year value : 2010
  $returnArray[$fieldName] = parent::convertTimestampToDate( strtotime($year.'-01-01') );
 
  // Generate the yearmonth subattribute filedName subattr_yearmonth_dt
  $fieldName = parent::generateSubattributeFieldName( $contentClassAttribute,
  'yearmonth',
  self::DEFAULT_SUBATTRIBUTE_TYPE );
 
  $month = date("n", $value); // Get Month value : 3
  $returnArray[$fieldName] = parent::convertTimestampToDate( strtotime($year.'-'.$month.'-01') );
   
  return $returnArray;
   
  } break;
 
  default:
  {} break;
  }
 }
}
 

A noter : $returnArray contient un tableau à clés, dont voici un exemple de sortie (effectuer avec var_dump) :

array(3) {
 ["attr_date_dt"]=>
 string(24) "2008-12-28T00:00:00.000Z"
 ["subattr_date-year_dt"]=>
 string(24) "2008-01-01T00:00:00.000Z"
 ["subattr_date-yearmonth_dt"]=>
 string(24) "2008-12-01T00:00:00.000Z"
}
 

A noter : Solr utilise le format de date ISO 8601, du type '2010-04-30T00:00:00Z'. La classe parent ezfSolrDocumentFieldBase propose la méthode convertTimestampToDate() pour convertir un format timestamp vers un format ISO 8601.

Le template de construction des facettes

Nos données par années et par mois-année sont maintenant disponibles. Il ne reste plus qu'à construire nos facettes avec la syntaxe habituelle :

{def $search_yearmonth=fetch( ezfind, search,
 hash( query , '',
 'facet', array( 
 hash('field', 'billet/date/year', 'sort', 'alpha', 'limit', 20 ),
 hash('field', 'billet/date/yearmonth', 'sort', 'alpha', 'limit', 20 )
 ),
 'class_id', array('billet'),
 'subtree_array', array(2)
 ))}
 
 {def $search_extras_year=$search_yearmonth['SearchExtras'].facet_fields[0].nameList|reverse}
 {def $search_extras_yearmonth=$search_yearmonth['SearchExtras'].facet_fields[1].nameList|reverse}
 {def $date_count = 0
 $date_ts = 0}
 
<li id="blog_block_10" class="colonne_block">
 <h1>Archives par années :</h1>
 <ul class="{$current_css} list">
 {foreach $search_extras_year as $facetID =&gt; $datevalue}
  {set $date_count = $search_yearmonth['SearchExtras'].facet_fields[0].countList[$facetID]}
  {set $date_ts = $datevalue|strtotime}
 <li><a href={concat('/Blogs/(year)/',$date_ts|datetime( 'custom', '%Y' ))|ezurl} title="Archives : {$date_ts|datetime( 'custom', '%Y' )} // {$date_count} Billet(s)">{$date_ts|datetime( 'custom', '%Y' )}</a></li>
 {/foreach}
 </ul>
</li>
<li id="blog_block_11" class="colonne_block">
 <h1>Archives par mois / années :</h1>
 <ul class="{$current_css} list">
 {foreach $search_extras_yearmonth as $facetID =&gt; $datevalue}
  {set $date_count = $search_yearmonth['SearchExtras'].facet_fields[1].countList[$facetID]}
  {set $date_ts = $datevalue|strtotime}
 <li><a href={concat('/Blogs/(year)/',$date_ts|datetime( 'custom', '%Y' ),'/(month)/',$date_ts|datetime( 'custom', '%n' ))|ezurl} title="Archives : {$date_ts|datetime( 'custom', '%F %Y' )} // {$date_count} Billet(s)">{$date_ts|datetime( 'custom', '%F %Y' )|upfirst}</a></li>
 {/foreach}
 </ul>
 
</li>
{undef $date_ts $date_count $search_yearmonth $search_extras_yearmonth}
 

A noter : Le fetch proposé est relativement basique, afin de faciliter la compréhension du mécanisme. Il faut bien sur faire évoluer le code pour obtenir le fonctionnel attendu (utiliser les filtres par exemple), mais ce n'est pas l'objet de ce billet puisque la documentation officielle détaille déjà la façon de procéder.

A noter : le 'sort', 'alpha' ne permet pas réellement de spécifier que l'on souhaite un tri alphabétique. Il s'agit surtout de spécifier que l'on ne souhaite pas un tri par 'count' (nombre d'items associés à la facette). Dans ce cas Solr applique un tri automatique 'croissant' en fonction de son index et du type de donnée (ce qui explique l'utilisation de l'opérateur reverse pour obtenir une liste décroissante).

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Gilles Guirand le 16/05/2010 15:30

Gilles Guirand : Développement avancé avec eZ Find (partie 1 : La gestion des datatypes entre eZ Find & Solr)

eZ Find - Chapter 1

Après 2 billets un peu "rapides" sur eZ Find et la gestion des datatypes, ainsi que l'utilisation des facettes pour construire un nuage de tags, voici le premier billet d'une série de tutoriels à propos d'eZ Find, qui décrivent plus en détail son fonctionnement et son utilisation avancée dans divers contextes. Cette série de tutoriels introduit quelques nouveautés de la version 2.2, sera traduite progressivement sur le share.ez.no, et servira de base pour une conférence de la eZ Conference 2010, ainsi qu'aux Recontres Mondiales du Logiciel Libre 2010

Cet article décrit comment eZ Find transforme et adapte les contenus eZ Publish, et leurs datatypes respectifs pour les indexer dans Solr. La compréhension de ces mécanismes bas niveaux d'eZ Find sont des prérequis indispensables lors des phases de développement et de debug, ne serait ce que pour savoir ou chercher et lire les portions de codes permettant de comprendre le rôle exact d'un settings, d'un paramètre ou d'un filtre.

Etape 1 : Prérequis avant de démarrer

Cet article n'a pas vocation à expliquer à nouveau comment installer et indexer son contenu avec eZ Find. La documentation décrit avec précision les opérations à effectuer. Cependant voici quelques astuces permettant de développer dans de bonnes conditions :

Relancer l'indexation du contenu après une modification

Il est parfois laborieux de relancer une indexation complète juste pour tester l'impact d'une modification mineure. Alors il existe pour cela quelques paramètres cachés dans le script /bin/php/updatesearchindexsolr.php, qui permettent de désigner un noeud parent de départ, ainsi q'une limite (attention les 3 paramètres combinés sont nécessaires) :

php extension/ezfind/bin/php/updatesearchindexsolr.php --siteaccess=monsiteaccess --topNodeID=2546 --offset=0 --limit=10
 
 

Vérifier ce que Solr a réellement indexé

Solr - Web administration

Pour savoir si vos contenus et vos attributs sont correctement indexés, il suffit d'effectuer une recherche dans l'interface Web d'administration de Solr : http://localhost:8983/solr/admin/. Cette interface est également utile pour vérifier comment les champs Solr ont été nommés, par exemple sur la version d'eZ Find 2.2, on peut observer 2 champs qui semblent des doublons concernent les titres des articles : attr_title_s et attr_title_t. La suite du billet détaille le comment et le pourquoi de ce comportement.

Debug direct avec Solr

En laissant une console active, pour pourrez observer les messages envoyés vers Solr de la forme :

INFO: [] webapp=/solr path=/select params={ ... MESSAGE ... } status=400 QTime=5
 

eZ Find - Console

Ce message peut être copier / coller à partir de la console, vers la fin de l'URL dans l'interfaçe d'administration de Solr : http://localhost:8983/solr/select/?MESSAGE. On obtient ainsi une sortie exacte de ce que Solr envois à eZ Find avant transformation. Cette manipulation est utile lors des phases de Debug, par exemple en manipulant directement le message pour obtenir exactement le résultat attendu.

Etape 2 : Comment eZ Find pousse le contenu eZ Publish vers Solr

Voici le parcours d'exécution du code lors de l'ajout / modification d'un contenu eZ Publish :

  • Exécution de la méthode registerSearchObject, de la classe eZContentOperationCollection
    Si DelayedIndexing activé (voir détail ci dessous) : ajout d'objectID à la table 'ezpending_actions' pour un traitement ultérieur
  • Si DelayedIndexing désactivé, fetch de l'objet et transmission de l'objet à la méthode eZSearch::addObject( $object, $needCommit )
  • Si eZ Find installé et activé, appel de la méthode eZSolr::addObject( $contentObject, $commit = true )
  • Au final, une fois les contenus préparés, appel de la méthode addDocs de la classe /extension/ezfind/classes/ezsolrbase.php, qui POST ensuite les données via HTTP à l'aide de cURL

eZ Find, un plug-in de recherche

eZ Find est construit comme un plug-in de recherche, c'est à dire que le module /content/search a été nativement construit pour être déporté sous forme de plug-in. Ainsi dans l'extension /ezfind/settings/site.ini.append.php on trouve la déclaration du plug-in :

[SearchSettings]
SearchEngine=ezsolr
 
 

C'est ensuite la méthode getEngine() (kernel/classes/ezsearch.php) qui s'occupe d'affecter la bonne classe PHP pour interfacer le plug-in de recherche, à savoir : /search/plugins/ezsolr/ezsolr.php

eZ Find et le mécanisme natif du DelayedIndexing

Lors des différentes opérations de mises à jours de contenus, la méthode addObject de la classe eZSolr sera invoqué. Ce mode de fonctionnement en plug-in permet également d'hériter des autres mécanismes natifs de la recherche eZ Publish, comme par exemple le DelayedIndexing, qui permet de déporter l'indexation (complète ou par classes) vers une tache planifiée : indexcontent.php :

[SearchSettings]
DelayedIndexing=disabled|enabled|classbased
DelayedIndexingClassList[]
DelayedIndexingClassList[]=mycontentclass
 

A noter : Cette technique est relativement efficace pour optimiser les temps de réponses Back Office, ou les imports de contenus régulier. Cependant, le DelayedIndexing souffre de quelques limites, puisqu'il est générique (et non optimisé spécifiquement pour eZ Find) et qu'il se contente donc de boucler sur la table 'ezpending_actions' afin d'indexer les objets un par un sans se préoccuper des autres finesses de Solr, comme par exemple l'ajout massif de contenus à indexer, suivi d'un unique 'commit'.

Etape 3 : Le fonctionnement de la méthode addObject

La méthode addObject (/search/plugins/ezsolr/ezsolr.php) reçoit :

  • un unique objet de contenu en paramètre
  • un paramètre optionnel indiquant s'il faut 'commiter' ou non le contenu ajouté (une très bonne façon d'optimiser les indexations massives de contenus)

Cette méthode agit de la façon suivante, dans les grandes lignes :

  • Vérification s'il faut indexer ou non le contenu, en fonction des classes exclues (settings : [IndexExclude])
  • Récupération des méta données de l'objet, et création des noms et valeurs des champs pour Solr
  • Récupération des méta données des noeuds, et création des noms et valeurs des champs pour Solr
  • Récupération des données des attributs de l'objet, dans chacune des langues, et création des noms et valeurs des champs pour Solr
  • Insertion dans l'index de Solr de l'ensemble des données collectées, en mono ou multicore selon les settings
  • Commit final, en fonction du paramètre optionnel de la méthode addObject

A noter : Depuis eZ Find 2.2, il est possible d'utiliser des 'cores' spécifiques à chaque langues (/extension/ezfind/java/solr.multicore), ce qui permet de dédier des index et des fichiers de configurations spécifiques à chaque langue (spellings.txt, synonmys.txt, stopwords.txt, etc.)

Etape 4 : Les nommage des champs côté Solr

Il est nécessaire de revenir sur la façon dont eZ Find nomme les champs transmis à Solr. Les principales techniques d'exploitations avancées d'eZ Find pour eZ Publish nécessitent de bien comprendre la sémantique et le mécanisme de nommage des champs.
C'est la classe /ezfind/classes/ezfsolrdocumentfieldbase.php, ou d'éventuelles classes héritées pour certains datatypes complexes (voir ma contribution ezfsolrdocumentfieldobjectrelation par exemple) qui a vocation à créer les noms des champs pour Solr, en respectant une sémantique très précise. On obtient ainsi la sémantique suivante :

Le nom des attributs :

  • Nom Solr : attr_[contentattributename]_[contentattributetype], exemple : 'attr_title_s'. Il faut noter l'absence de l'identifiant de la classe, ce qui ouvre des perspectives d'exploitation des attributs Solr pour filtrer plusieurs classes sur des attributs homonymes (l'objet d'un prochain billet).
  • Mapping dans un Fetch eZ Find (filtre par exemple) : [contentclassname]/[contentattributename]/[contentsubattributename], par exemple 'article/title'.
    Le nom de la classe 'article' est utilisé comme un filtre supplémentaire lors de la construction de la requête
  • Le 'title' est tranformé en 'attr_title_s', le _s étant déduit de settings eZ Find (voir ci-dessous)

Le nom des métadonnées :

  • Nom Solr : meta_[metadataname]_[metadatatype], exemple : meta_class_identifier_s
  • Mapping dans un Fetch eZ Find (tri par exemple) : usage interne, par exemple pour un tri par 'class_identifier'

Le nom des sous-attributs (inexploité par défaut) :

  • Nom Solr : subattr_[contentattributename]-[contentsubattributename]_[contentsubattributetype], example : subattr_relatedimage-alttext_s

Nativement, les sous attributs sont peu ou pas exploités. C'est par contre un formidable outil pour étendre le fonctionnement d'eZ Find et indexer des champs supplémentaire. Voir par exemple ma contribution : ezfsolrdocumentfieldobjectrelation, qui indexe tous les attributs des objets en relations, en les stockant dans des sous attributs, ce qui permet ensuite d'effectuer toutes les opérations nécessaires sur ces sous attributs (recherche, filtre, facettes), selon la syntaxe 'myclass/myattribute/mysubattribute'. Les prochains billets détailleront comment exploiter ce mécanisme pour bien d'autres usages.

Etape 5 : La gestion des types de champs côté eZ Find

Dans les noms de champs Solr, la dernière information désigne le type de champs, et donc comment Solr va devoir considérer l'information (entre un string, un texte, une date, un tableau, etc.)
Coté eZ Find, cette définition se fait comme toujours dans les settings. Depuis eZ Find 2.2, il est maintenant possible de définir un type de champs spécifique à chaque contexte d'utilisation :

  • DatatypeMap[] : le setting global, s'appliquant à tous les contextes par défaut
  • DatatypeMapSort[] : settings spécifique pour les tris
  • DatatypeMapFacet[] : settings spécifique pour les facettes
  • DatatypeMapFilter[] : settings spécifique pour les filtres

Par exemple, par défaut, dans la version actuelle d'eZ Find 2.2, les attributs de type 'textline' sont définis différemment pour les tris :

  • DatatypeMap[ezstring]=text
  • DatatypeMapSort[ezstring]=string

L'impact au niveau de Solr, c'est que tous les attributs de type 'textline' auront systématiquement 2 champs de correspondance :

  • attr_title_t (utilisé pour la recherche, les facettes et les filtres)
  • attr_title_s (utilisé pour les tris)

A noter : il est conseillé d'utiliser également le type 'string' pour les facettes, afin de considérer le caractère espace comme un caractère lambda, et ainsi obtenir des facettes de type 'ma facette', plutôt que 'ma' et 'facette'.

Etape 6 : La gestion des types de champs côté Solr

Solr possède un fichier de configuration permettant à eZ Find de lui spécifier que le '_s' signifie un string, le '_t' un texte, etc. Il s'agit du fichier /ezfind/java/solr/conf/schema.xml
Ce fichier de configuration contient la définition d'un certain nombre de noms de champs en dur (pour les meta-données par exemple), mais définie également les correspondances pour tous les noms de champs dynamiques (nos fameux attributs de classes eZ Publish) :

<dynamicField name="*_i" type="int" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_f" type="float" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_d" type="double" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_si" type="sint" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_sf" type="sfloat" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_sd" type="sdouble" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true" multiValued="true" termVectors="true"/>
<dynamicField name="*_sl" type="slong" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_l" type="long" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_t" type="text" indexed="true" stored="true" multiValued="true" termVectors="true"/>
<dynamicField name="*_b" type="boolean" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_dt" type="date" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_random" type="random" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_k" type="keyword" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="*_lk" type="lckeyword" indexed="true" stored="true" multiValued="true"/>
<!-- some trie-coded dynamic fields for faster range queries -->
<dynamicField name="*_ti" type="tint" indexed="true" stored="true"/>
<dynamicField name="*_tl" type="tlong" indexed="true" stored="true"/>
<dynamicField name="*_tf" type="tfloat" indexed="true" stored="true"/>
<dynamicField name="*_td" type="tdouble" indexed="true" stored="true"/>
<dynamicField name="*_tdt" type="tdate" indexed="true" stored="true"/>
<!-- geopoint for geospatial/location searches, boosting, ... -->
<dynamicField name="*_gpt" type="geopoint" indexed="true" stored="true"/>
 

Ce fichier permet aussi de définir des comportements plus complexes sur certains datatypes eZ Publish, comme par exemple les mots clés (keyword). On y trouve par exemple 2 types de champs différents pour la gestion des keywords ('keyword' pour un respect de la casse et 'lckeyword' pour une casse en lowercase), qui peuvent etre exploité indifféremment au niveau d'eZ Find (DatatypeMap).

<fieldtype name="lckeyword" class="solr.TextField" positionIncrementGap="100">
 <analyzer type="index">
 <tokenizer class="solr.PatternTokenizerFactory" pattern=", *" />
 <filter class="solr.TrimFilterFactory" />
 <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
 <filter class="solr.LowerCaseFilterFactory"/>
 <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
 </analyzer>
 <analyzer type="query">
 <tokenizer class="solr.PatternTokenizerFactory" pattern=", *" />
 <filter class="solr.TrimFilterFactory" />
 <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
 <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
 <filter class="solr.LowerCaseFilterFactory"/>
 <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
 </analyzer>
</fieldtype>
 

La définition des keywords est riche d'enseignement sur la configuration de Solr. On peut observer le fonctionnement des appels des filtres Solr, et comment sont traités les séparations de mots par des virgules (PatternTokenizerFactory), la gestion de la casse (LowerCaseFilterFactory), ou encore la suppression des doublons (RemoveDuplicatesTokenFilterFactory), etc.

Bookmark logos Delicious Blogmark Google Bookmark Linkedin Yahoo! Bookmark Partager sur Facebook Twitter Publié par Gilles Guirand le 09/05/2010 15:07