Optimisation Xquery avec Exist

On trouvera ci-dessous des explications sur chaque conseil.

KO

Voici les choses à éviter:

  • pas de eval()
  • pas d'expressions évaluées plusieurs fois ou inutiles
  • pas de //
  • pas de requête sur une expression contruite

OK

Voici les points recommandés pour l'optimisation :

  • limiter au maximum les recherches portant sur un critère, et préférer la navigation par parents, enfants, et frère à partir d'un noeud déjà obtenu
  • utilisation judicieuse des index pour les recherches portant sur un critère

Qualité

Voici les points recommandés pour la qualité du code. On trouvera ici un exemple de XQuery correct : TODO

  • mettre $Id$ dans un commentaire au début
  • documentation interne: paramètres HTTP
  • documenter en XQuery les types des arguments et de la valeur de retour
  • utiliser des noms parlants pour les variables et fonctions, sans abréviations; éviter les mots qui ont plusieurs significations
  • utiliser les tags à la Javadoc de XQDoc ( http://www.xqdoc.org/ ) : @param, @return
  • séparer la recherche d'information de la construction de la réponse


EXPLICATIONS

Pas de eval()

Il faut savoir que le code XQuery, après compilation, est mis en cache par eXist. Et bien sûr, les arguments de la fonction eval() ne peuvent pas être mis en cache. Mais surtout l'utilisation de la fonction eval() conduit à un style de programmation difficile à relire et à débugger. De plus on peut toujours remplacer eval() par une tournure standard.

Pas d'expressions évaluées plusieurs fois ou inutiles

eXist ne pratique aucune analyse et optimisation du genre de celles pratiquées par un compilateur Java: factorisation de sous-expressions communes, élimination de code mort, etc. Il faut veuiller en particulier aux sous-expressions communes, qu'il faut stocker dans une variable, ce qui concourt aussi à la lisibilité du code.

Pas de //

$a//b conduit à parcourir complètement le (ou les) sous-arbre pointés par $a à la recherche d'un élément <b> . En général on sait assez précisément où il se trouve.

Pas de requête sur une expression contruite
Le contre-exemple typique:

let $e := <a><b>content</b></a>
let result := $e/b/text()


Limiter au maximum les recherches portant sur un critère

Une requête telle que

$res := collection("/db/projects")/a/b [ id = $val ] 
conduit à parcourir complètement une collection. Bien sûr de telles requêtes forment la base d'un script XQuery (et la majeure partie du temps écoulé). Mais une fois qu'on a obtenu le résultat $res, on peut naviguer très efficacememt par parents, enfants, et frère à partir de là :
$a := $res / parent::a 
$next-sibling := $a / next-sibling:a

Utilisation judicieuse des index pour les recherches portant sur un critère

Il y a actuellement trois types d'index utilisateur dans eXist. Tous nécessitent une indexation préalable de la base ou d'une arborescence de sous-collections.

  1. les index plein texte (fulltext), qui indexent les mots
  2. les index typés par chemin XPath (appelés "range index" parce qu'ils permettent une requête sur un intervalle de valeurs numériques)
  3. les index par nom de balise ("Qname index") : http://wiki.exist-db.org/space/jmvanel/New+index+by+QName

Les index 1. et 2. sont moins performants, mais ont deux avantages:

  • ils ne nécessitent pas de changer le code de la requête;
  • il n'y a pas de risque de résultats erronés si l'indexation n'a pas été faite.

L'index 3. n'a pas ces 2 avantages, mais est presque aussi performant qu'une base relationelle. Il ne permet pas de se restreindre à un chemin XPath, mais seulement à un nom de balise. Les index 2. et 3. sont tous deux typés (entiers, ou chaînes) , et permettent de sélectionner par critère d'égalité ou d'inégalité (comparaison).