Comment réaliser un Group By en XSLT 1.0 ?

Camille SCHNEIDER
Publié par Camille SCHNEIDER
Catégorie : BizTalk
04/05/2020

Problématique :

 

Le schéma source est issu d’une base de données SQL, il est composé d’un seul record répétable « Order », contenant tous les champs.

Le schéma de destination est composé de plusieurs records qui sont imbriqués.

Notre besoin ici est de grouper les données source, selon plusieurs critères :

  1. Shipment
  2. Container
  3. Order

 

map ListOrder to ListShipment groupe

 

Nous pouvons recevoir environ 50 000 lignes en entrée de la map, c’est pour cela qu’il faut trouver la méthode la plus optimisée pour faire ce traitement.

 

Solution :

 

Pour commencer il semble difficile d’effectuer un tel traitement avec le mappeur, une map en full XSLT s’impose. Le Group By en XSLT 1.0 n’existant pas, il faut donc employer la méthode de Muenchian.

https://en.wikipedia.org/wiki/XSLT/Muenchian_grouping

Cette méthode consiste à créer une clé composite.

 

<xsl:key name=""  match="" use=""/>

 

Une key est composée d’un nom, d’une requête XPath et de ce qui va permettre d’identifier la ligne recherchée : cela peut être un élément, un attribut ou encore une valeur calculée.

A noter : une key ne peut pas être créée dans un template, on ne peut donc pas utiliser une variable dans la requête XPath.

 

xslt liste cle

 

Dans le cas présent, le premier groupement est effectué sur le champ ShipmentNumber.

La première key est donc la suivante :

 

<xsl:key name="groupByShipment" match="/s0:ListOrders/Order" use="./ShipmentNumber"/>

 

Pour effectuer le groupement, il faut alors faire appel à la key dans un foreach.

La fonction generate_id() permet ensuite d’identifier un noeud spécifique en fonction de la key utilisée.

 

<xsl:for-each select= "/s0:ListOrders/Order[generate-id(.)=generate-id(key('groupByShipment', ./ShipmentNumber))]">

 

A la sortie de ce foreach, nous avons donc l’équivalent d’un distinct effectué sur le champ ShipmentNumber.

 

xslt grouper Shipment cle unique

 

A l’intérieur du foreach, nous assignons à une variable toutes les lignes avec un même ShipmentNumber.

 

<xsl:variable name="Shipment" select="key('groupsByShipment', ./ShipmentNumber)"/>

 

Il faut maintenant grouper sur le ContainerNumber, sans perdre le groupement par ShipmentNumber, nous avons donc besoin d’une deuxième clé :

 

<xsl:key name="groupByContainer" match="/s0:ListOrders/Order" use="concat(./ShipmentNumber, ./ContainerNumber)"/>

 

En utilisant un concat sur les deux champs cela permet de dissocier deux ContainerNumber identiques qui n’auraient pas le même ShipmentNumber.

Nous avons donc un nouveau foreach qui cette fois va grouper par ContainerNumber. Ici, nous ne partons plus de la racine mais de la variable contenant toutes les lignes d’un même ShipmentNumber.

 

xslt grouper Container cle composite

 

A noter : Si nous n’avions pas utilisé le concat mais simplement ContainerNumber, la key n’aurait été valable qu’une seule fois par ContainerNumber et donc à la deuxième itératrion du groupeByShipment nous n’aurions pas pu avoir un ContainerNumber déjà trouvé dans la première itération.

Il suffit ensuite de répéter l’opération pour chaque groupement.

 

Message d’exemple source :

xml message source avant groupe

 

Exemple sans le concat :

xml grouper cle unique

 

Exemple avec le concat :

xml grouper cle composite